From c69716b959ba380b41afda5d599fb766065754d0 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Mon, 4 Mar 2024 00:53:57 +0000 Subject: [PATCH 01/29] Feat: refactor 2.0 --- Cargo.lock | 1311 +++------ Cargo.toml | 239 +- build.rs | 8 +- config/client.toml | 39 + config/server.toml | 34 + src/async/ext.rs | 239 -- src/async/io.rs | 219 -- src/async/join.rs | 130 - src/async/macro.rs | 9 - src/async/mod.rs | 273 -- src/async/select.rs | 53 - src/async/sync.rs | 5 - src/async/time.rs | 34 - src/bin/client.rs | 3 + src/bin/client/fuso_clap.rs | 143 - src/bin/client/fuso_main.rs | 9 - src/bin/server.rs | 110 + src/bin/server/fuso_main.rs | 75 - src/channel/mod.rs | 1 + src/channel/remote.rs | 8 + src/cli/client.rs | 0 src/cli/mod.rs | 5 + src/cli/server.rs | 23 + src/client/builder.rs | 57 - src/client/mod.rs | 131 - src/config/client.rs | 182 ++ src/config/mod.rs | 57 + src/config/server.rs | 122 + src/core/accepter.rs | 306 +- src/core/boxed.rs | 73 - src/core/compress/lz4/mod.rs | 537 ---- src/core/compress/lz4/third_party/lib/LICENSE | 24 - src/core/compress/lz4/third_party/lib/lz4.c | 2573 ----------------- src/core/compress/lz4/third_party/lib/lz4.h | 778 ----- src/core/compress/lz4/third_party/mod.rs | 72 - src/core/compress/mod.rs | 23 - src/core/controller.rs | 46 - src/core/encryption/aes/mod.rs | 244 -- src/core/encryption/mod.rs | 27 - src/core/encryption/rsa/mod.rs | 239 -- src/core/future.rs | 32 + src/core/generator.rs | 34 - src/core/guard/buffer.rs | 105 - src/core/guard/fallback.rs | 221 -- src/core/guard/mod.rs | 7 - src/core/guard/timer.rs | 222 -- src/core/handshake.rs | 46 + src/core/io/mod.rs | 137 + src/core/mixing.rs | 125 - src/core/mod.rs | 248 +- src/core/net/kcp.rs | 121 + src/core/net/mod.rs | 7 + src/core/net/tcp.rs | 96 + src/core/net/udp.rs | 165 ++ src/core/observer.rs | 89 - src/core/pool/mod.rs | 85 - src/core/pool/stream.rs | 86 - src/core/processor.rs | 63 - src/core/protocol/local/mod.rs | 473 --- src/core/protocol/mod.rs | 227 -- src/core/protocol/proto.rs | 101 - src/core/protocol/serde/mod.rs | 1 - src/core/provider.rs | 177 -- src/core/rpc/mod.rs | 12 + src/core/socket.rs | 533 ---- src/core/split.rs | 83 + src/core/stream/codec.rs | 171 ++ src/core/stream/compress/lz4/mod.rs | 30 + src/core/stream/compress/mod.rs | 92 + src/core/stream/crypto/aes/mod.rs | 29 + src/core/stream/crypto/mod.rs | 143 + src/core/stream/crypto/rsa/mod.rs | 29 + src/core/stream/fallback.rs | 103 + src/core/stream/mod.rs | 4 + src/core/task/mod.rs | 3 + src/core/task/pool.rs | 16 + src/error/mod.rs | 489 +--- src/http/mod.rs | 5 - src/http/pages/mod.rs | 1 - src/http/routes/mod.rs | 21 - src/lib.rs | 35 +- src/net/kcp/builder.rs | 93 - src/net/kcp/mod.rs | 824 ------ src/net/kcp/stream.rs | 255 -- src/net/kcp/third_party/LICENSE | 21 - src/net/kcp/third_party/error.rs | 95 - src/net/kcp/third_party/mod.rs | 1293 --------- src/net/mod.rs | 17 - src/net/penetrate/accepter.rs | 69 - src/net/penetrate/bridge/mod.rs | 234 -- src/net/penetrate/builder.rs | 262 -- src/net/penetrate/client.rs | 397 --- src/net/penetrate/handshake/mod.rs | 162 -- src/net/penetrate/handshake/real_ip.rs | 20 - src/net/penetrate/mock/direct.rs | 40 - src/net/penetrate/mock/mod.rs | 36 - src/net/penetrate/mock/socks.rs | 330 --- src/net/penetrate/mod.rs | 19 - src/net/penetrate/observer.rs | 152 - src/net/penetrate/selector.rs | 79 - src/net/penetrate/server.rs | 627 ---- src/net/proxy/mod.rs | 1 - src/net/quic/mod.rs | 1 - src/net/socks/auth.rs | 232 -- src/net/socks/mod.rs | 456 --- src/net/tun/mod.rs | 1 - src/net/udp/mod.rs | 227 -- src/observer/mod.rs | 171 -- src/runtime/mod.rs | 17 +- src/runtime/smol/mod.rs | 242 -- src/runtime/smol/penetrate.rs | 67 - src/runtime/smol/udp.rs | 195 -- src/runtime/tokio/kcp.rs | 144 + src/runtime/tokio/mod.rs | 294 +- src/runtime/tokio/penetrate.rs | 124 - src/runtime/tokio/tcp.rs | 127 + src/server/builder.rs | 55 - src/server/mod.rs | 173 +- src/service/mod.rs | 8 + 119 files changed, 2855 insertions(+), 17833 deletions(-) create mode 100644 config/client.toml create mode 100644 config/server.toml delete mode 100644 src/async/ext.rs delete mode 100644 src/async/io.rs delete mode 100644 src/async/join.rs delete mode 100644 src/async/macro.rs delete mode 100644 src/async/mod.rs delete mode 100644 src/async/select.rs delete mode 100644 src/async/sync.rs delete mode 100644 src/async/time.rs create mode 100644 src/bin/client.rs delete mode 100644 src/bin/client/fuso_clap.rs delete mode 100644 src/bin/client/fuso_main.rs create mode 100644 src/bin/server.rs delete mode 100644 src/bin/server/fuso_main.rs create mode 100644 src/channel/mod.rs create mode 100644 src/channel/remote.rs create mode 100644 src/cli/client.rs create mode 100644 src/cli/mod.rs create mode 100644 src/cli/server.rs delete mode 100644 src/client/builder.rs create mode 100644 src/config/client.rs create mode 100644 src/config/mod.rs create mode 100644 src/config/server.rs delete mode 100644 src/core/boxed.rs delete mode 100644 src/core/compress/lz4/mod.rs delete mode 100644 src/core/compress/lz4/third_party/lib/LICENSE delete mode 100644 src/core/compress/lz4/third_party/lib/lz4.c delete mode 100644 src/core/compress/lz4/third_party/lib/lz4.h delete mode 100644 src/core/compress/lz4/third_party/mod.rs delete mode 100644 src/core/compress/mod.rs delete mode 100644 src/core/controller.rs delete mode 100644 src/core/encryption/aes/mod.rs delete mode 100644 src/core/encryption/mod.rs delete mode 100644 src/core/encryption/rsa/mod.rs create mode 100644 src/core/future.rs delete mode 100644 src/core/generator.rs delete mode 100644 src/core/guard/buffer.rs delete mode 100644 src/core/guard/fallback.rs delete mode 100644 src/core/guard/mod.rs delete mode 100644 src/core/guard/timer.rs create mode 100644 src/core/handshake.rs create mode 100644 src/core/io/mod.rs delete mode 100644 src/core/mixing.rs create mode 100644 src/core/net/kcp.rs create mode 100644 src/core/net/mod.rs create mode 100644 src/core/net/tcp.rs create mode 100644 src/core/net/udp.rs delete mode 100644 src/core/observer.rs delete mode 100644 src/core/pool/mod.rs delete mode 100644 src/core/pool/stream.rs delete mode 100644 src/core/processor.rs delete mode 100644 src/core/protocol/local/mod.rs delete mode 100644 src/core/protocol/mod.rs delete mode 100644 src/core/protocol/proto.rs delete mode 100644 src/core/protocol/serde/mod.rs delete mode 100644 src/core/provider.rs create mode 100644 src/core/rpc/mod.rs delete mode 100644 src/core/socket.rs create mode 100644 src/core/split.rs create mode 100644 src/core/stream/codec.rs create mode 100644 src/core/stream/compress/lz4/mod.rs create mode 100644 src/core/stream/compress/mod.rs create mode 100644 src/core/stream/crypto/aes/mod.rs create mode 100644 src/core/stream/crypto/mod.rs create mode 100644 src/core/stream/crypto/rsa/mod.rs create mode 100644 src/core/stream/fallback.rs create mode 100644 src/core/stream/mod.rs create mode 100644 src/core/task/mod.rs create mode 100644 src/core/task/pool.rs delete mode 100644 src/http/mod.rs delete mode 100644 src/http/pages/mod.rs delete mode 100644 src/http/routes/mod.rs delete mode 100644 src/net/kcp/builder.rs delete mode 100644 src/net/kcp/mod.rs delete mode 100644 src/net/kcp/stream.rs delete mode 100644 src/net/kcp/third_party/LICENSE delete mode 100644 src/net/kcp/third_party/error.rs delete mode 100644 src/net/kcp/third_party/mod.rs delete mode 100644 src/net/mod.rs delete mode 100644 src/net/penetrate/accepter.rs delete mode 100644 src/net/penetrate/bridge/mod.rs delete mode 100644 src/net/penetrate/builder.rs delete mode 100644 src/net/penetrate/client.rs delete mode 100644 src/net/penetrate/handshake/mod.rs delete mode 100644 src/net/penetrate/handshake/real_ip.rs delete mode 100644 src/net/penetrate/mock/direct.rs delete mode 100644 src/net/penetrate/mock/mod.rs delete mode 100644 src/net/penetrate/mock/socks.rs delete mode 100644 src/net/penetrate/mod.rs delete mode 100644 src/net/penetrate/observer.rs delete mode 100644 src/net/penetrate/selector.rs delete mode 100644 src/net/penetrate/server.rs delete mode 100644 src/net/proxy/mod.rs delete mode 100644 src/net/quic/mod.rs delete mode 100644 src/net/socks/auth.rs delete mode 100644 src/net/socks/mod.rs delete mode 100644 src/net/tun/mod.rs delete mode 100644 src/net/udp/mod.rs delete mode 100644 src/observer/mod.rs delete mode 100644 src/runtime/smol/mod.rs delete mode 100644 src/runtime/smol/penetrate.rs delete mode 100644 src/runtime/smol/udp.rs create mode 100644 src/runtime/tokio/kcp.rs delete mode 100644 src/runtime/tokio/penetrate.rs create mode 100644 src/runtime/tokio/tcp.rs delete mode 100644 src/server/builder.rs create mode 100644 src/service/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 4b51d67..e2bc7c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,160 +3,88 @@ version = 3 [[package]] -name = "aes" -version = "0.8.1" +name = "addr2line" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe0133578c0986e1fe3dfcd4af1cc5b2dd6c3dbf534d69916ce16a2701d40ba" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", + "gimli", ] [[package]] -name = "aho-corasick" -version = "0.7.18" +name = "adler" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" -dependencies = [ - "memchr", -] +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] -name = "async-channel" -version = "1.6.1" +name = "aho-corasick" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", + "memchr", ] [[package]] -name = "async-executor" -version = "1.4.1" +name = "anstream" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "once_cell", - "slab", + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", ] [[package]] -name = "async-fs" -version = "1.5.0" +name = "anstyle" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b3ca4f8ff117c37c278a2f7415ce9be55560b846b5bc4412aaa5d29c1c3dae2" -dependencies = [ - "async-lock", - "blocking", - "futures-lite", -] +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] -name = "async-io" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07" -dependencies = [ - "concurrent-queue", - "futures-lite", - "libc", - "log", - "once_cell", - "parking", - "polling", - "slab", - "socket2", - "waker-fn", - "winapi", -] - -[[package]] -name = "async-lock" -version = "2.5.0" +name = "anstyle-parse" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ - "event-listener", + "utf8parse", ] [[package]] -name = "async-mutex" -version = "1.4.0" +name = "anstyle-query" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "event-listener", + "windows-sys 0.52.0", ] [[package]] -name = "async-net" -version = "1.6.1" +name = "anstyle-wincon" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5373304df79b9b4395068fb080369ec7178608827306ce4d081cba51cac551df" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ - "async-io", - "blocking", - "futures-lite", + "anstyle", + "windows-sys 0.52.0", ] -[[package]] -name = "async-process" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2c06e30a24e8c78a3987d07f0930edf76ef35e027e7bdb063fccafdad1f60c" -dependencies = [ - "async-io", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "libc", - "once_cell", - "signal-hook", - "winapi", -] - -[[package]] -name = "async-task" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" - [[package]] name = "async-trait" -version = "0.1.56" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", "syn", ] -[[package]] -name = "atomic-waker" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -164,63 +92,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "axum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2cc6e8e8c993cb61a005fab8c1e5093a29199b7253b05a6883999312935c1ff" -dependencies = [ - "async-trait", - "axum-core", - "bitflags", - "bytes", - "futures-util", - "http", - "http-body", - "hyper", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tower", - "tower-http", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum-core" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4d047478b986f14a13edad31a009e2e05cb241f9805d0d75e4cba4e129ad4d" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "mime", -] - -[[package]] -name = "base64ct" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdca834647821e0b13d9539a8634eb62d3501b6b6c2cec1722786ee6671b851" - -[[package]] -name = "bincode" -version = "1.3.3" +name = "backtrace" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ - "serde", + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", ] [[package]] @@ -229,61 +112,17 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "block-padding" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a90ec2df9600c28a01c56c4784c9207a96d2451833aeceb8cc97e4c9548bb78" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blocking" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" -dependencies = [ - "async-channel", - "async-task", - "atomic-waker", - "fastrand", - "futures-lite", - "once_cell", -] - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - [[package]] name = "bytes" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" - -[[package]] -name = "cache-padded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" - -[[package]] -name = "cbc" -version = "0.1.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "3286b845d0fccbdd15af433f61c5970e711987036cb468f437ff6badd70f4e24" [[package]] name = "cfg-if" @@ -292,40 +131,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "cipher" -version = "0.4.3" +name = "clap" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" dependencies = [ - "crypto-common", - "inout", + "clap_builder", + "clap_derive", ] [[package]] -name = "clap" -version = "3.2.8" +name = "clap_builder" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" dependencies = [ - "atty", - "bitflags", - "clap_derive", + "anstream", + "anstyle", "clap_lex", - "indexmap", - "once_cell", "strsim", - "termcolor", - "textwrap", ] [[package]] name = "clap_derive" -version = "3.2.7" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ "heck", - "proc-macro-error", "proc-macro2", "quote", "syn", @@ -333,154 +166,78 @@ dependencies = [ [[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 = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" -dependencies = [ - "cache-padded", -] - -[[package]] -name = "const-oid" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" - -[[package]] -name = "cpufeatures" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" -dependencies = [ - "libc", -] - -[[package]] -name = "crypto-bigint" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "crypto-common" -version = "0.1.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5999502d32b9c48d492abe66392408144895020ec4709e549e840799f3bb74c0" -dependencies = [ - "generic-array", - "typenum", -] +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] -name = "der" -version = "0.5.1" +name = "colorchoice" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" -dependencies = [ - "const-oid", - "crypto-bigint", - "pem-rfc7468", -] +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] -name = "digest" -version = "0.10.3" +name = "env_filter" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" dependencies = [ - "crypto-common", + "log", + "regex", ] [[package]] name = "env_logger" -version = "0.9.0" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +checksum = "6c012a26a7f605efc424dd53697843a72be7dc86ad2d01f7814337794a12231d" dependencies = [ - "atty", + "anstream", + "anstyle", + "env_filter", "humantime", "log", - "regex", - "termcolor", ] [[package]] -name = "event-listener" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" - -[[package]] -name = "fastrand" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" -dependencies = [ - "instant", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" +name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" -dependencies = [ - "matches", - "percent-encoding", -] +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "fuso" version = "1.0.5-beta" dependencies = [ - "aes", - "async-channel", - "async-mutex", - "axum", - "bincode", - "bytes", - "cbc", "cc", "clap", "env_logger", - "futures", + "fuso-socks", + "kcp-rust", "log", - "num_cpus", - "once_cell", + "parking_lot", "pin-project", - "rand", - "rsa", "serde", - "serde_json", - "smol", "tokio", "toml", ] +[[package]] +name = "fuso-socks" +version = "0.1.0" +source = "git+https://github.com/editso/fuso-socks5#556cfd0308cd177da2022caa598378dca59e9248" +dependencies = [ + "async-trait", + "bytes", + "futures", + "log", +] + [[package]] name = "futures" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -493,9 +250,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -503,15 +260,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -520,30 +277,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" - -[[package]] -name = "futures-lite" -version = "1.12.0" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", @@ -552,21 +294,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -581,86 +323,28 @@ dependencies = [ ] [[package]] -name = "generic-array" -version = "0.14.5" +name = "gimli" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "hashbrown" -version = "0.12.1" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "http" -version = "0.2.8" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "http-range-header" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" - -[[package]] -name = "httparse" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" [[package]] name = "humantime" @@ -668,90 +352,37 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" -[[package]] -name = "hyper" -version = "0.14.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - [[package]] name = "indexmap" -version = "1.9.1" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ - "autocfg", + "equivalent", "hashbrown", ] [[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "block-padding", - "generic-array", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "itoa" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +name = "kcp-rust" +version = "0.1.2" +source = "git+https://github.com/editso/kcp-rust#e7cd0c2d74652185e1c31ea6d4e5201ee71b9b89" dependencies = [ - "spin", + "cc", + "log", + "pin-project", ] [[package]] name = "libc" -version = "0.2.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" - -[[package]] -name = "libm" -version = "0.2.2" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -759,124 +390,54 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - -[[package]] -name = "matchit" -version = "0.5.0" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] -name = "mime" -version = "0.3.16" +name = "miniz_oxide" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] [[package]] name = "mio" -version = "0.8.4" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", - "log", "wasi", - "windows-sys", -] - -[[package]] -name = "num-bigint-dig" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566d173b2f9406afbc5510a90925d5a2cd80cae4605631f1212303df265de011" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand", - "smallvec", - "zeroize", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", - "libm", + "windows-sys 0.48.0", ] [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] [[package]] -name = "once_cell" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" - -[[package]] -name = "os_str_bytes" -version = "6.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" - -[[package]] -name = "parking" -version = "2.0.0" +name = "object" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] [[package]] name = "parking_lot" @@ -890,46 +451,31 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-targets 0.48.5", ] -[[package]] -name = "pem-rfc7468" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01de5d978f34aa4b2296576379fcc416034702fd94117c56ffd8a1a767cefb30" -dependencies = [ - "base64ct", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - [[package]] name = "pin-project" -version = "1.0.11" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.11" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", @@ -938,9 +484,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -948,191 +494,88 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkcs1" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78f66c04ccc83dd4486fd46c33896f4e17b24a7a3a6400dedc48ed0ddd72320" -dependencies = [ - "der", - "pkcs8", - "zeroize", -] - -[[package]] -name = "pkcs8" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" -dependencies = [ - "der", - "spki", - "zeroize", -] - -[[package]] -name = "polling" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" -dependencies = [ - "cfg-if", - "libc", - "log", - "wepoll-ffi", - "winapi", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.20" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.6.0" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", + "regex-automata", "regex-syntax", ] [[package]] -name = "regex-syntax" -version = "0.6.27" +name = "regex-automata" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] [[package]] -name = "rsa" -version = "0.6.1" +name = "regex-syntax" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf22754c49613d2b3b119f0e5d46e34a2c628a937e3024b8762de4e7d8c710b" -dependencies = [ - "byteorder", - "digest", - "num-bigint-dig", - "num-integer", - "num-iter", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core", - "smallvec", - "subtle", - "zeroize", -] +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] -name = "ryu" -version = "1.0.10" +name = "rustc-demangle" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.138" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.138" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", @@ -1140,172 +583,89 @@ dependencies = [ ] [[package]] -name = "serde_json" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" +name = "serde_spanned" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ - "form_urlencoded", - "itoa", - "ryu", "serde", ] -[[package]] -name = "signal-hook" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" -dependencies = [ - "libc", - "signal-hook-registry", -] - [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] [[package]] name = "slab" -version = "0.4.6" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] [[package]] name = "smallvec" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" - -[[package]] -name = "smol" -version = "1.2.5" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cf3b5351f3e783c1d79ab5fc604eeed8b8ae9abd36b166e8b87a089efd85e4" -dependencies = [ - "async-channel", - "async-executor", - "async-fs", - "async-io", - "async-lock", - "async-net", - "async-process", - "blocking", - "futures-lite", - "once_cell", -] +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "socket2" -version = "0.4.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "winapi", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spki" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" -dependencies = [ - "base64ct", - "der", + "windows-sys 0.52.0", ] [[package]] name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "subtle" -version = "2.4.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "syn" -version = "1.0.98" +version = "2.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" - -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" - [[package]] name = "tokio" -version = "1.19.2" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ + "backtrace", "bytes", "libc", - "memchr", "mio", "num_cpus", - "once_cell", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "1.8.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", @@ -1314,212 +674,193 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" dependencies = [ "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", ] [[package]] -name = "tower" -version = "0.4.13" +name = "toml_datetime" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ - "futures-core", - "futures-util", - "pin-project", - "pin-project-lite", - "tokio", - "tower-layer", - "tower-service", - "tracing", + "serde", ] [[package]] -name = "tower-http" -version = "0.3.4" +name = "toml_edit" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" +checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" dependencies = [ - "bitflags", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-range-header", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] -name = "tower-layer" -version = "0.3.1" +name = "unicode-ident" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] -name = "tower-service" -version = "0.3.2" +name = "utf8parse" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] -name = "tracing" -version = "0.1.35" +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" -dependencies = [ - "cfg-if", - "log", - "pin-project-lite", - "tracing-core", -] +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "tracing-core" -version = "0.1.28" +name = "windows-sys" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "once_cell", + "windows-targets 0.48.5", ] [[package]] -name = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" - -[[package]] -name = "typenum" -version = "1.15.0" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.3", +] [[package]] -name = "unicode-ident" -version = "1.0.1" +name = "windows-targets" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +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", +] [[package]] -name = "version_check" -version = "0.9.4" +name = "windows-targets" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" +dependencies = [ + "windows_aarch64_gnullvm 0.52.3", + "windows_aarch64_msvc 0.52.3", + "windows_i686_gnu 0.52.3", + "windows_i686_msvc 0.52.3", + "windows_x86_64_gnu 0.52.3", + "windows_x86_64_gnullvm 0.52.3", + "windows_x86_64_msvc 0.52.3", +] [[package]] -name = "waker-fn" -version = "1.1.0" +name = "windows_aarch64_gnullvm" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] -name = "want" -version = "0.3.0" +name = "windows_aarch64_gnullvm" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] +checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" [[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +name = "windows_aarch64_msvc" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] -name = "wepoll-ffi" -version = "0.1.2" +name = "windows_aarch64_msvc" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" -dependencies = [ - "cc", -] +checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" [[package]] -name = "winapi" -version = "0.3.9" +name = "windows_i686_gnu" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +name = "windows_i686_gnu" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" [[package]] -name = "winapi-util" -version = "0.1.5" +name = "windows_i686_msvc" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "windows_i686_msvc" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" [[package]] -name = "windows-sys" -version = "0.36.1" +name = "windows_x86_64_gnu" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", -] +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" +name = "windows_x86_64_gnu" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" [[package]] -name = "windows_i686_gnu" -version = "0.36.1" +name = "windows_x86_64_gnullvm" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] -name = "windows_i686_msvc" -version = "0.36.1" +name = "windows_x86_64_gnullvm" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" [[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" +name = "windows_x86_64_msvc" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" [[package]] -name = "zeroize" -version = "1.5.6" +name = "winnow" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20b578acffd8516a6c3f2a1bdefc1ec37e547bb4e0fb8b6b01a4cafc886b4442" +checksum = "7a4191c47f15cc3ec71fcb4913cb83d58def65dd3787610213c649283b5ce178" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml index 88cacb0..9ef5850 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,153 +1,86 @@ -[package] -name = "fuso" -version = "1.0.5-beta" -edition = "2021" -build = "build.rs" -authors = ["editso "] -description = "An intranet penetration proxy tool" -license = "GPL-3.0" -keywords = ["proxy", "penetrate", "socks5", "async", "small", "networking"] -categories = ["proxy", "network-programming"] -repository = "https://github.com/editso/fuso" -homepage = "https://github.com/editso/fuso" - - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[build-dependencies] -cc = "1.0" - -[dependencies] -log = "0.4.16" -async-mutex = "1.4.0" -async-channel = "1.6.1" -pin-project = "1.0.10" - -[dependencies.futures] -version = "0.3.21" -optional = true - -[dependencies.tokio] -version = "1.17.0" -optional = true -features = ["full", "io-util"] - -[dependencies.smol] -version = "1.2.5" -optional = true - -[dependencies.serde] -version = "1.0.136" -optional = true -features = ["derive"] - -[dependencies.bincode] -version = "1.3.3" -optional = true - -[dependencies.axum] -version = "0.5.1" -optional = true - -[dependencies.toml] -version = "0.5.9" -optional = true - -[dependencies.clap] -version = "3.1.8" -features = ["derive"] -optional = true - -[dependencies.bytes] -version = "1.1.0" -optional = true - -[dependencies.env_logger] -version = "0.9.0" -optional = true - -[dependencies.rsa] -version = "0.6.1" -optional = true - -[dependencies.aes] -version = "0.8.1" -optional = true - -[dependencies.cbc] -version = "0.1.2" -optional = true - -[dependencies.rand] -version = "0.8.5" -optional = true - -[dependencies.serde_json] -version = "1.0.83" -optional = true - -[dependencies.once_cell] -version = "1.13.0" -optional = true - -[dependencies.num_cpus] -version = "1.13.1" -optional = true - - -[profile.release] -lto = true -debug = false -rpath = false -incremental = false -overflow-checks = false -opt-level = 'z' -codegen-units = 1 -panic = 'abort' - - -[features] -# 默认开启tokio异步 & clap参数解析器 -default = ['fuso-rt-tokio', "fuso-api", "fuso-json", "fuso-kcp","fuso-clap", "bytes", "fuso-serde", "fuso-socks5", "fuso-crypt-rsa", "fuso-crypt-aes", "fus-log"] -# 只提供api,不提供web界面 -fuso-api = ["axum", "fuso-rt-tokio"] -# web界面 -fuso-dashboard = ["fuso-api", "toml", "serde"] -# 配置文件的方式运行 -fuso-toml = ["toml"] -# 使用serde序列化进行数据传输 -fuso-serde = ["serde", "bincode"] -# 使用clap进行参数解析 -fuso-clap = ["clap"] -# 运行时 -fuso-rt-smol = ['smol', "futures", "num_cpus", "once_cell"] -# tokio运行时 -fuso-rt-tokio = ['tokio'] -# 自定义运行时 -fuso-rt-custom = ["futures"] -# kcp -fuso-kcp = [] -# quic -fuso-quic = [] -# 直连模式 -fuso-proxy = [] -# socks5代理 -fuso-socks5 = [] -# rsa加密 -fuso-crypt-rsa = ["rsa", "rand"] -# aes加密 -fuso-crypt-aes = ["aes", "cbc"] -# json -fuso-json = ["serde_json"] -# 客户端日志输出 -fuc-log = ["env_logger"] -fus-log = ["env_logger"] - -[[bin]] -name = "fuc" -path = "src/bin/client/fuso_main.rs" - - -[[bin]] -name = "fus" -path = "src/bin/server/fuso_main.rs" \ No newline at end of file +[package] +name = "fuso" +version = "1.0.5-beta" +edition = "2021" +build = "build.rs" +authors = ["editso "] +description = "An intranet penetration proxy tool" +license = "GPL-3.0" +keywords = ["proxy", "penetrate", "socks5", "async", "small", "networking"] +categories = ["proxy", "network-programming"] +repository = "https://github.com/editso/fuso" +homepage = "https://github.com/editso/fuso" + + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[[bin]] +name = "fuc" +path = "src/bin/client.rs" + +[[bin]] +name = "fus" +path = "src/bin/server.rs" + +[profile.release] +lto = true +debug = false +rpath = false +incremental = false +overflow-checks = false +opt-level = 'z' +codegen-units = 1 +panic = 'abort' + +[features] +default = ["fuso-config", "fuso-cli", "fuso-kcp"] +fuso-kcp = ["kcp-rust"] +fuso-cli = ["clap"] +fuso-serde = ["serde"] +fuso-config = ["toml", "serde"] + + +[build-dependencies] +cc = "1.0" + + +[dependencies] +env_logger = "0.11.2" +log = "0.4.16" +pin-project = "1.1.4" +parking_lot = "0.12.1" + +[dependencies.kcp-rust] +optional = true +version = "*" +git = "https://github.com/editso/kcp-rust" +default-features = false + +[dependencies.fuso-socks] +optional = true +version = "*" +git = "https://github.com/editso/fuso-socks5" + +[dependencies.toml] +optional = true +version = "0.8.10" + +[dependencies.serde] +optional = true +version = "1.0.197" +features = ["derive"] + + +[dependencies.clap] +optional = true +version = "4.5.1" +features = ["derive"] + + +[dependencies.tokio] +version = "1.36.0" +features = ["full"] + +[dev-dependencies.tokio] +version = "1.36.0" +features = ["test-util", "full"] diff --git a/build.rs b/build.rs index 78bed5e..f7baf93 100644 --- a/build.rs +++ b/build.rs @@ -1,7 +1,7 @@ fn main() { - cc::Build::new() - .file(concat!("src/core/compress/lz4/third_party/lib/", "lz4.c")) - .include("src/core/compress/lz4/third_party/lib/") - .compile("lib_third_party_compress_lz4") + // cc::Build::new() + // .file(concat!("src/core/compress/lz4/third_party/lib/", "lz4.c")) + // .include("src/core/compress/lz4/third_party/lib/") + // .compile("lib_third_party_compress_lz4") } diff --git a/config/client.toml b/config/client.toml new file mode 100644 index 0000000..efd44c2 --- /dev/null +++ b/config/client.toml @@ -0,0 +1,39 @@ +features = ["kcp", "socks5"] + +[server] +addr = ["127.0.0.1"] +ports = [9999, 8080] +retries = -1 +crypto = ["rsa"] +compress = ["lz4"] +heartbeat_interval = 30000 +auth = { type = "secret", secret = "12345678" } + +[ssh1] +type = "forward" +boot = "fork" +exposes = [8080, 9999] +channel = 6777 +target = { type = "static", port = 2222, addr = ["127.0.0.1"] } +keep_alive = { interval = 100 } + +[bridge1] +type = "bridge" +bind = "0.0.0.0" +port = 9999 +auth = { type = "secret", secret = "111111111111111" } + +[proxy1] +type = "proxy" +bind = "0.0.0.0" +port = 8080 +crypto = ["rsa", "aes"] +target = { type = "static", addr = ["baidu.com"], port = 443 } +rewrite = { with = "http_header", host = "https://baidu.com" } + +[proxy2] +type = "proxy" +bind = "0.0.0.0" +port = 7890 +crypto = ["rsa", "aes"] +target = { type = "dynamic" } \ No newline at end of file diff --git a/config/server.toml b/config/server.toml new file mode 100644 index 0000000..5f8761d --- /dev/null +++ b/config/server.toml @@ -0,0 +1,34 @@ +crypto = [ "rsa" ] +compress = ["lz4"] +features = ["kcp", "proxy", "multiprocess"] + + +[auth] +type = "secret" +secret = "fasedfhkasjdfh" + +[manager] +bind = "127.0.0.1" +port = 8888 +context = "/api" +auth = { type = "secret", secret = "secret" } + +[[listen]] +type = "kcp" +port = 6666 +bind = "0.0.0.0" + + +[[listen]] +type = "tcp" +port = 8888 +bind = "127.0.0.1" +crypto = [ "rsa" ] +auth = { type = "secret", secret = "ffsdfsdfjkl" } +compress = ["lz4"] + + +[[listen]] +type = "tcp" +port = 8880 +bind = "127.0.0.1" diff --git a/src/async/ext.rs b/src/async/ext.rs deleted file mode 100644 index 0a13fbf..0000000 --- a/src/async/ext.rs +++ /dev/null @@ -1,239 +0,0 @@ -use std::{future::Future, io, pin::Pin, task::Poll}; - -use crate::AsyncWrite; - -#[pin_project::pin_project] -pub struct Read<'a, T: Unpin> { - buf: super::ReadBuf<'a>, - #[pin] - reader: &'a mut T, -} - -#[pin_project::pin_project] -pub struct Write<'a, T: Unpin> { - buf: &'a [u8], - #[pin] - writer: &'a mut T, -} - -pub struct Close<'a, T: Unpin> { - writer: &'a mut T, -} - -pub struct Flush<'a, T: Unpin> { - writer: &'a mut T, -} - -#[pin_project::pin_project] -pub struct ReadExact<'a, T> { - buf: super::ReadBuf<'a>, - #[pin] - reader: &'a mut T, -} - -#[pin_project::pin_project] -pub struct WriteAll<'a, T> { - buf: &'a [u8], - offset: usize, - #[pin] - writer: &'a mut T, -} - -pub trait AsyncReadExt: super::AsyncRead { - #[inline] - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Read<'a, Self> - where - Self: Sized + Unpin, - { - Read { - #[cfg(feature = "fuso-rt-tokio")] - buf: super::ReadBuf::new(buf), - #[cfg(any(feature = "fuso-rt-smol", feature = "fuso-rt-custom"))] - buf: super::ReadBuf::new(buf), - reader: self, - } - } - - #[inline] - fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self> - where - Self: Sized + Unpin, - { - ReadExact { - #[cfg(feature = "fuso-rt-tokio")] - buf: super::ReadBuf::new(buf), - #[cfg(any(feature = "fuso-rt-smol", feature = "fuso-rt-custom"))] - buf: super::ReadBuf::new(buf), - reader: self, - } - } -} - -pub trait AsyncWriteExt: super::AsyncWrite { - #[inline] - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Write<'a, Self> - where - Self: Sized + Unpin, - { - Write { - buf: buf, - writer: self, - } - } - - fn close<'a>(&'a mut self) -> Close<'a, Self> - where - Self: Sized + Unpin, - { - Close { writer: self } - } - - fn flush<'a>(&'a mut self) -> Flush<'a, Self> - where - Self: Sized + Unpin, - { - Flush { writer: self } - } - - #[inline] - fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAll<'a, Self> - where - Self: Sized + Unpin, - { - WriteAll { - buf, - offset: 0, - writer: self, - } - } -} - -impl AsyncReadExt for T where T: super::AsyncRead + Unpin {} -impl AsyncWriteExt for T where T: super::AsyncWrite + Unpin {} - -impl<'a, T> Future for Read<'a, T> -where - T: super::AsyncRead + Unpin, -{ - type Output = crate::Result; - - #[inline] - fn poll( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - let mut this = self.project(); - Pin::new(&mut **this.reader).poll_read(cx, this.buf) - } -} - -impl<'a, T> Future for Write<'a, T> -where - T: super::AsyncWrite + Unpin, -{ - type Output = crate::Result; - - #[inline] - fn poll( - self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - let mut this = self.project(); - Pin::new(&mut **this.writer).poll_write(cx, *this.buf) - } -} - -#[inline] -fn eof() -> std::io::Error { - std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "early eof") -} - -impl<'a, T> Future for ReadExact<'a, T> -where - T: super::AsyncRead + Unpin, -{ - type Output = crate::Result<()>; - - fn poll( - self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - let this = self.project(); - - let buf = this.buf; - let mut reader = this.reader; - - loop { - let rem = buf.remaining(); - if rem != 0 { - match Pin::new(&mut **reader).poll_read(cx, buf)? { - Poll::Pending => break Poll::Pending, - Poll::Ready(n) => { - if n == 0 { - return Poll::Ready(Err(eof().into())); - } - } - } - } else { - break Poll::Ready(Ok(())); - } - } - } -} - -impl<'a, T> Future for WriteAll<'a, T> -where - T: super::AsyncWrite + Unpin, -{ - type Output = crate::Result<()>; - - fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { - let this = self.project(); - let mut writer = this.writer; - let offset = this.offset; - - loop { - if this.buf.is_empty() { - return Poll::Ready(Ok(())); - } - - match Pin::new(&mut **writer).poll_write(cx, &this.buf[*offset..])? { - Poll::Pending => break Poll::Pending, - Poll::Ready(0) => { - return Poll::Ready( - Err(io::Error::from(io::ErrorKind::ConnectionReset).into()), - ); - } - Poll::Ready(n) => { - *offset += n; - } - } - - if *offset == this.buf.len() { - break Poll::Ready(Ok(())); - } - } - } -} - -impl<'a, T> Future for Close<'a, T> -where - T: AsyncWrite + Unpin, -{ - type Output = crate::Result<()>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { - Pin::new(&mut *self.writer).poll_close(cx) - } -} - -impl<'a, T> Future for Flush<'a, T> -where - T: AsyncWrite + Unpin, -{ - type Output = crate::Result<()>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { - Pin::new(&mut *self.writer).poll_close(cx) - } -} diff --git a/src/async/io.rs b/src/async/io.rs deleted file mode 100644 index ed24c2e..0000000 --- a/src/async/io.rs +++ /dev/null @@ -1,219 +0,0 @@ -use std::{future::Future, ops::Deref, pin::Pin, sync::Arc, task::Poll}; - -use crate::{ - ext::{AsyncReadExt, AsyncWriteExt}, - AsyncRead, AsyncWrite, NetSocket, -}; - -type BoxedFuture = Pin> + Send + 'static>>; - -macro_rules! unwrap { - ($r: expr) => { - match $r { - Ok(r) => r, - Err(e) => return Poll::Ready(Err(e.into())), - } - }; -} - -pub struct Forward { - futures: Vec, -} - -pub struct Inner(std::sync::Mutex); - -pub struct ReadHalf(Arc>); - -pub struct WriteHalf(Arc>); - -impl NetSocket for Inner -where - S: NetSocket, -{ - fn peer_addr(&self) -> crate::Result { - self.0.lock()?.peer_addr() - } - - fn local_addr(&self) -> crate::Result { - self.0.lock()?.local_addr() - } -} - -impl NetSocket for ReadHalf -where - R: NetSocket, -{ - fn peer_addr(&self) -> crate::Result { - self.0.peer_addr() - } - fn local_addr(&self) -> crate::Result { - self.0.local_addr() - } -} - -impl NetSocket for WriteHalf -where - W: NetSocket, -{ - fn peer_addr(&self) -> crate::Result { - self.0.peer_addr() - } - - fn local_addr(&self) -> crate::Result { - self.0.local_addr() - } -} - -impl Future for Forward { - type Output = crate::Result<()>; - - fn poll( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - let mut futures = Vec::new(); - while let Some(mut future) = self.futures.pop() { - match Pin::new(&mut future).poll(cx) { - Poll::Pending => futures.push(future), - Poll::Ready(r) => { - return Poll::Ready(r); - } - } - } - - drop(std::mem::replace(&mut self.futures, futures)); - - if self.futures.is_empty() { - Poll::Ready(Ok(())) - } else { - Poll::Pending - } - } -} - -pub fn forward(s1: S1, s2: S2) -> Forward -where - S1: AsyncRead + AsyncWrite + Unpin + Send + 'static, - S2: AsyncRead + AsyncWrite + Unpin + Send + 'static, -{ - let (s1_reader, s1_writer) = split(s1); - let (s2_reader, s2_writer) = split(s2); - - fn copy(mut reader: R, mut writer: W) -> BoxedFuture - where - R: AsyncRead + Unpin + Send + 'static, - W: AsyncWrite + Unpin + Send + 'static, - { - Box::pin(async move { - let mut buf = unsafe { - let mut buf = Vec::with_capacity(1500); - buf.set_len(1500); - buf - }; - - loop { - let r = reader.read(&mut buf).await; - - if r.is_err() { - return Err(unsafe { r.unwrap_err_unchecked() }); - } - - let n = unsafe { r.unwrap_unchecked() }; - - if n == 0 { - let _ = writer.flush().await; - return writer.close().await; - } - - log::trace!("forward {}bytes data", n); - - let r = writer.write_all(&buf[..n]).await; - - if r.is_err() { - return Err(unsafe { - let err = r.unwrap_err_unchecked(); - log::trace!("forward error {}", err); - err - }); - } - } - }) - } - - Forward { - futures: vec![copy(s1_reader, s2_writer), copy(s2_reader, s1_writer)], - } -} - -impl Deref for Inner { - type Target = std::sync::Mutex; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl AsyncRead for ReadHalf -where - R: AsyncRead + Unpin, -{ - fn poll_read( - self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &mut crate::ReadBuf<'_>, - ) -> Poll> { - let mut inner = unwrap!(self.0.lock()); - Pin::new(&mut *inner).poll_read(cx, buf) - } -} - -impl AsyncWrite for WriteHalf -where - W: AsyncWrite + Unpin, -{ - fn poll_write( - self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> Poll> { - let mut inner = unwrap!(self.0.lock()); - Pin::new(&mut *inner).poll_write(cx, buf) - } - - fn poll_close( - self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> Poll> { - let mut inner = unwrap!(self.0.lock()); - Pin::new(&mut *inner).poll_close(cx) - } - - fn poll_flush( - self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> Poll> { - let mut inner = unwrap!(self.0.lock()); - Pin::new(&mut *inner).poll_flush(cx) - } -} - -pub fn split(t: T) -> (ReadHalf, WriteHalf) -where - T: AsyncRead + AsyncWrite + Send + Unpin + 'static, -{ - let inner = Arc::new(Inner(std::sync::Mutex::new(t))); - - (ReadHalf(inner.clone()), WriteHalf(inner)) -} - -impl Clone for ReadHalf { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - -impl Clone for WriteHalf { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} diff --git a/src/async/join.rs b/src/async/join.rs deleted file mode 100644 index 9611def..0000000 --- a/src/async/join.rs +++ /dev/null @@ -1,130 +0,0 @@ -use std::{future::Future, pin::Pin, task::Poll}; - -type BoxedFuture = Pin + Send + 'static>>; - -pub struct Join { - futures: Vec>, -} - -pub struct JoinOutput { - result: (Option, Option), - fut1: Option>, - fut2: Option>, -} - -impl Join { - pub fn join(f1: F1, f2: F2) -> Self - where - F1: Future + Send + 'static, - F2: Future + Send + 'static, - { - Join { - futures: vec![Box::pin(f1), Box::pin(f2)], - } - } - - pub fn add(mut self, fut: F) -> Self - where - F: Future + Send + 'static, - { - self.futures.push(Box::pin(fut)); - self - } -} - -impl Future for Join { - type Output = (); - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - let mut futures = Vec::new(); - while let Some(mut future) = self.futures.pop() { - if let Poll::Pending = Pin::new(&mut future).poll(cx) { - futures.push(future); - } - } - - if futures.is_empty() { - Poll::Ready(()) - } else { - drop(std::mem::replace(&mut self.futures, futures)); - Poll::Pending - } - } -} - -impl Future for JoinOutput, crate::Result> -where - O1: Unpin + 'static, - O2: Unpin + 'static, -{ - type Output = crate::Result<(O1, O2)>; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - loop { - match self.fut1.take() { - None if self.fut2.is_none() => break, - Some(mut fut) => match Pin::new(&mut fut).poll(cx) { - Poll::Pending => { - drop(std::mem::replace(&mut self.fut1, Some(fut))); - if self.fut2.is_none() { - break; - } - } - Poll::Ready(Err(e)) => { - // 都出错了还 poll???,退出! - return Poll::Ready(Err(e)); - } - Poll::Ready(r) => drop(std::mem::replace(&mut self.result.0, Some(r))), - }, - _ => {} - } - - match self.fut2.take() { - None if self.fut1.is_none() => break, - Some(mut fut) => match Pin::new(&mut fut).poll(cx) { - Poll::Pending => { - drop(std::mem::replace(&mut self.fut2, Some(fut))); - break; - } - Poll::Ready(Err(e)) => { - // 都出错了还 poll???,退出! - return Poll::Ready(Err(e)); - } - Poll::Ready(r) => { - drop(std::mem::replace(&mut self.result.1, Some(r))); - } - }, - _ => {} - } - } - - if self.fut1.is_none() && self.fut2.is_none() { - // unwrap_unchecked真丑,考虑下次更换 - let r1 = unsafe { self.result.0.take().unwrap_unchecked().unwrap_unchecked() }; - let r2 = unsafe { self.result.1.take().unwrap_unchecked().unwrap_unchecked() }; - Poll::Ready(Ok((r1, r2))) - } else { - Poll::Pending - } - } -} - -pub fn join_output(f1: F1, f2: F2) -> JoinOutput -where - F1: Future + Send + 'static, - F2: Future + Send + 'static, - O1: Unpin + 'static, - O2: Unpin + 'static, -{ - JoinOutput { - result: (None, None), - fut1: Some(Box::pin(f1)), - fut2: Some(Box::pin(f2)), - } -} diff --git a/src/async/macro.rs b/src/async/macro.rs deleted file mode 100644 index d818716..0000000 --- a/src/async/macro.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[macro_export] -macro_rules! ready { - ($poll: expr) => { - match $poll { - std::task::Poll::Pending => return std::task::Poll::Pending, - std::task::Poll::Ready(ready) => ready, - } - }; -} diff --git a/src/async/mod.rs b/src/async/mod.rs deleted file mode 100644 index 18b6105..0000000 --- a/src/async/mod.rs +++ /dev/null @@ -1,273 +0,0 @@ -pub mod ext; -pub mod io; -pub mod join; -pub mod r#macro; -pub mod select; -pub mod sync; -pub mod time; - -use std::{ - future::Future, - ops::{Deref, DerefMut}, - pin::Pin, - task::{Context, Poll}, -}; - -use crate::NetSocket; - -pub type BoxedFuture<'lifetime, T> = Pin + 'lifetime>>; - -#[cfg(feature = "fuso-rt-smol")] -mod smol_io { - pub use smol::io::{AsyncRead, AsyncWrite}; -} - -#[cfg(feature = "fuso-rt-custom")] -mod io { - pub use futures::io::{AsyncRead, AsyncWrite}; -} - -#[cfg(feature = "fuso-rt-tokio")] -pub struct ReadBuf<'a> { - buf: tokio::io::ReadBuf<'a>, -} - -#[cfg(any(feature = "fuso-rt-smol", feature = "fuso-rt-custom"))] -#[derive(Debug)] -pub struct ReadBuf<'a> { - buf: &'a mut [u8], - offset: usize, -} - -pub trait AsyncRead { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll>; -} - -pub trait AsyncWrite { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll>; - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; -} - -pub trait Stream: NetSocket + AsyncRead + AsyncWrite + Unpin {} - -impl AsyncRead for Box { - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll> { - Pin::new(&mut **self).poll_read(cx, buf) - } -} - -impl AsyncWrite for Box { - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - Pin::new(&mut **self).poll_write(cx, buf) - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut **self).poll_flush(cx) - } - - fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut **self).poll_close(cx) - } -} - -impl Stream for S where S: NetSocket + AsyncWrite + AsyncRead + Unpin {} - -#[cfg(feature = "fuso-rt-tokio")] -impl AsyncWrite for T -where - T: tokio::io::AsyncWrite, -{ - #[inline] - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - tokio::io::AsyncWrite::poll_write(self, cx, buf).map_err(Into::into) - } - - #[inline] - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - tokio::io::AsyncWrite::poll_flush(self, cx).map_err(Into::into) - } - - #[inline] - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - tokio::io::AsyncWrite::poll_shutdown(self, cx).map_err(Into::into) - } -} - -#[cfg(feature = "fuso-rt-tokio")] -impl AsyncRead for T -where - T: tokio::io::AsyncRead, -{ - #[inline] - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll> { - match tokio::io::AsyncRead::poll_read(self, cx, &mut buf.buf) { - Poll::Pending => Poll::Pending, - Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())), - Poll::Ready(Ok(())) => Poll::Ready(Ok(buf.filled().len())), - } - } -} - -#[cfg(any(feature = "fuso-rt-smol", feature = "fuso-rt-custom"))] -impl AsyncWrite for T -where - T: smol_io::AsyncWrite, -{ - #[inline] - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - futures::AsyncWrite::poll_write(self, cx, buf).map_err(Into::into) - } - - #[inline] - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - futures::AsyncWrite::poll_flush(self, cx).map_err(Into::into) - } - - #[inline] - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - futures::AsyncWrite::poll_close(self, cx).map_err(Into::into) - } -} - -#[cfg(any(feature = "fuso-rt-smol", feature = "fuso-rt-custom"))] -impl AsyncRead for T -where - T: smol_io::AsyncRead, -{ - #[inline] - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll> { - match futures::AsyncRead::poll_read(self, cx, &mut buf.buf[buf.offset..])? { - Poll::Pending => Poll::Pending, - Poll::Ready(n) => { - buf.advance(n); - Poll::Ready(Ok(n)) - } - } - } -} - -impl<'a> ReadBuf<'a> { - #[cfg(feature = "fuso-rt-tokio")] - pub fn new(buf: &'a mut [u8]) -> Self { - Self { - buf: tokio::io::ReadBuf::new(buf), - } - } - - #[cfg(any(feature = "fuso-rt-smol", feature = "fuso-rt-custom"))] - pub fn new(buf: &'a mut [u8]) -> Self { - Self { buf, offset: 0 } - } - - #[cfg(any(feature = "fuso-rt-smol", feature = "fuso-rt-custom"))] - pub fn len(&self) -> usize{ - self.buf.len() - } - - #[cfg(feature = "fuso-rt-tokio")] - pub fn len(&self) -> usize { - self.buf.capacity() - } - - #[cfg(any(feature = "fuso-rt-smol", feature = "fuso-rt-custom"))] - pub fn remaining(&self) -> usize { - self.len() - self.offset - } - - pub fn has_remaining(&self) -> bool { - self.remaining() > 0 - } - - #[cfg(any(feature = "fuso-rt-smol", feature = "fuso-rt-custom"))] - pub fn position(&self) -> usize { - self.offset - } - - #[cfg(feature = "fuso-rt-tokio")] - pub fn position(&self) -> usize { - self.buf.filled().len() - } - - #[cfg(feature = "fuso-rt-tokio")] - pub fn iter_mut(&mut self) -> &mut [u8] { - self.buf.filled_mut() - } - - #[cfg(any(feature = "fuso-rt-smol", feature = "fuso-rt-custom"))] - pub fn iter_mut(&mut self) -> &mut [u8] { - &mut self.buf[..self.offset] - } - - #[cfg(any(feature = "fuso-rt-smol", feature = "fuso-rt-custom"))] - pub fn advance(&mut self, n: usize) { - debug_assert!(self.offset + n <= self.buf.len()); - self.offset += n; - } - - #[cfg(any(feature = "fuso-rt-smol", feature = "fuso-rt-custom"))] - pub fn initialize_unfilled(&mut self) -> &mut [u8] { - &mut self.buf[self.offset..] - } -} - -#[cfg(any(feature = "fuso-rt-smol", feature = "fuso-rt-custom"))] -impl<'a> Deref for ReadBuf<'a> { - type Target = [u8]; - - #[inline] - fn deref(&self) -> &Self::Target { - self.buf - } -} - -#[cfg(feature = "fuso-rt-tokio")] -impl<'a> Deref for ReadBuf<'a> { - type Target = tokio::io::ReadBuf<'a>; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.buf - } -} - -impl<'a> DerefMut for ReadBuf<'a> { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.buf - } -} diff --git a/src/async/select.rs b/src/async/select.rs deleted file mode 100644 index 0a1aebf..0000000 --- a/src/async/select.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::{future::Future, pin::Pin, task::Poll}; - -type BoxedFuture = Pin + 'static>>; - -pub struct Select { - futures: Vec>, -} - -unsafe impl Send for Select{} - -impl Select -where - O: 'static, -{ - pub fn select(f1: F1, f2: F2) -> Self - where - F1: Future + 'static, - F2: Future + 'static, - { - Select { - futures: vec![Box::pin(f1), Box::pin(f2)], - } - } - - pub fn add(mut self, fut: F) -> Self - where - F: Future + Send + 'static, - { - self.futures.push(Box::pin(fut)); - self - } -} - -impl Future for Select -where - O: 'static, -{ - type Output = O; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - for future in self.futures.iter_mut() { - match Pin::new(future).poll(cx) { - Poll::Ready(ready) => return Poll::Ready(ready), - Poll::Pending => continue, - } - } - - Poll::Pending - } -} diff --git a/src/async/sync.rs b/src/async/sync.rs deleted file mode 100644 index d54c466..0000000 --- a/src/async/sync.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[cfg(feature = "fuso-rt-smol")] -pub use smol::lock::Mutex; - -#[cfg(feature = "fuso-rt-tokio")] -pub use tokio::sync::Mutex; diff --git a/src/async/time.rs b/src/async/time.rs deleted file mode 100644 index fca830b..0000000 --- a/src/async/time.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::{future::Future, time::Duration}; - -#[cfg(feature = "fuso-rt-tokio")] -pub async fn wait_for(timeout: Duration, fut: F) -> crate::Result -where - F: Future + Send + 'static, - O: Send + 'static, -{ - Ok(tokio::time::timeout(timeout, fut).await?) -} - -#[cfg(feature = "fuso-rt-tokio")] -pub async fn sleep(timeout: Duration) { - tokio::time::sleep(timeout).await -} - -#[cfg(feature = "fuso-rt-smol")] -pub async fn wait_for(timeout: Duration, fut: F) -> crate::Result -where - F: Future + Send + 'static, - O: Send + 'static, -{ - use crate::error; - - smol::future::race(async move { Ok(fut.await) }, async move { - Err(error::Kind::Timeout(smol::Timer::after(timeout).await).into()) - }) - .await -} - -#[cfg(feature = "fuso-rt-smol")] -pub async fn sleep(timeout: Duration) { - let _ = smol::Timer::after(timeout).await; -} diff --git a/src/bin/client.rs b/src/bin/client.rs new file mode 100644 index 0000000..cee5e36 --- /dev/null +++ b/src/bin/client.rs @@ -0,0 +1,3 @@ +fn main(){ + +} \ No newline at end of file diff --git a/src/bin/client/fuso_clap.rs b/src/bin/client/fuso_clap.rs deleted file mode 100644 index 5de3f0c..0000000 --- a/src/bin/client/fuso_clap.rs +++ /dev/null @@ -1,143 +0,0 @@ -use std::net::IpAddr; -use std::time::Duration; - -use clap::ArgAction; -use clap::Parser; -use fuso::penetrate::PenetrateRsaAndAesHandshake; -use fuso::FusoPenetrateConnector; -use fuso::Socket; - -#[derive(Parser)] -#[clap(author, version, about)] -struct FusoArgs { - /// 是否启用 kcp, 默认不启用 - #[clap(long, default_value = "false", action = ArgAction::SetTrue, display_order=1)] - kcp: bool, - /// 映射名称 - #[clap(short, long, default_value = "anonymous", display_order = 1)] - name: String, - /// 启用socks - #[clap(long, default_value = "false", action = ArgAction::SetTrue, display_order=2)] - socks: bool, - /// 映射成功,实际访问端口 - #[clap( - long, - visible_alias = "bind", - visible_short_alias = 'b', - default_value = "0", - display_order = 9 - )] - visit_bind_port: u16, - /// 桥接监听地址 - #[clap( - long, - default_value = "127.0.0.1", - visible_alias = "bl", - display_order = 5 - )] - bridge_listen: IpAddr, - /// 桥接监听端口 - #[clap(long, visible_alias = "bp", display_order = 6)] - bridge_port: Option, - /// 服务端地址 - #[cfg(debug_assertions)] - #[clap(default_value = "127.0.0.1")] - server_host: String, - /// 服务端地址 - #[cfg(not(debug_assertions))] - server_host: String, - /// 服务端端口 - #[clap(default_value = "6722")] - server_port: u16, - /// 转发地址 - #[clap( - long, - default_value = "127.0.0.1", - visible_alias = "fh", - display_order = 7 - )] - forward_host: String, - /// 转发端口 - #[clap(long, default_value = "80", visible_alias = "fp", display_order = 8)] - forward_port: u16, - /// 是否启用socks5 udp转发, 默认不启用 - #[clap(long, default_value = "false", visible_alias = "su", action = ArgAction::SetTrue, display_order=2)] - socks_udp: bool, - /// socks5账号 - #[clap(long, visible_alias = "s5u", display_order = 3)] - socks_username: Option, - /// socks5密码 - #[clap(long, visible_alias = "s5p", display_order = 4)] - socks_password: Option, - /// 最大等待读取时间 - #[clap(long, default_value = "5", display_order = 11)] - maximum_rtime: u64, - /// 最大等待写入时间 - #[clap(long, default_value = "5", display_order = 12)] - maximum_wtime: u64, - /// 最大等待建立连接时间 - #[clap(long, default_value = "10", display_order = 13)] - maximum_wctime: u64, - /// 发送心跳延时 - #[clap(long, default_value = "30", display_order = 14)] - heartbeat_delay: u64, - /// 通信端口 - #[clap(long, default_value = "0", display_order = 15)] - channel_port: u16, - /// 日志级别 - #[cfg(feature = "fuc-log")] - #[cfg(debug_assertions)] - #[clap(long, default_value = "debug", display_order = 10, possible_values = ["info", "warn", "error", "debug", "trace", "off"])] - /// 日志级别 - log_level: log::LevelFilter, - #[cfg(not(debug_assertions))] - #[cfg(feature = "fuc-log")] - #[clap(long, default_value = "info", display_order = 10, possible_values = ["info", "warn", "error", "debug", "trace", "off"])] - log_level: log::LevelFilter, -} - -pub async fn fuso_main() -> fuso::Result<()> { - let args = FusoArgs::parse(); - - #[cfg(feature = "fuc-log")] - env_logger::builder() - .filter_module("fuso", args.log_level) - .default_format() - .format_module_path(false) - .init(); - - let fuso = fuso::builder_client() - .await? - .using_handshake(PenetrateRsaAndAesHandshake::Client) - .using_penetrate( - Socket::tcp(args.visit_bind_port), - Socket::tcp((args.forward_host, args.forward_port)), - ) - .maximum_retries(None) - .heartbeat_delay(Duration::from_secs(args.heartbeat_delay)) - .maximum_wait(Duration::from_secs(args.maximum_wctime)) - .set_name(args.name) - .enable_kcp(args.kcp) - .enable_socks5(args.socks) - .enable_socks5_udp(args.socks_udp) - .channel_port(args.channel_port) - .set_socks5_password(args.socks_password) - .set_socks5_username(args.socks_username) - .build( - Socket::tcp((args.server_host, args.server_port)), - FusoPenetrateConnector::new().await?, - ); - - let fuso = match args.bridge_port { - None => fuso.run(), - Some(port) => fuso - .using_bridge( - Socket::tcp((args.bridge_listen, port)), - fuso::FusoAccepter, - PenetrateRsaAndAesHandshake::Server, - ) - .run(), - }; - - fuso.await -} diff --git a/src/bin/client/fuso_main.rs b/src/bin/client/fuso_main.rs deleted file mode 100644 index b557e7d..0000000 --- a/src/bin/client/fuso_main.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod fuso_clap; - -#[cfg(feature = "fuso-clap")] -use fuso_clap as fuc; - - -fn main() -> fuso::Result<()> { - fuso::block_on(fuc::fuso_main()) -} diff --git a/src/bin/server.rs b/src/bin/server.rs new file mode 100644 index 0000000..04956e0 --- /dev/null +++ b/src/bin/server.rs @@ -0,0 +1,110 @@ +use std::net::SocketAddr; + +use fuso::{ + config::server::Config, + core::{ + accepter::{AccepterExt, MultiAccepter, StreamAccepter, TaggedAccepter}, + handshake::Handshake, + io::AsyncReadExt, + rpc::AsyncCaller, + split::SplitStream, + stream::fallback::Fallback, + }, + error, +}; + +#[derive(Debug, Clone)] +pub enum Tagged { + Kcp, + Tcp, +} + +#[cfg(feature = "fuso-cli")] +fn main() { + match fuso::cli::server::parse() { + Ok(conf) => fuso::enter_async_main(enter_fuso_main(conf)).unwrap(), + Err(e) => { + println!("{:?}", e) + } + } +} + +async fn enter_fuso_main(conf: Config) -> error::Result<()> { + // conf.listens.len() + env_logger::builder() + .filter_level(log::LevelFilter::Debug) + .init(); + + let mut accepter = MultiAccepter::new(); + + for listen in conf.listens { + match listen { + fuso::config::server::Listen::Kcp(kcp) => { + accepter.add(TaggedAccepter::new( + Tagged::Kcp, + StreamAccepter::new({ + fuso::core::net::KcpListener::bind_with_tokio( + Default::default(), + kcp.as_socket_addr(), + ) + .await? + }), + )); + } + fuso::config::server::Listen::Tcp(tcp) => { + accepter.add(TaggedAccepter::new( + Tagged::Tcp, + StreamAccepter::new({ + fuso::core::net::TcpListener::bind_with_tokio(tcp.as_socket_addr()).await? + }), + )); + } + fuso::config::server::Listen::Proxy(proxy) => { + accepter.add(TaggedAccepter::new( + Tagged::Tcp, + StreamAccepter::new({ + fuso::core::net::TcpListener::bind_with_tokio(proxy.as_socket_addr()) + .await? + }), + )); + } + fuso::config::server::Listen::Tunnel(forward) => { + accepter.add(TaggedAccepter::new( + Tagged::Tcp, + StreamAccepter::new({ + fuso::core::net::TcpListener::bind_with_tokio(forward.as_socket_addr()) + .await? + }), + )); + } + } + } + + loop { + let (tag, (addr, s)) = accepter.accept().await?; + + let mut a = s.do_handshake(&()).await?; + + + + let mut buf = [0u8; 1024]; + + let (mut reader, writer) = a.split(); + + let n = reader.read(&mut buf).await?; + + // let (k, s) = a.into_inner(); + + // assert_eq!(k, Some(buf[..n].to_vec())); + + println!("{:?}", String::from_utf8_lossy(&buf[..n])); + + + + + + println!("{:?} => {}", tag, addr); + } + + Ok(()) +} diff --git a/src/bin/server/fuso_main.rs b/src/bin/server/fuso_main.rs deleted file mode 100644 index 88b10e4..0000000 --- a/src/bin/server/fuso_main.rs +++ /dev/null @@ -1,75 +0,0 @@ -use std::net::IpAddr; - -use clap::Parser; - -#[derive(Parser)] -pub struct FusoArgs { - /// 监听的端口 - #[clap(short, long, default_value = "6722")] - port: u16, - /// 监听的地址 - #[clap(short, long, default_value = "0.0.0.0")] - listen: IpAddr, - /// 启用udp转发 - #[clap(long, default_value = "false")] - enable_ufd: bool, - /// 启用socks5 - #[clap(long, default_value = "false")] - enable_socks: bool, - /// webhook - #[clap(long)] - observer: Option, - /// 日志级别 - #[cfg(debug_assertions)] - #[cfg(feature = "fus-log")] - #[clap(long, default_value = "debug")] - log_level: log::LevelFilter, - /// 日志级别 - #[cfg(feature = "fus-log")] - #[cfg(not(debug_assertions))] - #[clap(long, default_value = "info")] - log_level: log::LevelFilter, - /// 发送心跳延时 - #[clap(long, default_value = "30")] - heartbeat_delay: u64, -} - -#[cfg(feature = "fus-log")] -fn init_logger(log_level: log::LevelFilter) { - let is_info_log = log_level.eq(&log::LevelFilter::Info); - env_logger::builder() - .filter_module("fuso", log_level) - .default_format() - .format_timestamp_millis() - .format_target(!is_info_log) - .init(); -} - -fn main() -> fuso::Result<()> { - use fuso::{ - observer::Executable, penetrate::PenetrateRsaAndAesHandshake, FusoExecutor, - FusoUdpServerProvider, Socket, FusoUdpForwardProvider, - }; - use std::time::Duration; - - let args = FusoArgs::parse(); - - #[cfg(feature = "fus-log")] - init_logger(args.log_level); - - fuso::block_on(async move { - fuso::builder_server(Executable::new(args.observer, FusoExecutor)) - .using_handshake(PenetrateRsaAndAesHandshake::Server) - .using_kcp(FusoUdpServerProvider, FusoExecutor) - .using_penetrate() - .heartbeat_timeout(Duration::from_secs(args.heartbeat_delay)) - .using_adapter() - .using_direct() - .using_socks() - .using_udp_forward(FusoUdpForwardProvider) - .build() - .bind(Socket::tcp((args.listen, args.port))) - .run() - .await - }) -} diff --git a/src/channel/mod.rs b/src/channel/mod.rs new file mode 100644 index 0000000..bdb9d62 --- /dev/null +++ b/src/channel/mod.rs @@ -0,0 +1 @@ +mod remote; \ No newline at end of file diff --git a/src/channel/remote.rs b/src/channel/remote.rs new file mode 100644 index 0000000..bf9ccc7 --- /dev/null +++ b/src/channel/remote.rs @@ -0,0 +1,8 @@ +pub struct RemoteChannel{ + +} + + +impl RemoteChannel{ + +} \ No newline at end of file diff --git a/src/cli/client.rs b/src/cli/client.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/cli/mod.rs b/src/cli/mod.rs new file mode 100644 index 0000000..ea0a7ab --- /dev/null +++ b/src/cli/mod.rs @@ -0,0 +1,5 @@ +pub mod client; +pub mod server; + + + diff --git a/src/cli/server.rs b/src/cli/server.rs new file mode 100644 index 0000000..c6e3248 --- /dev/null +++ b/src/cli/server.rs @@ -0,0 +1,23 @@ +use clap::Parser; + +use crate::{config::server::Config, error}; + +#[derive(Debug, Parser)] +#[command(version, about, long_about = None)] +pub struct App { + #[arg(short, long)] + config: Option, +} + +pub fn parse() -> error::Result { + let mut app = App::parse(); + + app.config.replace(format!("config/server.toml")); + + let cfg = match app.config { + None => Config::default(), + Some(cfg) => toml::from_str(&{ std::fs::read_to_string(cfg)? })?, + }; + + Ok(cfg) +} diff --git a/src/client/builder.rs b/src/client/builder.rs deleted file mode 100644 index 755c1d4..0000000 --- a/src/client/builder.rs +++ /dev/null @@ -1,57 +0,0 @@ -use std::{future::Future, pin::Pin, sync::Arc, time::Duration}; - -use crate::{ - generator::Generator, ClientProvider, DecorateProvider, Executor, Fuso, Processor, Provider, - Socket, Stream, WrappedProvider, -}; - -use super::Client; - -type BoxedFuture = Pin> + Send + 'static>>; - -pub struct ClientBuilder { - pub(crate) executor: E, - pub(crate) retry_delay: Option, - pub(crate) maximum_retries: Option, - pub(crate) handshake: Option>)>>, - pub(crate) client_provider: ClientProvider

, -} - -impl ClientBuilder -where - E: Executor + 'static, - P: Provider> + Send + Sync + 'static, - S: Stream + Send + 'static, -{ - pub fn using_handshake(mut self, handshake: H) -> Self - where - H: Provider>)>> - + Send - + Sync - + 'static, - { - self.handshake = Some(WrappedProvider::wrap(handshake)); - self - } - - pub fn build, H, G>(self, socket: A, handler: H) -> Fuso> - where - G: Generator>> + Unpin + Send + 'static, - H: Provider<(S, Processor, S, ()>), Output = BoxedFuture> - + Send - + Sync - + 'static, - { - let socket = socket.into(); - - Fuso(Client { - socket: socket.clone(), - maximum_retries: self.maximum_retries, - retry_delay: self.retry_delay.unwrap_or(Duration::from_secs(5)), - handler: Arc::new(handler), - executor: Arc::new(self.executor), - handshake: self.handshake, - client_provider: self.client_provider.set_server_socket(socket), - }) - } -} diff --git a/src/client/mod.rs b/src/client/mod.rs index 269c1c7..e69de29 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,131 +0,0 @@ -mod builder; - -use std::{future::Future, pin::Pin, sync::Arc, time::Duration}; - -pub use builder::*; - -use crate::{ - generator::{Generator, GeneratorEx}, - time, ClientProvider, DecorateProvider, Executor, Fuso, Kind, Processor, Provider, Serve, - Socket, Stream, WrappedProvider, -}; - -type BoxedFuture = Pin> + Send + 'static>>; - -pub enum Route { - Forward(S), - Provider(WrappedProvider), -} - -pub struct Client { - pub(crate) socket: Socket, - pub(crate) executor: Arc, - pub(crate) handler: Arc, - pub(crate) maximum_retries: Option, - pub(crate) retry_delay: Duration, - pub(crate) handshake: Option>)>>, - pub(crate) client_provider: ClientProvider

, -} - -impl Client -where - E: Executor + 'static, - S: Stream + Send + 'static, - P: Provider> + Send + Sync + 'static, - G: Generator>> + Unpin + Send + 'static, - H: Provider<(S, Processor, S, ()>), Output = BoxedFuture> - + Send - + Sync - + 'static, -{ - async fn run(self) -> crate::Result<()> { - let executor = self.executor; - let provider = self.client_provider.clone(); - let handshake = self.handshake; - let mut retries_count = 0; - let maximum_retries = self.maximum_retries; - - loop { - let socket = self.socket.clone(); - - let stream = match self.client_provider.connect(socket).await { - Ok(stream) => { - log::info!("connection established"); - stream - } - Err(e) => { - log::warn!("connect to {} failed err: {}", self.socket, e); - time::sleep(self.retry_delay).await; - - if let Some(retries) = maximum_retries { - if retries > retries_count { - break Err(Kind::MaxRetries(retries).into()); - } - } - - retries_count += 1; - - log::debug!("reconnect({}) to {} ", retries_count, self.socket); - - continue; - } - }; - - let stream = match handshake.as_ref() { - Some(handshake) => handshake.call(stream).await, - None => Ok((stream, None)), - }; - - let (server, decorator) = match stream { - Ok(stream) => stream, - Err(e) => { - log::error!("handshake failed {}", e); - break Ok(()); - } - }; - - let processor = Processor::new(Arc::new(provider.clone()), None, decorator); - - let mut generate = match self.handler.call((server, processor)).await { - Ok(generate) => generate, - Err(e) => { - log::warn!("processing failed ! err: {}", e); - time::sleep(self.retry_delay).await; - continue; - } - }; - - loop { - match generate.next().await { - Ok(None) => break, - Ok(Some(fut)) => { - executor.spawn(fut); - } - Err(e) => { - log::error!("encountered an error err: {}", e); - time::sleep(self.retry_delay).await; - break; - } - } - } - } - } -} - -impl Fuso> -where - E: Executor + 'static, - H: Provider<(S, Processor, S, ()>), Output = BoxedFuture> - + Send - + Sync - + 'static, - P: Provider> + Send + Sync + 'static, - S: Stream + Send + 'static, - G: Generator>> + Unpin + Send + 'static, -{ - pub fn run(self) -> Fuso { - Fuso(Serve { - fut: Box::pin(self.0.run()), - }) - } -} diff --git a/src/config/client.rs b/src/config/client.rs new file mode 100644 index 0000000..abc6007 --- /dev/null +++ b/src/config/client.rs @@ -0,0 +1,182 @@ +use std::{ + collections::{HashMap, HashSet}, + io, + net::IpAddr, +}; + +use serde::{Deserialize, Serialize}; + +use super::{Authentication, BootKind, Compress, Crypto, KeepAlive}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Config { + pub server: Server, + pub features: Vec, + #[serde(flatten)] + pub services: HashMap, + #[serde(default = "Default::default")] + pub default_crypto: Vec, + #[serde(default = "Default::default")] + pub default_compress: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum Feature { + Kcp, + Socks5, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Server { + pub addr: ServerAddr, + pub ports: Vec, + pub retries: i32, + #[serde(default = "Default::default")] + pub crypto: Vec, + #[serde(default = "Default::default")] + pub compress: Vec, + #[serde(rename = "auth")] + pub authentication: Authentication, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ServerAddr { + WithIpAddr(Vec), + WithDomain(Vec), +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(tag = "type", rename_all = "lowercase")] +pub enum Service { + /// 代理 + Proxy(WithProxyService), + Bridge(WithBridgeService), + /// 端口转发 + Forward(WithForwardService), +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct WithForwardService { + /// 服务器运行方式 + #[serde(default = "Default::default")] + pub boot: BootKind, + /// 转发到的目标地址 + pub target: FinalTarget, + /// 对于某些长连接的请求,保持会话 + pub keep_alive: Option, + /// 服务端对外暴露的端口, 可以填写多个, 确保端口没有被占用 + /// 如果没有填写,那么随机分配,这时请确保防火墙中该随机端口被允许访问 + #[serde(default = "Default::default")] + pub exposes: Vec, + /// 与服务器建立连接时的端口 + /// 该字段是可选的, 如果没有填写, 将会使用`exposes`字段中的端口来建立连接 + /// 如果填写了0, 那么会随机分配一个端口,这时请确保防火墙中该随机端口被允许访问 + pub channel: Option, + /// 加密方式 + #[serde(default = "Default::default")] + pub crypto: HashSet, + /// 压缩方式 + #[serde(default = "Default::default")] + pub compress: HashSet, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct WithBridgeService { + pub bind: IpAddr, + pub port: u16, + #[serde(rename = "auth")] + pub authentication: Authentication, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct WithProxyService { + /// 监听地址 + pub bind: IpAddr, + /// 监听端口 + pub port: u16, + /// 启动方式 + #[serde(default = "Default::default")] + pub boot: BootKind, + #[serde(default = "Default::default")] + pub rewrite: Option, + /// 转发到的目标地址 + #[serde(default = "Default::default")] + pub target: FinalTarget, + /// 加密方式 + #[serde(default = "Default::default")] + pub crypto: HashSet, + /// 压缩方式 + #[serde(default = "Default::default")] + pub compress: HashSet, + /// 对于某些长连接的请求,保持会话 + pub keep_alive: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(tag = "with", rename_all = "lowercase")] +pub enum Rewrite { + #[serde(rename = "http_header")] + HttpHeader(WithHttpHeaderRewrite), +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct WithHttpHeaderRewrite { + #[serde(flatten)] + pub headers: HashMap, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(tag = "type", rename_all = "lowercase")] +pub enum FinalTarget { + /// 静态地址 + Static { addr: ServerAddr, port: u16 }, + /// 当该地址是一个动态地址时, 代理模式将会变为socks5 + Dynamic, +} + +impl Default for FinalTarget { + fn default() -> Self { + Self::Dynamic + } +} + + +impl ServerAddr { + async fn connect(&self, port: u16) -> io::Result<()> { + match self { + ServerAddr::WithIpAddr(addr) => { + unimplemented!() + } + ServerAddr::WithDomain(domain) => { + unimplemented!() + } + } + } +} + + +#[cfg(test)] +#[cfg(feature = "fuso-toml")] +mod tests { + + use super::Config; + + #[test] + fn test_client_config() -> std::io::Result<()> { + let config = std::fs::read("config/client.toml")?; + + println!("{}", String::from_utf8_lossy(&config)); + + let a = toml::from_str::(&String::from_utf8(config).unwrap()).unwrap(); + + // let a = a.server.addr.connect(80).await; + + // let a = a.services.get("").unwrap(); + + println!("{:#?}", a); + + Ok(()) + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..0aa9516 --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,57 @@ +use serde::{Deserialize, Serialize}; + +pub mod client; +pub mod server; + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "type", rename_all = "lowercase")] + +pub enum Authentication { + None, + Secret(AuthWithSecret), + Account(AuthWithAccount), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AuthWithSecret { + secret: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AuthWithAccount { + username: String, + password: String, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] +#[serde(rename_all = "lowercase")] +pub enum BootKind { + /// 为fork时, 表示开启一个子进程 + Fork, + /// 默认为单进程模式 + Default, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] +#[serde(rename_all = "lowercase")] +pub enum Crypto { + Aes, + Rsa +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] +#[serde(rename_all = "lowercase")] +pub enum Compress { + Lz4, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct KeepAlive { + interval: u32, +} + +impl Default for BootKind { + fn default() -> Self { + Self::Default + } +} diff --git a/src/config/server.rs b/src/config/server.rs new file mode 100644 index 0000000..cc0d49b --- /dev/null +++ b/src/config/server.rs @@ -0,0 +1,122 @@ +use std::{ + collections::HashSet, + net::{IpAddr, SocketAddr}, + ops::Deref, +}; + +use serde::{Deserialize, Serialize}; + +use super::{Authentication, Compress, Crypto}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Config { + /// 加密方式 + #[serde(default = "Default::default")] + pub crypto: HashSet, + /// 压缩方式 + #[serde(default = "Default::default")] + pub compress: HashSet, + #[serde(rename = "listen")] + pub listens: HashSet, + pub manager: Option, + pub features: HashSet, + #[serde(rename = "auth")] + pub authentication: Authentication, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Manager { + pub bind: IpAddr, + pub port: u16, + pub context: String, + #[serde(rename = "auth")] + pub authentication: Authentication, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +#[serde(rename_all = "lowercase")] +pub enum Feature { + Kcp, + Proxy, + Socks5, + Forward, + MultiProcess, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +#[serde(tag = "type", rename_all = "lowercase")] +pub enum Listen { + Kcp(KcpListenMetadata), + Tcp(ListenMetadata), + Proxy(ListenMetadata), + Tunnel(ListenMetadata), +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] + +pub struct ListenMetadata { + pub bind: IpAddr, + pub port: u16, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct KcpListenMetadata { + #[serde(flatten)] + pub metadata: ListenMetadata, + // config: kcp_rust::Config +} + +impl ListenMetadata { + pub fn as_socket_addr(self) -> SocketAddr { + SocketAddr::new(self.bind, self.port) + } +} + +impl From for SocketAddr { + fn from(value: ListenMetadata) -> Self { + value.as_socket_addr() + } +} + +impl Deref for KcpListenMetadata { + type Target = ListenMetadata; + fn deref(&self) -> &Self::Target { + &self.metadata + } +} + +impl Default for Config { + fn default() -> Self { + Config { + listens: Default::default(), + manager: None, + features: HashSet::new(), + authentication: Authentication::None, + crypto: Default::default(), + compress: Default::default(), + } + } +} + +#[cfg(test)] +#[cfg(feature = "fuso-toml")] +mod tests { + use std::collections::HashSet; + + use crate::config::Authentication; + + use super::Config; + + #[test] + fn test_server_config() -> std::io::Result<()> { + let config = std::fs::read("config/server.toml")?; + + println!("{}", String::from_utf8_lossy(&config)); + + let a = toml::from_str::(&String::from_utf8(config).unwrap()).unwrap(); + + println!("{:#?}", a); + + Ok(()) + } +} diff --git a/src/core/accepter.rs b/src/core/accepter.rs index f902206..bb34f8f 100644 --- a/src/core/accepter.rs +++ b/src/core/accepter.rs @@ -1,262 +1,182 @@ -use crate::{ready, Address, ReadBuf, Result}; -use std::net::SocketAddr; -use std::sync::Arc; -use std::{future::Future, pin::Pin}; +use std::{ + net::SocketAddr, + pin::Pin, + task::{Context, Poll}, +}; -use std::task::{Context, Poll}; +use crate::error; -pub trait NetSocket { - fn peer_addr(&self) -> crate::Result

; +use super::{BoxedStream, Stream}; - fn local_addr(&self) -> crate::Result
; -} +pub trait Accepter { + type Output; -pub trait Accepter: NetSocket { - type Stream; - fn poll_accept(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; + fn poll_accept( + self: Pin<&mut Self>, + ctx: &mut Context<'_>, + ) -> Poll>; } -pub struct AccepterWrapper(Box + Send + Unpin + 'static>); - -pub trait UdpSocket: NetSocket { - fn poll_recv_from( - self: Pin<&Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll>; - - fn poll_send(self: Pin<&Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll>; - - fn poll_recv(self: Pin<&Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>) - -> Poll>; - - fn poll_send_to( - self: Pin<&Self>, - cx: &mut Context<'_>, - addr: &SocketAddr, - buf: &[u8], - ) -> Poll>; +pub trait AccepterExt: Accepter { + fn accept<'a>(&'a mut self) -> Accept<'a, Self> + where + Self: Sized + Unpin, + { + Accept { accepter: self } + } } -pub struct Accept<'a, T> { - accepter: &'a mut T, +pub struct Accept<'a, A: Unpin> { + accepter: &'a mut A, } -#[pin_project::pin_project] -pub struct RecvFrom<'a, T> { - buf: ReadBuf<'a>, - #[pin] - receiver: &'a T, -} +pub struct BoxedAccepter<'a, O>(Box + Unpin + Send + 'a>); -#[pin_project::pin_project] -pub struct UdpSend<'a, T> { - buf: &'a [u8], - #[pin] - sender: &'a T, -} +pub struct StreamAccepter<'a, O>(Box + Unpin + Send + 'a>); -#[pin_project::pin_project] -pub struct UdpRecv<'a, T> { - buf: ReadBuf<'a>, - #[pin] - receiver: &'a T, +pub struct TaggedAccepter<'a, T: Clone, O> { + tag: T, + accepter: BoxedAccepter<'a, O>, } -#[pin_project::pin_project] -pub struct SendTo<'a, T> { - buf: &'a [u8], - addr: &'a SocketAddr, - #[pin] - sender: &'a T, +pub struct MultiAccepter<'a, O> { + accepter_list: Vec>, } -pub trait AccepterExt: Accepter { - fn accept<'a>(&'a mut self) -> Accept<'a, Self> +impl<'a, O> BoxedAccepter<'a, O> { + pub fn new(accepter: A) -> Self where - Self: Sized + Unpin, + A: Accepter + Unpin + Send + 'a, { - Accept { accepter: self } + Self(Box::new(accepter)) } } -pub trait UdpReceiverExt: UdpSocket { - fn recv_from<'a>(&'a self, buf: &'a mut [u8]) -> RecvFrom<'a, Self> +impl<'a, T, O> TaggedAccepter<'a, T, O> +where + T: Clone, +{ + pub fn new(tag: T, accepter: A) -> Self where - Self: Sized + Unpin, + A: Accepter + Unpin + Send + 'a, { - RecvFrom { - buf: ReadBuf::new(buf), - receiver: self, + Self { + tag, + accepter: BoxedAccepter::new(accepter), } } +} - fn send<'a>(&'a self, buf: &'a [u8]) -> UdpSend<'a, Self> - where - Self: Sized + Unpin, - { - UdpSend { buf, sender: self } - } - - fn recv<'a>(&'a self, buf: &'a mut [u8]) -> UdpRecv<'a, Self> - where - Self: Sized + Unpin, - { - UdpRecv { - buf: ReadBuf::new(buf), - receiver: self, +impl<'a, O> MultiAccepter<'a, O> { + pub fn new() -> Self { + MultiAccepter { + accepter_list: Default::default(), } } - fn send_to<'a>(&'a self, addr: &'a SocketAddr, buf: &'a [u8]) -> SendTo<'a, Self> + pub fn add(&mut self, accepter: A) where - Self: Sized + Unpin, + A: Accepter + Unpin + Send + 'static, { - SendTo { - buf, - addr, - sender: self, - } + self.accepter_list.push(BoxedAccepter::new(accepter)) } } -impl AccepterExt for T where T: Accepter + Unpin {} -impl UdpReceiverExt for T where T: UdpSocket + Unpin {} +impl<'a, O> Accepter for MultiAccepter<'a, O> { + type Output = O; -impl<'a, T> Future for Accept<'a, T> -where - T: Accepter + Unpin, -{ - type Output = Result; - - fn poll( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - Pin::new(&mut *self.accepter).poll_accept(cx) - } -} + fn poll_accept( + mut self: Pin<&mut Self>, + ctx: &mut Context<'_>, + ) -> Poll> { + for accepter in &mut self.accepter_list { + match Pin::new(accepter).poll_accept(ctx)? { + Poll::Pending => continue, + Poll::Ready(out) => return Poll::Ready(Ok(out)), + } + } -impl<'a, T> Future for RecvFrom<'a, T> -where - T: UdpSocket + Unpin, -{ - type Output = Result<(usize, SocketAddr)>; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.project(); - let addr = ready!(Pin::new(&**this.receiver).poll_recv_from(cx, this.buf))?; - Poll::Ready(Ok((this.buf.position(), addr))) + Poll::Pending } } -impl<'a, T> Future for SendTo<'a, T> -where - T: UdpSocket + Unpin, -{ - type Output = Result; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.project(); - Pin::new(&**this.sender).poll_send_to(cx, this.addr, this.buf) +impl<'a, O> Accepter for BoxedAccepter<'a, O> { + type Output = O; + fn poll_accept( + mut self: Pin<&mut Self>, + ctx: &mut Context<'_>, + ) -> Poll> { + Pin::new(&mut *self.0).poll_accept(ctx) } } -impl<'a, T> Future for UdpSend<'a, T> +impl<'a, T, O> Accepter for TaggedAccepter<'a, T, O> where - T: UdpSocket + Unpin, + T: Clone + Unpin, { - type Output = Result; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.project(); - Pin::new(&**this.sender).poll_send(cx, this.buf) + type Output = (T, O); + + fn poll_accept( + mut self: Pin<&mut Self>, + ctx: &mut Context<'_>, + ) -> Poll> { + match Pin::new(&mut self.accepter).poll_accept(ctx)? { + Poll::Pending => Poll::Pending, + Poll::Ready(o) => Poll::Ready(Ok((self.tag.clone(), o))), + } } } -impl<'a, T> Future for UdpRecv<'a, T> +impl<'a, A> std::future::Future for Accept<'a, A> where - T: UdpSocket + Unpin, + A: Accepter + Unpin, { - type Output = Result; + type Output = error::Result; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.project(); - ready!(Pin::new(&**this.receiver).poll_recv(cx, this.buf))?; - Poll::Ready(Ok(this.buf.position())) + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Pin::new(&mut *self.accepter).poll_accept(cx) } } -impl NetSocket for Arc +impl<'a, O> StreamAccepter<'a, (SocketAddr, O)> where - U: UdpSocket, + O: Stream + 'static, { - fn local_addr(&self) -> crate::Result
{ - (**self).local_addr() - } - - fn peer_addr(&self) -> crate::Result
{ - (**self).peer_addr() + pub fn new(accepter: A) -> Self + where + A: Accepter + Unpin + Send + 'a, + { + Self(Box::new(accepter)) } } -impl UdpSocket for Arc +impl<'a, O> Accepter for StreamAccepter<'a, (SocketAddr, O)> where - U: UdpSocket + Unpin + 'static, + O: Stream + Send + Unpin + 'static, { - fn poll_recv_from( - self: Pin<&Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll> { - Pin::new(&**self).poll_recv_from(cx, buf) - } - - fn poll_send(self: Pin<&Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { - Pin::new(&**self).poll_send(cx, buf) - } - - fn poll_recv( - self: Pin<&Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll> { - Pin::new(&**self).poll_recv(cx, buf) - } - - fn poll_send_to( - self: Pin<&Self>, - cx: &mut Context<'_>, - addr: &SocketAddr, - buf: &[u8], - ) -> Poll> { - Pin::new(&**self).poll_send_to(cx, addr, buf) + type Output = (SocketAddr, BoxedStream<'a>); + fn poll_accept( + mut self: Pin<&mut Self>, + ctx: &mut Context<'_>, + ) -> Poll> { + match Pin::new(&mut *self.0).poll_accept(ctx)? { + Poll::Pending => Poll::Pending, + Poll::Ready((addr, stream)) => Poll::Ready(Ok((addr, BoxedStream::new(stream)))), + } } } -impl AccepterWrapper { - pub fn wrap(accepter: A) -> Self - where - A: Accepter + Send + Unpin + 'static, - { - AccepterWrapper(Box::new(accepter)) - } -} +impl AccepterExt for A where A: Accepter {} -impl NetSocket for AccepterWrapper { - fn local_addr(&self) -> crate::Result
{ - self.0.local_addr() - } +#[cfg(test)] +mod tests { - fn peer_addr(&self) -> crate::Result
{ - self.0.peer_addr() - } -} + use super::{AccepterExt, MultiAccepter}; -impl Accepter for AccepterWrapper -where - S: Unpin, -{ - type Stream = S; + #[tokio::test] + async fn test_accepter() { + let mut accepter = MultiAccepter::<(i32, i32)>::new(); - fn poll_accept(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut *self.0).poll_accept(cx) + let (a, b) = accepter.accept().await.unwrap(); } } diff --git a/src/core/boxed.rs b/src/core/boxed.rs deleted file mode 100644 index ed83fe1..0000000 --- a/src/core/boxed.rs +++ /dev/null @@ -1,73 +0,0 @@ -use std::pin::Pin; - -use crate::{Address, AsyncRead, AsyncWrite, NetSocket, Stream}; - -pub struct FusoStream(Box); - -impl FusoStream { - pub fn new(t: T) -> Self - where - T: Stream + Send + 'static, - { - Self(Box::new(t)) - } -} - -pub trait ToBoxStream { - fn into_boxed_stream(self) -> FusoStream; -} - -unsafe impl Sync for FusoStream {} - -impl NetSocket for FusoStream { - fn peer_addr(&self) -> crate::Result
{ - self.0.peer_addr() - } - - fn local_addr(&self) -> crate::Result
{ - self.0.local_addr() - } -} - -impl AsyncWrite for FusoStream { - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> std::task::Poll> { - Pin::new(&mut *self.0).poll_write(cx, buf) - } - - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - Pin::new(&mut *self.0).poll_flush(cx) - } - - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - Pin::new(&mut *self.0).poll_close(cx) - } -} - -impl AsyncRead for FusoStream { - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &mut crate::ReadBuf<'_>, - ) -> std::task::Poll> { - Pin::new(&mut *self.0).poll_read(cx, buf) - } -} - -impl ToBoxStream for T -where - T: NetSocket + Stream + Send + 'static, -{ - fn into_boxed_stream(self) -> FusoStream { - FusoStream::new(self) - } -} diff --git a/src/core/compress/lz4/mod.rs b/src/core/compress/lz4/mod.rs deleted file mode 100644 index 3f3ae45..0000000 --- a/src/core/compress/lz4/mod.rs +++ /dev/null @@ -1,537 +0,0 @@ -mod third_party; - -use std::{ffi::c_void, pin::Pin, task::Poll}; - -use crate::{guard::buffer::Buffer, AsyncRead, AsyncWrite, Lz4Err, NetSocket, ReadBuf}; - -use super::{Decoder, Encoder}; - -/// ref https://github.&com/lz4/lz4/blob/dev/examples/blockStreaming_ringBuffer.c -pub struct Lz4Compress { - lz4_stream: T, - /// 上层读取时,如果解压后的数据大于了提供的缓冲区时,数据将缓存到此处 - lz4_rbuf: Buffer, - /// 压缩缓冲区 - lz4_ebuf: Option>, - /// 解压缓冲区 - lz4_dbuf: Option>, - /// 需要从lz4_stream读取的数据 - lz4_dneed: usize, - /// lz4_dbuf偏移 - lz4_doffset: usize, - /// dbuf 初始化状态 - lz4_dinit: bool, - /// 环形压缩缓冲 - lz4_ering_buf: Vec, - /// 环形解压缓冲 - lz4_dring_buf: Vec, - /// 偏移量 - lz4_dring_offset: usize, - /// 偏移量 - lz4_ering_offset: usize, - /// 相对lz4_ebuf写入偏移 - lz4_woffset: usize, - /// 对于write提供的buf偏移 - lz4_compressed_offset: usize, - /// 一次最多能压缩的数据 - lz4_compress_max_bytes: usize, - /// ---- - lz4_encode_insptr: *mut c_void, - /// ---- - lz4_decode_insptr: *mut c_void, -} - -unsafe impl Send for Lz4Compress {} -unsafe impl Sync for Lz4Compress {} - -impl NetSocket for Lz4Compress -where - T: NetSocket, -{ - fn local_addr(&self) -> crate::Result { - self.lz4_stream.local_addr() - } - - fn peer_addr(&self) -> crate::Result { - self.lz4_stream.peer_addr() - } -} - -impl Lz4Compress -where - T: AsyncRead + AsyncWrite + Unpin, -{ - pub fn new(lz4_stream: T) -> Self { - let mut lz4_ering_buf = Vec::with_capacity(1024 * 2 + 1024); - let mut lz4_dring_buf = Vec::with_capacity(1024 * 2 + 1024); - - unsafe { - // 临时使用,未来将改为配置文件的形式 - lz4_ering_buf.set_len(1024 * 2 + 1024); - lz4_dring_buf.set_len(1024 * 2 + 1024); - } - - log::debug!( - "use lz4 compression ering: {}, dring: {}", - lz4_ering_buf.capacity(), - lz4_dring_buf.capacity() - ); - - Self { - lz4_dinit: false, - lz4_dneed: 0, - lz4_ebuf: None, - lz4_dbuf: None, - lz4_woffset: 0, - lz4_stream, - lz4_doffset: 0, - lz4_dring_buf, - lz4_ering_buf, - lz4_rbuf: Default::default(), - lz4_ering_offset: 0, - lz4_dring_offset: 0, - lz4_compressed_offset: 0, - lz4_compress_max_bytes: 1024, - lz4_decode_insptr: unsafe { third_party::LZ4_createStreamDecode() }, - lz4_encode_insptr: unsafe { third_party::LZ4_createStream() }, - } - } -} - -impl Drop for Lz4Compress { - fn drop(&mut self) { - if !self.lz4_encode_insptr.is_null() { - unsafe { - third_party::LZ4_freeStream(self.lz4_encode_insptr); - self.lz4_encode_insptr = std::ptr::null_mut(); - } - } - - if !self.lz4_decode_insptr.is_null() { - unsafe { - third_party::LZ4_freeStreamDecode(self.lz4_decode_insptr); - self.lz4_encode_insptr = std::ptr::null_mut(); - } - } - } -} - -impl AsyncRead for Lz4Compress -where - T: AsyncRead + Unpin, -{ - fn poll_read( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &mut crate::ReadBuf<'_>, - ) -> std::task::Poll> { - log::debug!("lz4 decode buf: {}bytes", buf.len()); - - if !self.lz4_rbuf.is_empty() { - let unfilled = buf.initialize_unfilled(); - let n = self.lz4_rbuf.read_to_buffer(unfilled); - buf.advance(n); - return Poll::Ready(Ok(n)); - } - - if self.lz4_decode_insptr.is_null() { - return Poll::Ready(Err(Lz4Err::EncodeReleased.into())); - } - - self.poll_decode_read(cx, buf) - } -} - -impl AsyncWrite for Lz4Compress -where - T: AsyncWrite + Unpin, -{ - fn poll_close( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - Pin::new(&mut self.lz4_stream).poll_close(cx) - } - - fn poll_flush( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - Pin::new(&mut self.lz4_stream).poll_flush(cx) - } - - fn poll_write( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> std::task::Poll> { - log::debug!("lz4 encode data: {}bytes", buf.len()); - - if self.lz4_encode_insptr.is_null() { - return Poll::Ready(Err(Lz4Err::EncodeReleased.into())); - } - - self.poll_encode_write(cx, buf) - } -} - -impl Encoder for Lz4Compress -where - T: AsyncWrite + Unpin, -{ - fn poll_encode_write( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context, - buf: &[u8], - ) -> std::task::Poll> { - loop { - let woffset = self.lz4_woffset; - let max_bytes = self.lz4_compress_max_bytes; - - match self.lz4_ebuf.take() { - Some(ebuf) => { - let n = match Pin::new(&mut self.lz4_stream).poll_write(cx, &ebuf[woffset..])? { - Poll::Ready(n) => n, - Poll::Pending => { - drop(std::mem::replace(&mut self.lz4_ebuf, Some(ebuf))); - return Poll::Pending; - } - }; - - if n == 0 { - return Poll::Ready(Ok(n)); - } - - self.lz4_woffset += n; - - if self.lz4_woffset == ebuf.len() && self.lz4_compressed_offset == buf.len() { - self.lz4_woffset = 0; - self.lz4_compressed_offset = 0; - break Poll::Ready(Ok(buf.len())); - } else if self.lz4_woffset == ebuf.len() { - self.lz4_woffset = 0; - } else { - drop(std::mem::replace(&mut self.lz4_ebuf, Some(ebuf))); - } - } - None => { - // ebuf.len() == 0 说明是第一次写, 进行初始化 - // 用户提供的数据有可能会超过lz4_compress_max_bytes, 那么这里就需要进行分片压缩 - // 还挺复杂... - - let compress_buf_size = - unsafe { third_party::LZ4_compressBound(max_bytes as i32) as usize }; - - let mut ebuf: Vec = Vec::with_capacity(compress_buf_size + 4); - - let need_compress = &buf[self.lz4_compressed_offset..]; - let need_compress_len = need_compress.len(); - let compress_len = { - if need_compress_len < max_bytes { - need_compress_len - } else { - let diff = need_compress_len - max_bytes; - need_compress_len - diff - } - }; - - unsafe { - let lz4_encode_ins = self.lz4_encode_insptr; - let need_ring_offset = self.lz4_ering_offset; - let need_ring_buf = &mut self.lz4_ering_buf[need_ring_offset..]; - - std::ptr::copy( - need_compress.as_ptr(), - need_ring_buf.as_mut_ptr(), - compress_len, - ); - - let compressed_len = third_party::LZ4_compress_fast_continue( - lz4_encode_ins, - need_ring_buf.as_ptr(), - ebuf.as_mut_ptr().offset(4), - compress_len as i32, - compress_buf_size as i32, - 0, - ); - - log::debug!( - "total {}bytes, compressed: {}bytes, max: {}, ring: {}, offset: {}", - buf.len(), - compressed_len, - compress_len, - need_ring_buf.len(), - need_ring_offset - ); - - if compressed_len <= 0 { - return Poll::Ready(Err(Lz4Err::Compress.into())); - } - - let compress_len_bytes = compressed_len.to_le_bytes(); - - std::ptr::copy(compress_len_bytes.as_ptr(), ebuf.as_mut_ptr(), 4); - - ebuf.set_len(compressed_len as usize + 4); - } - - self.lz4_compressed_offset += compress_len as usize; - - self.lz4_ering_offset = { - if self.lz4_ering_offset + compress_len - >= self.lz4_ering_buf.capacity() - self.lz4_compress_max_bytes - { - 0 - } else { - self.lz4_ering_offset + compress_len - } - }; - - drop(std::mem::replace(&mut self.lz4_ebuf, Some(ebuf))); - } - } - } - } -} - -impl Decoder for Lz4Compress -where - T: AsyncRead + Unpin, -{ - fn poll_decode_read( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context, - buf: &mut crate::ReadBuf<'_>, - ) -> std::task::Poll> { - loop { - let max_bytes = unsafe { - third_party::LZ4_compressBound(self.lz4_compress_max_bytes as i32) as usize - }; - - let need_size = self.lz4_dneed; - let need_offset = self.lz4_doffset; - - match self.lz4_dbuf.take() { - None => { - let mut dbuf = Vec::with_capacity(4); - unsafe { - dbuf.set_len(4); - } - self.lz4_dneed = 4; - drop(std::mem::replace(&mut self.lz4_dbuf, Some(dbuf))); - } - Some(dbuf) if need_offset == need_size && !self.lz4_dinit => { - // 获取到压缩块大小,继续读取.... - - let need_size = unsafe { *(dbuf.as_ptr() as *const i32) } as usize; - - if need_size <= 0 || need_size > max_bytes { - return Poll::Ready(Err(Lz4Err::Decompress.into())); - } - - let mut dbuf = Vec::with_capacity(max_bytes); - - unsafe { - dbuf.set_len(max_bytes); - } - - self.lz4_dneed = need_size; - self.lz4_doffset = 0; - self.lz4_dinit = true; - drop(std::mem::replace(&mut self.lz4_dbuf, Some(dbuf))); - } - Some(dbuf) if need_offset == need_size && self.lz4_dinit => { - let dring_offset = self.lz4_dring_offset; - let lz4_decode_ins = self.lz4_decode_insptr; - - let decompress_size = unsafe { - let max_bytes = self.lz4_compress_max_bytes; - let dring_buf = &mut self.lz4_dring_buf[dring_offset..]; - - third_party::LZ4_decompress_safe_continue( - lz4_decode_ins, - dbuf.as_ptr(), - dring_buf.as_mut_ptr(), - need_size as i32, - max_bytes as i32, - ) - }; - - if decompress_size <= 0 { - // 解压失败了 呜呜呜..... - return Poll::Ready(Err(Lz4Err::Decompress.into())); - } - - let decompress_size = decompress_size as usize; - - self.lz4_dinit = false; - self.lz4_dneed = 0; - self.lz4_doffset = 0; - - self.lz4_dring_offset = { - if self.lz4_dring_offset + decompress_size - >= self.lz4_dring_buf.capacity() - max_bytes - { - 0 - } else { - self.lz4_dring_offset + decompress_size - } - }; - - let unfilled_len = buf.remaining(); - let dring_buf = &self.lz4_dring_buf[dring_offset..][..decompress_size]; - - if unfilled_len < decompress_size { - // 提供的缓冲区不够存放当前解压后的数据,临时存放到 lz4_rbuf - - unsafe { - let unfilled = buf.initialize_unfilled(); - std::ptr::copy(dring_buf.as_ptr(), unfilled.as_mut_ptr(), unfilled_len) - } - - let rem = dring_buf[unfilled_len..].to_vec(); - - log::debug!( - "buffer {}bytes, decompressed: {}bytes, rem: {}bytes", - buf.len(), - decompress_size, - rem.len() - ); - - self.lz4_rbuf.push_all(rem); - buf.advance(unfilled_len); - - return Poll::Ready(Ok(unfilled_len)); - } else { - // 提供的缓冲区完全够大,直接copy - - unsafe { - let unfilled = buf.initialize_unfilled(); - std::ptr::copy( - dring_buf.as_ptr(), - unfilled.as_mut_ptr(), - decompress_size, - ) - } - - buf.advance(decompress_size); - - log::debug!( - "buffer {}bytes, decompressed: {}bytes", - buf.len(), - decompress_size - ); - - return Poll::Ready(Ok(decompress_size)); - } - } - Some(mut dbuf) => { - let mut read_buf = ReadBuf::new(&mut dbuf[need_offset..need_size]); - - let n = match Pin::new(&mut self.lz4_stream).poll_read(cx, &mut read_buf)? { - Poll::Ready(n) => n, - Poll::Pending => { - // 未就绪,直接返回 等待下层 wake - drop(std::mem::replace(&mut self.lz4_dbuf, Some(dbuf))); - return Poll::Pending; - } - }; - - if n == 0 { - self.lz4_dneed = 0; - self.lz4_doffset = 0; - return Poll::Ready(Ok(0)); - } else { - self.lz4_doffset += n; - drop(std::mem::replace(&mut self.lz4_dbuf, Some(dbuf))); - } - } - } - } - } -} - -#[cfg(test)] -#[allow(unused)] -#[cfg(feature = "fuso-rt-tokio")] -mod tests { - - use std::time::Duration; - - use tokio::net::{TcpListener, TcpStream}; - - use super::Lz4Compress; - use crate::{ - ext::AsyncWriteExt, - protocol::{AsyncRecvPacket, Poto, ToBytes}, - r#async::ext::AsyncReadExt, - time, - }; - - fn init_logger() { - #[cfg(feature = "fuso-log")] - env_logger::Builder::default() - .filter_module("fuso", log::LevelFilter::Debug) - .init(); - } - - #[test] - #[cfg(feature = "fuso-rt-tokio")] - fn test_lz4_client() { - init_logger(); - - tokio::runtime::Runtime::new() - .unwrap() - .block_on(async move { - let tcp = TcpStream::connect("127.0.0.1:6666").await.unwrap(); - let mut lz4 = Lz4Compress::new(tcp); - - let mut data1 = [0u8; 1500]; - let mut data2 = [0u8; 1500]; - - data1.fill_with(rand::random); - data2.fill_with(rand::random); - - lz4.write_all(&data1).await.unwrap(); - - let mut buf2 = [0u8; 1500]; - - lz4.read_exact(&mut buf2).await.unwrap(); - - lz4.write_all(&data2).await.unwrap(); - - log::debug!("....."); - - lz4.read_exact(&mut buf2).await.unwrap(); - }); - } - - #[test] - #[cfg(feature = "fuso-rt-tokio")] - fn test_lz4_server() { - init_logger(); - - tokio::runtime::Runtime::new() - .unwrap() - .block_on(async move { - let tcp = TcpListener::bind("127.0.0.1:6666").await.unwrap(); - - loop { - let (tcp, _) = tcp.accept().await.unwrap(); - let mut lz4 = Lz4Compress::new(tcp); - loop { - let mut buf = [0u8; 1500]; - match lz4.read_exact(&mut buf).await { - Ok(e) => {} - Err(_) => { - break; - } - } - if let Err(e) = lz4.write_all(&buf).await { - break; - } - } - } - }); - } -} diff --git a/src/core/compress/lz4/third_party/lib/LICENSE b/src/core/compress/lz4/third_party/lib/LICENSE deleted file mode 100644 index 4884916..0000000 --- a/src/core/compress/lz4/third_party/lib/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -LZ4 Library -Copyright (c) 2011-2020, Yann Collet -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 HOLDER 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. diff --git a/src/core/compress/lz4/third_party/lib/lz4.c b/src/core/compress/lz4/third_party/lib/lz4.c deleted file mode 100644 index 3f468d7..0000000 --- a/src/core/compress/lz4/third_party/lib/lz4.c +++ /dev/null @@ -1,2573 +0,0 @@ -/* - LZ4 - Fast LZ compression algorithm - Copyright (C) 2011-2020, Yann Collet. - - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - - 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. - - You can contact the author at : - - LZ4 homepage : http://www.lz4.org - - LZ4 source repository : https://github.com/lz4/lz4 -*/ - -/*-************************************ -* Tuning parameters -**************************************/ -/* - * LZ4_HEAPMODE : - * Select how default compression functions will allocate memory for their hash table, - * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()). - */ -#ifndef LZ4_HEAPMODE -# define LZ4_HEAPMODE 0 -#endif - -/* - * LZ4_ACCELERATION_DEFAULT : - * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0 - */ -#define LZ4_ACCELERATION_DEFAULT 1 -/* - * LZ4_ACCELERATION_MAX : - * Any "acceleration" value higher than this threshold - * get treated as LZ4_ACCELERATION_MAX instead (fix #876) - */ -#define LZ4_ACCELERATION_MAX 65537 - - -/*-************************************ -* CPU Feature Detection -**************************************/ -/* LZ4_FORCE_MEMORY_ACCESS - * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. - * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. - * The below switch allow to select different access method for improved performance. - * Method 0 (default) : use `memcpy()`. Safe and portable. - * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). - * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. - * Method 2 : direct access. This method is portable but violate C standard. - * It can generate buggy code on targets which assembly generation depends on alignment. - * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) - * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. - * Prefer these methods in priority order (0 > 1 > 2) - */ -#ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally */ -# if defined(__GNUC__) && \ - ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \ - || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) -# define LZ4_FORCE_MEMORY_ACCESS 2 -# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__) -# define LZ4_FORCE_MEMORY_ACCESS 1 -# endif -#endif - -/* - * LZ4_FORCE_SW_BITCOUNT - * Define this parameter if your target system or compiler does not support hardware bit count - */ -#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for WinCE doesn't support Hardware bit count */ -# undef LZ4_FORCE_SW_BITCOUNT /* avoid double def */ -# define LZ4_FORCE_SW_BITCOUNT -#endif - - - -/*-************************************ -* Dependency -**************************************/ -/* - * LZ4_SRC_INCLUDED: - * Amalgamation flag, whether lz4.c is included - */ -#ifndef LZ4_SRC_INCLUDED -# define LZ4_SRC_INCLUDED 1 -#endif - -#ifndef LZ4_STATIC_LINKING_ONLY -#define LZ4_STATIC_LINKING_ONLY -#endif - -#ifndef LZ4_DISABLE_DEPRECATE_WARNINGS -#define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */ -#endif - -#define LZ4_STATIC_LINKING_ONLY /* LZ4_DISTANCE_MAX */ -#include "lz4.h" -/* see also "memory routines" below */ - - -/*-************************************ -* Compiler Options -**************************************/ -#if defined(_MSC_VER) && (_MSC_VER >= 1400) /* Visual Studio 2005+ */ -# include /* only present in VS2005+ */ -# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -# pragma warning(disable : 6237) /* disable: C6237: conditional expression is always 0 */ -#endif /* _MSC_VER */ - -#ifndef LZ4_FORCE_INLINE -# ifdef _MSC_VER /* Visual Studio */ -# define LZ4_FORCE_INLINE static __forceinline -# else -# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ -# ifdef __GNUC__ -# define LZ4_FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define LZ4_FORCE_INLINE static inline -# endif -# else -# define LZ4_FORCE_INLINE static -# endif /* __STDC_VERSION__ */ -# endif /* _MSC_VER */ -#endif /* LZ4_FORCE_INLINE */ - -/* LZ4_FORCE_O2 and LZ4_FORCE_INLINE - * gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8, - * together with a simple 8-byte copy loop as a fall-back path. - * However, this optimization hurts the decompression speed by >30%, - * because the execution does not go to the optimized loop - * for typical compressible data, and all of the preamble checks - * before going to the fall-back path become useless overhead. - * This optimization happens only with the -O3 flag, and -O2 generates - * a simple 8-byte copy loop. - * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy8 - * functions are annotated with __attribute__((optimize("O2"))), - * and also LZ4_wildCopy8 is forcibly inlined, so that the O2 attribute - * of LZ4_wildCopy8 does not affect the compression speed. - */ -#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__) -# define LZ4_FORCE_O2 __attribute__((optimize("O2"))) -# undef LZ4_FORCE_INLINE -# define LZ4_FORCE_INLINE static __inline __attribute__((optimize("O2"),always_inline)) -#else -# define LZ4_FORCE_O2 -#endif - -#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__) -# define expect(expr,value) (__builtin_expect ((expr),(value)) ) -#else -# define expect(expr,value) (expr) -#endif - -#ifndef likely -#define likely(expr) expect((expr) != 0, 1) -#endif -#ifndef unlikely -#define unlikely(expr) expect((expr) != 0, 0) -#endif - -/* Should the alignment test prove unreliable, for some reason, - * it can be disabled by setting LZ4_ALIGN_TEST to 0 */ -#ifndef LZ4_ALIGN_TEST /* can be externally provided */ -# define LZ4_ALIGN_TEST 1 -#endif - - -/*-************************************ -* Memory routines -**************************************/ -#ifdef LZ4_USER_MEMORY_FUNCTIONS -/* memory management functions can be customized by user project. - * Below functions must exist somewhere in the Project - * and be available at link time */ -void* LZ4_malloc(size_t s); -void* LZ4_calloc(size_t n, size_t s); -void LZ4_free(void* p); -# define ALLOC(s) LZ4_malloc(s) -# define ALLOC_AND_ZERO(s) LZ4_calloc(1,s) -# define FREEMEM(p) LZ4_free(p) -#else -# include /* malloc, calloc, free */ -# define ALLOC(s) malloc(s) -# define ALLOC_AND_ZERO(s) calloc(1,s) -# define FREEMEM(p) free(p) -#endif - -#include /* memset, memcpy */ -#define MEM_INIT(p,v,s) memset((p),(v),(s)) - - -/*-************************************ -* Common Constants -**************************************/ -#define MINMATCH 4 - -#define WILDCOPYLENGTH 8 -#define LASTLITERALS 5 /* see ../doc/lz4_Block_format.md#parsing-restrictions */ -#define MFLIMIT 12 /* see ../doc/lz4_Block_format.md#parsing-restrictions */ -#define MATCH_SAFEGUARD_DISTANCE ((2*WILDCOPYLENGTH) - MINMATCH) /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */ -#define FASTLOOP_SAFE_DISTANCE 64 -static const int LZ4_minLength = (MFLIMIT+1); - -#define KB *(1 <<10) -#define MB *(1 <<20) -#define GB *(1U<<30) - -#define LZ4_DISTANCE_ABSOLUTE_MAX 65535 -#if (LZ4_DISTANCE_MAX > LZ4_DISTANCE_ABSOLUTE_MAX) /* max supported by LZ4 format */ -# error "LZ4_DISTANCE_MAX is too big : must be <= 65535" -#endif - -#define ML_BITS 4 -#define ML_MASK ((1U<=1) -# include -#else -# ifndef assert -# define assert(condition) ((void)0) -# endif -#endif - -#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use after variable declarations */ - -#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2) -# include - static int g_debuglog_enable = 1; -# define DEBUGLOG(l, ...) { \ - if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \ - fprintf(stderr, __FILE__ ": "); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, " \n"); \ - } } -#else -# define DEBUGLOG(l, ...) {} /* disabled */ -#endif - -static int LZ4_isAligned(const void* ptr, size_t alignment) -{ - return ((size_t)ptr & (alignment -1)) == 0; -} - - -/*-************************************ -* Types -**************************************/ -#include -#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) -# include - typedef uint8_t BYTE; - typedef uint16_t U16; - typedef uint32_t U32; - typedef int32_t S32; - typedef uint64_t U64; - typedef uintptr_t uptrval; -#else -# if UINT_MAX != 4294967295UL -# error "LZ4 code (when not C++ or C99) assumes that sizeof(int) == 4" -# endif - typedef unsigned char BYTE; - typedef unsigned short U16; - typedef unsigned int U32; - typedef signed int S32; - typedef unsigned long long U64; - typedef size_t uptrval; /* generally true, except OpenVMS-64 */ -#endif - -#if defined(__x86_64__) - typedef U64 reg_t; /* 64-bits in x32 mode */ -#else - typedef size_t reg_t; /* 32-bits in x32 mode */ -#endif - -typedef enum { - notLimited = 0, - limitedOutput = 1, - fillOutput = 2 -} limitedOutput_directive; - - -/*-************************************ -* Reading and writing into memory -**************************************/ - -/** - * LZ4 relies on memcpy with a constant size being inlined. In freestanding - * environments, the compiler can't assume the implementation of memcpy() is - * standard compliant, so it can't apply its specialized memcpy() inlining - * logic. When possible, use __builtin_memcpy() to tell the compiler to analyze - * memcpy() as if it were standard compliant, so it can inline it in freestanding - * environments. This is needed when decompressing the Linux Kernel, for example. - */ -#if defined(__GNUC__) && (__GNUC__ >= 4) -#define LZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size) -#else -#define LZ4_memcpy(dst, src, size) memcpy(dst, src, size) -#endif - -static unsigned LZ4_isLittleEndian(void) -{ - const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ - return one.c[0]; -} - - -#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2) -/* lie to the compiler about data alignment; use with caution */ - -static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; } -static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; } -static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; } - -static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } -static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; } - -#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1) - -/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ -/* currently only defined for gcc and icc */ -typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) LZ4_unalign; - -static U16 LZ4_read16(const void* ptr) { return ((const LZ4_unalign*)ptr)->u16; } -static U32 LZ4_read32(const void* ptr) { return ((const LZ4_unalign*)ptr)->u32; } -static reg_t LZ4_read_ARCH(const void* ptr) { return ((const LZ4_unalign*)ptr)->uArch; } - -static void LZ4_write16(void* memPtr, U16 value) { ((LZ4_unalign*)memPtr)->u16 = value; } -static void LZ4_write32(void* memPtr, U32 value) { ((LZ4_unalign*)memPtr)->u32 = value; } - -#else /* safe and portable access using memcpy() */ - -static U16 LZ4_read16(const void* memPtr) -{ - U16 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; -} - -static U32 LZ4_read32(const void* memPtr) -{ - U32 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; -} - -static reg_t LZ4_read_ARCH(const void* memPtr) -{ - reg_t val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; -} - -static void LZ4_write16(void* memPtr, U16 value) -{ - LZ4_memcpy(memPtr, &value, sizeof(value)); -} - -static void LZ4_write32(void* memPtr, U32 value) -{ - LZ4_memcpy(memPtr, &value, sizeof(value)); -} - -#endif /* LZ4_FORCE_MEMORY_ACCESS */ - - -static U16 LZ4_readLE16(const void* memPtr) -{ - if (LZ4_isLittleEndian()) { - return LZ4_read16(memPtr); - } else { - const BYTE* p = (const BYTE*)memPtr; - return (U16)((U16)p[0] + (p[1]<<8)); - } -} - -static void LZ4_writeLE16(void* memPtr, U16 value) -{ - if (LZ4_isLittleEndian()) { - LZ4_write16(memPtr, value); - } else { - BYTE* p = (BYTE*)memPtr; - p[0] = (BYTE) value; - p[1] = (BYTE)(value>>8); - } -} - -/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */ -LZ4_FORCE_INLINE -void LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd) -{ - BYTE* d = (BYTE*)dstPtr; - const BYTE* s = (const BYTE*)srcPtr; - BYTE* const e = (BYTE*)dstEnd; - - do { LZ4_memcpy(d,s,8); d+=8; s+=8; } while (d= 16. */ -LZ4_FORCE_INLINE void -LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd) -{ - BYTE* d = (BYTE*)dstPtr; - const BYTE* s = (const BYTE*)srcPtr; - BYTE* const e = (BYTE*)dstEnd; - - do { LZ4_memcpy(d,s,16); LZ4_memcpy(d+16,s+16,16); d+=32; s+=32; } while (d= dstPtr + MINMATCH - * - there is at least 8 bytes available to write after dstEnd */ -LZ4_FORCE_INLINE void -LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset) -{ - BYTE v[8]; - - assert(dstEnd >= dstPtr + MINMATCH); - - switch(offset) { - case 1: - MEM_INIT(v, *srcPtr, 8); - break; - case 2: - LZ4_memcpy(v, srcPtr, 2); - LZ4_memcpy(&v[2], srcPtr, 2); - LZ4_memcpy(&v[4], v, 4); - break; - case 4: - LZ4_memcpy(v, srcPtr, 4); - LZ4_memcpy(&v[4], srcPtr, 4); - break; - default: - LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset); - return; - } - - LZ4_memcpy(dstPtr, v, 8); - dstPtr += 8; - while (dstPtr < dstEnd) { - LZ4_memcpy(dstPtr, v, 8); - dstPtr += 8; - } -} -#endif - - -/*-************************************ -* Common functions -**************************************/ -static unsigned LZ4_NbCommonBytes (reg_t val) -{ - assert(val != 0); - if (LZ4_isLittleEndian()) { - if (sizeof(val) == 8) { -# if defined(_MSC_VER) && (_MSC_VER >= 1800) && (defined(_M_AMD64) && !defined(_M_ARM64EC)) && !defined(LZ4_FORCE_SW_BITCOUNT) -/*-************************************************************************************************* -* ARM64EC is a Microsoft-designed ARM64 ABI compatible with AMD64 applications on ARM64 Windows 11. -* The ARM64EC ABI does not support AVX/AVX2/AVX512 instructions, nor their relevant intrinsics -* including _tzcnt_u64. Therefore, we need to neuter the _tzcnt_u64 code path for ARM64EC. -****************************************************************************************************/ -# if defined(__clang__) && (__clang_major__ < 10) - /* Avoid undefined clang-cl intrinics issue. - * See https://github.com/lz4/lz4/pull/1017 for details. */ - return (unsigned)__builtin_ia32_tzcnt_u64(val) >> 3; -# else - /* x64 CPUS without BMI support interpret `TZCNT` as `REP BSF` */ - return (unsigned)_tzcnt_u64(val) >> 3; -# endif -# elif defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) - unsigned long r = 0; - _BitScanForward64(&r, (U64)val); - return (unsigned)r >> 3; -# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ - ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ - !defined(LZ4_FORCE_SW_BITCOUNT) - return (unsigned)__builtin_ctzll((U64)val) >> 3; -# else - const U64 m = 0x0101010101010101ULL; - val ^= val - 1; - return (unsigned)(((U64)((val & (m - 1)) * m)) >> 56); -# endif - } else /* 32 bits */ { -# if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT) - unsigned long r; - _BitScanForward(&r, (U32)val); - return (unsigned)r >> 3; -# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ - ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ - !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT) - return (unsigned)__builtin_ctz((U32)val) >> 3; -# else - const U32 m = 0x01010101; - return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24; -# endif - } - } else /* Big Endian CPU */ { - if (sizeof(val)==8) { -# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ - ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ - !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT) - return (unsigned)__builtin_clzll((U64)val) >> 3; -# else -#if 1 - /* this method is probably faster, - * but adds a 128 bytes lookup table */ - static const unsigned char ctz7_tab[128] = { - 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - }; - U64 const mask = 0x0101010101010101ULL; - U64 const t = (((val >> 8) - mask) | val) & mask; - return ctz7_tab[(t * 0x0080402010080402ULL) >> 57]; -#else - /* this method doesn't consume memory space like the previous one, - * but it contains several branches, - * that may end up slowing execution */ - static const U32 by32 = sizeof(val)*4; /* 32 on 64 bits (goal), 16 on 32 bits. - Just to avoid some static analyzer complaining about shift by 32 on 32-bits target. - Note that this code path is never triggered in 32-bits mode. */ - unsigned r; - if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; } - if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } - r += (!val); - return r; -#endif -# endif - } else /* 32 bits */ { -# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ - ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ - !defined(LZ4_FORCE_SW_BITCOUNT) - return (unsigned)__builtin_clz((U32)val) >> 3; -# else - val >>= 8; - val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) | - (val + 0x00FF0000)) >> 24; - return (unsigned)val ^ 3; -# endif - } - } -} - - -#define STEPSIZE sizeof(reg_t) -LZ4_FORCE_INLINE -unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit) -{ - const BYTE* const pStart = pIn; - - if (likely(pIn < pInLimit-(STEPSIZE-1))) { - reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); - if (!diff) { - pIn+=STEPSIZE; pMatch+=STEPSIZE; - } else { - return LZ4_NbCommonBytes(diff); - } } - - while (likely(pIn < pInLimit-(STEPSIZE-1))) { - reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); - if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; } - pIn += LZ4_NbCommonBytes(diff); - return (unsigned)(pIn - pStart); - } - - if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; } - if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; } - if ((pIn compression run slower on incompressible data */ - - -/*-************************************ -* Local Structures and types -**************************************/ -typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t; - -/** - * This enum distinguishes several different modes of accessing previous - * content in the stream. - * - * - noDict : There is no preceding content. - * - withPrefix64k : Table entries up to ctx->dictSize before the current blob - * blob being compressed are valid and refer to the preceding - * content (of length ctx->dictSize), which is available - * contiguously preceding in memory the content currently - * being compressed. - * - usingExtDict : Like withPrefix64k, but the preceding content is somewhere - * else in memory, starting at ctx->dictionary with length - * ctx->dictSize. - * - usingDictCtx : Everything concerning the preceding content is - * in a separate context, pointed to by ctx->dictCtx. - * ctx->dictionary, ctx->dictSize, and table entries - * in the current context that refer to positions - * preceding the beginning of the current compression are - * ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx - * ->dictSize describe the location and size of the preceding - * content, and matches are found by looking in the ctx - * ->dictCtx->hashTable. - */ -typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive; -typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive; - - -/*-************************************ -* Local Utils -**************************************/ -int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; } -const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; } -int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } -int LZ4_sizeofState(void) { return sizeof(LZ4_stream_t); } - - -/*-**************************************** -* Internal Definitions, used only in Tests -*******************************************/ -#if defined (__cplusplus) -extern "C" { -#endif - -int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize); - -int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, - int compressedSize, int maxOutputSize, - const void* dictStart, size_t dictSize); -int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest, - int compressedSize, int targetOutputSize, int dstCapacity, - const void* dictStart, size_t dictSize); -#if defined (__cplusplus) -} -#endif - -/*-****************************** -* Compression functions -********************************/ -LZ4_FORCE_INLINE U32 LZ4_hash4(U32 sequence, tableType_t const tableType) -{ - if (tableType == byU16) - return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1))); - else - return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG)); -} - -LZ4_FORCE_INLINE U32 LZ4_hash5(U64 sequence, tableType_t const tableType) -{ - const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG; - if (LZ4_isLittleEndian()) { - const U64 prime5bytes = 889523592379ULL; - return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); - } else { - const U64 prime8bytes = 11400714785074694791ULL; - return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); - } -} - -LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType) -{ - if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType); - return LZ4_hash4(LZ4_read32(p), tableType); -} - -LZ4_FORCE_INLINE void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType) -{ - switch (tableType) - { - default: /* fallthrough */ - case clearedTable: { /* illegal! */ assert(0); return; } - case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; } - case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; } - case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; } - } -} - -LZ4_FORCE_INLINE void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType) -{ - switch (tableType) - { - default: /* fallthrough */ - case clearedTable: /* fallthrough */ - case byPtr: { /* illegal! */ assert(0); return; } - case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; } - case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; } - } -} - -LZ4_FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h, - void* tableBase, tableType_t const tableType, - const BYTE* srcBase) -{ - switch (tableType) - { - case clearedTable: { /* illegal! */ assert(0); return; } - case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; } - case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; } - case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; } - } -} - -LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) -{ - U32 const h = LZ4_hashPosition(p, tableType); - LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase); -} - -/* LZ4_getIndexOnHash() : - * Index of match position registered in hash table. - * hash position must be calculated by using base+index, or dictBase+index. - * Assumption 1 : only valid if tableType == byU32 or byU16. - * Assumption 2 : h is presumed valid (within limits of hash table) - */ -LZ4_FORCE_INLINE U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType) -{ - LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2); - if (tableType == byU32) { - const U32* const hashTable = (const U32*) tableBase; - assert(h < (1U << (LZ4_MEMORY_USAGE-2))); - return hashTable[h]; - } - if (tableType == byU16) { - const U16* const hashTable = (const U16*) tableBase; - assert(h < (1U << (LZ4_MEMORY_USAGE-1))); - return hashTable[h]; - } - assert(0); return 0; /* forbidden case */ -} - -static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType, const BYTE* srcBase) -{ - if (tableType == byPtr) { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; } - if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h] + srcBase; } - { const U16* const hashTable = (const U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */ -} - -LZ4_FORCE_INLINE const BYTE* -LZ4_getPosition(const BYTE* p, - const void* tableBase, tableType_t tableType, - const BYTE* srcBase) -{ - U32 const h = LZ4_hashPosition(p, tableType); - return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase); -} - -LZ4_FORCE_INLINE void -LZ4_prepareTable(LZ4_stream_t_internal* const cctx, - const int inputSize, - const tableType_t tableType) { - /* If the table hasn't been used, it's guaranteed to be zeroed out, and is - * therefore safe to use no matter what mode we're in. Otherwise, we figure - * out if it's safe to leave as is or whether it needs to be reset. - */ - if ((tableType_t)cctx->tableType != clearedTable) { - assert(inputSize >= 0); - if ((tableType_t)cctx->tableType != tableType - || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU) - || ((tableType == byU32) && cctx->currentOffset > 1 GB) - || tableType == byPtr - || inputSize >= 4 KB) - { - DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx); - MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE); - cctx->currentOffset = 0; - cctx->tableType = (U32)clearedTable; - } else { - DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)"); - } - } - - /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back, - * is faster than compressing without a gap. - * However, compressing with currentOffset == 0 is faster still, - * so we preserve that case. - */ - if (cctx->currentOffset != 0 && tableType == byU32) { - DEBUGLOG(5, "LZ4_prepareTable: adding 64KB to currentOffset"); - cctx->currentOffset += 64 KB; - } - - /* Finally, clear history */ - cctx->dictCtx = NULL; - cctx->dictionary = NULL; - cctx->dictSize = 0; -} - -/** LZ4_compress_generic() : - * inlined, to ensure branches are decided at compilation time. - * Presumed already validated at this stage: - * - source != NULL - * - inputSize > 0 - */ -LZ4_FORCE_INLINE int LZ4_compress_generic_validated( - LZ4_stream_t_internal* const cctx, - const char* const source, - char* const dest, - const int inputSize, - int* inputConsumed, /* only written when outputDirective == fillOutput */ - const int maxOutputSize, - const limitedOutput_directive outputDirective, - const tableType_t tableType, - const dict_directive dictDirective, - const dictIssue_directive dictIssue, - const int acceleration) -{ - int result; - const BYTE* ip = (const BYTE*) source; - - U32 const startIndex = cctx->currentOffset; - const BYTE* base = (const BYTE*) source - startIndex; - const BYTE* lowLimit; - - const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx; - const BYTE* const dictionary = - dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary; - const U32 dictSize = - dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize; - const U32 dictDelta = (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with index in current context */ - - int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx); - U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */ - const BYTE* const dictEnd = dictionary ? dictionary + dictSize : dictionary; - const BYTE* anchor = (const BYTE*) source; - const BYTE* const iend = ip + inputSize; - const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1; - const BYTE* const matchlimit = iend - LASTLITERALS; - - /* the dictCtx currentOffset is indexed on the start of the dictionary, - * while a dictionary in the current context precedes the currentOffset */ - const BYTE* dictBase = (dictionary == NULL) ? NULL : - (dictDirective == usingDictCtx) ? - dictionary + dictSize - dictCtx->currentOffset : - dictionary + dictSize - startIndex; - - BYTE* op = (BYTE*) dest; - BYTE* const olimit = op + maxOutputSize; - - U32 offset = 0; - U32 forwardH; - - DEBUGLOG(5, "LZ4_compress_generic_validated: srcSize=%i, tableType=%u", inputSize, tableType); - assert(ip != NULL); - /* If init conditions are not met, we don't have to mark stream - * as having dirty context, since no action was taken yet */ - if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */ - if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) { return 0; } /* Size too large (not within 64K limit) */ - if (tableType==byPtr) assert(dictDirective==noDict); /* only supported use case with byPtr */ - assert(acceleration >= 1); - - lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0); - - /* Update context state */ - if (dictDirective == usingDictCtx) { - /* Subsequent linked blocks can't use the dictionary. */ - /* Instead, they use the block we just compressed. */ - cctx->dictCtx = NULL; - cctx->dictSize = (U32)inputSize; - } else { - cctx->dictSize += (U32)inputSize; - } - cctx->currentOffset += (U32)inputSize; - cctx->tableType = (U32)tableType; - - if (inputSizehashTable, tableType, base); - ip++; forwardH = LZ4_hashPosition(ip, tableType); - - /* Main Loop */ - for ( ; ; ) { - const BYTE* match; - BYTE* token; - const BYTE* filledIp; - - /* Find a match */ - if (tableType == byPtr) { - const BYTE* forwardIp = ip; - int step = 1; - int searchMatchNb = acceleration << LZ4_skipTrigger; - do { - U32 const h = forwardH; - ip = forwardIp; - forwardIp += step; - step = (searchMatchNb++ >> LZ4_skipTrigger); - - if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; - assert(ip < mflimitPlusOne); - - match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base); - forwardH = LZ4_hashPosition(forwardIp, tableType); - LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base); - - } while ( (match+LZ4_DISTANCE_MAX < ip) - || (LZ4_read32(match) != LZ4_read32(ip)) ); - - } else { /* byU32, byU16 */ - - const BYTE* forwardIp = ip; - int step = 1; - int searchMatchNb = acceleration << LZ4_skipTrigger; - do { - U32 const h = forwardH; - U32 const current = (U32)(forwardIp - base); - U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); - assert(matchIndex <= current); - assert(forwardIp - base < (ptrdiff_t)(2 GB - 1)); - ip = forwardIp; - forwardIp += step; - step = (searchMatchNb++ >> LZ4_skipTrigger); - - if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; - assert(ip < mflimitPlusOne); - - if (dictDirective == usingDictCtx) { - if (matchIndex < startIndex) { - /* there was no match, try the dictionary */ - assert(tableType == byU32); - matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); - match = dictBase + matchIndex; - matchIndex += dictDelta; /* make dictCtx index comparable with current context */ - lowLimit = dictionary; - } else { - match = base + matchIndex; - lowLimit = (const BYTE*)source; - } - } else if (dictDirective == usingExtDict) { - if (matchIndex < startIndex) { - DEBUGLOG(7, "extDict candidate: matchIndex=%5u < startIndex=%5u", matchIndex, startIndex); - assert(startIndex - matchIndex >= MINMATCH); - assert(dictBase); - match = dictBase + matchIndex; - lowLimit = dictionary; - } else { - match = base + matchIndex; - lowLimit = (const BYTE*)source; - } - } else { /* single continuous memory segment */ - match = base + matchIndex; - } - forwardH = LZ4_hashPosition(forwardIp, tableType); - LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); - - DEBUGLOG(7, "candidate at pos=%u (offset=%u \n", matchIndex, current - matchIndex); - if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; } /* match outside of valid area */ - assert(matchIndex < current); - if ( ((tableType != byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX)) - && (matchIndex+LZ4_DISTANCE_MAX < current)) { - continue; - } /* too far */ - assert((current - matchIndex) <= LZ4_DISTANCE_MAX); /* match now expected within distance */ - - if (LZ4_read32(match) == LZ4_read32(ip)) { - if (maybe_extMem) offset = current - matchIndex; - break; /* match found */ - } - - } while(1); - } - - /* Catch up */ - filledIp = ip; - while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; } - - /* Encode Literals */ - { unsigned const litLength = (unsigned)(ip - anchor); - token = op++; - if ((outputDirective == limitedOutput) && /* Check output buffer overflow */ - (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) { - return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ - } - if ((outputDirective == fillOutput) && - (unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) { - op--; - goto _last_literals; - } - if (litLength >= RUN_MASK) { - int len = (int)(litLength - RUN_MASK); - *token = (RUN_MASK<= 255 ; len-=255) *op++ = 255; - *op++ = (BYTE)len; - } - else *token = (BYTE)(litLength< olimit)) { - /* the match was too close to the end, rewind and go to last literals */ - op = token; - goto _last_literals; - } - - /* Encode Offset */ - if (maybe_extMem) { /* static test */ - DEBUGLOG(6, " with offset=%u (ext if > %i)", offset, (int)(ip - (const BYTE*)source)); - assert(offset <= LZ4_DISTANCE_MAX && offset > 0); - LZ4_writeLE16(op, (U16)offset); op+=2; - } else { - DEBUGLOG(6, " with offset=%u (same segment)", (U32)(ip - match)); - assert(ip-match <= LZ4_DISTANCE_MAX); - LZ4_writeLE16(op, (U16)(ip - match)); op+=2; - } - - /* Encode MatchLength */ - { unsigned matchCode; - - if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx) - && (lowLimit==dictionary) /* match within extDict */ ) { - const BYTE* limit = ip + (dictEnd-match); - assert(dictEnd > match); - if (limit > matchlimit) limit = matchlimit; - matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit); - ip += (size_t)matchCode + MINMATCH; - if (ip==limit) { - unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit); - matchCode += more; - ip += more; - } - DEBUGLOG(6, " with matchLength=%u starting in extDict", matchCode+MINMATCH); - } else { - matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit); - ip += (size_t)matchCode + MINMATCH; - DEBUGLOG(6, " with matchLength=%u", matchCode+MINMATCH); - } - - if ((outputDirective) && /* Check output buffer overflow */ - (unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) { - if (outputDirective == fillOutput) { - /* Match description too long : reduce it */ - U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255; - ip -= matchCode - newMatchCode; - assert(newMatchCode < matchCode); - matchCode = newMatchCode; - if (unlikely(ip <= filledIp)) { - /* We have already filled up to filledIp so if ip ends up less than filledIp - * we have positions in the hash table beyond the current position. This is - * a problem if we reuse the hash table. So we have to remove these positions - * from the hash table. - */ - const BYTE* ptr; - DEBUGLOG(5, "Clearing %u positions", (U32)(filledIp - ip)); - for (ptr = ip; ptr <= filledIp; ++ptr) { - U32 const h = LZ4_hashPosition(ptr, tableType); - LZ4_clearHash(h, cctx->hashTable, tableType); - } - } - } else { - assert(outputDirective == limitedOutput); - return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ - } - } - if (matchCode >= ML_MASK) { - *token += ML_MASK; - matchCode -= ML_MASK; - LZ4_write32(op, 0xFFFFFFFF); - while (matchCode >= 4*255) { - op+=4; - LZ4_write32(op, 0xFFFFFFFF); - matchCode -= 4*255; - } - op += matchCode / 255; - *op++ = (BYTE)(matchCode % 255); - } else - *token += (BYTE)(matchCode); - } - /* Ensure we have enough space for the last literals. */ - assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit)); - - anchor = ip; - - /* Test end of chunk */ - if (ip >= mflimitPlusOne) break; - - /* Fill table */ - LZ4_putPosition(ip-2, cctx->hashTable, tableType, base); - - /* Test next position */ - if (tableType == byPtr) { - - match = LZ4_getPosition(ip, cctx->hashTable, tableType, base); - LZ4_putPosition(ip, cctx->hashTable, tableType, base); - if ( (match+LZ4_DISTANCE_MAX >= ip) - && (LZ4_read32(match) == LZ4_read32(ip)) ) - { token=op++; *token=0; goto _next_match; } - - } else { /* byU32, byU16 */ - - U32 const h = LZ4_hashPosition(ip, tableType); - U32 const current = (U32)(ip-base); - U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); - assert(matchIndex < current); - if (dictDirective == usingDictCtx) { - if (matchIndex < startIndex) { - /* there was no match, try the dictionary */ - matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); - match = dictBase + matchIndex; - lowLimit = dictionary; /* required for match length counter */ - matchIndex += dictDelta; - } else { - match = base + matchIndex; - lowLimit = (const BYTE*)source; /* required for match length counter */ - } - } else if (dictDirective==usingExtDict) { - if (matchIndex < startIndex) { - assert(dictBase); - match = dictBase + matchIndex; - lowLimit = dictionary; /* required for match length counter */ - } else { - match = base + matchIndex; - lowLimit = (const BYTE*)source; /* required for match length counter */ - } - } else { /* single memory segment */ - match = base + matchIndex; - } - LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); - assert(matchIndex < current); - if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1) - && (((tableType==byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+LZ4_DISTANCE_MAX >= current)) - && (LZ4_read32(match) == LZ4_read32(ip)) ) { - token=op++; - *token=0; - if (maybe_extMem) offset = current - matchIndex; - DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i", - (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source)); - goto _next_match; - } - } - - /* Prepare next loop */ - forwardH = LZ4_hashPosition(++ip, tableType); - - } - -_last_literals: - /* Encode Last Literals */ - { size_t lastRun = (size_t)(iend - anchor); - if ( (outputDirective) && /* Check output buffer overflow */ - (op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) { - if (outputDirective == fillOutput) { - /* adapt lastRun to fill 'dst' */ - assert(olimit >= op); - lastRun = (size_t)(olimit-op) - 1/*token*/; - lastRun -= (lastRun + 256 - RUN_MASK) / 256; /*additional length tokens*/ - } else { - assert(outputDirective == limitedOutput); - return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ - } - } - DEBUGLOG(6, "Final literal run : %i literals", (int)lastRun); - if (lastRun >= RUN_MASK) { - size_t accumulator = lastRun - RUN_MASK; - *op++ = RUN_MASK << ML_BITS; - for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; - *op++ = (BYTE) accumulator; - } else { - *op++ = (BYTE)(lastRun< 0); - DEBUGLOG(5, "LZ4_compress_generic: compressed %i bytes into %i bytes", inputSize, result); - return result; -} - -/** LZ4_compress_generic() : - * inlined, to ensure branches are decided at compilation time; - * takes care of src == (NULL, 0) - * and forward the rest to LZ4_compress_generic_validated */ -LZ4_FORCE_INLINE int LZ4_compress_generic( - LZ4_stream_t_internal* const cctx, - const char* const src, - char* const dst, - const int srcSize, - int *inputConsumed, /* only written when outputDirective == fillOutput */ - const int dstCapacity, - const limitedOutput_directive outputDirective, - const tableType_t tableType, - const dict_directive dictDirective, - const dictIssue_directive dictIssue, - const int acceleration) -{ - DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, dstCapacity=%i", - srcSize, dstCapacity); - - if ((U32)srcSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; } /* Unsupported srcSize, too large (or negative) */ - if (srcSize == 0) { /* src == NULL supported if srcSize == 0 */ - if (outputDirective != notLimited && dstCapacity <= 0) return 0; /* no output, can't write anything */ - DEBUGLOG(5, "Generating an empty block"); - assert(outputDirective == notLimited || dstCapacity >= 1); - assert(dst != NULL); - dst[0] = 0; - if (outputDirective == fillOutput) { - assert (inputConsumed != NULL); - *inputConsumed = 0; - } - return 1; - } - assert(src != NULL); - - return LZ4_compress_generic_validated(cctx, src, dst, srcSize, - inputConsumed, /* only written into if outputDirective == fillOutput */ - dstCapacity, outputDirective, - tableType, dictDirective, dictIssue, acceleration); -} - - -int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) -{ - LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse; - assert(ctx != NULL); - if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; - if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; - if (maxOutputSize >= LZ4_compressBound(inputSize)) { - if (inputSize < LZ4_64Klimit) { - return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration); - } else { - const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32; - return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); - } - } else { - if (inputSize < LZ4_64Klimit) { - return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); - } else { - const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32; - return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration); - } - } -} - -/** - * LZ4_compress_fast_extState_fastReset() : - * A variant of LZ4_compress_fast_extState(). - * - * Using this variant avoids an expensive initialization step. It is only safe - * to call if the state buffer is known to be correctly initialized already - * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of - * "correctly initialized"). - */ -int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration) -{ - LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse; - if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; - if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; - - if (dstCapacity >= LZ4_compressBound(srcSize)) { - if (srcSize < LZ4_64Klimit) { - const tableType_t tableType = byU16; - LZ4_prepareTable(ctx, srcSize, tableType); - if (ctx->currentOffset) { - return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, dictSmall, acceleration); - } else { - return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); - } - } else { - const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; - LZ4_prepareTable(ctx, srcSize, tableType); - return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); - } - } else { - if (srcSize < LZ4_64Klimit) { - const tableType_t tableType = byU16; - LZ4_prepareTable(ctx, srcSize, tableType); - if (ctx->currentOffset) { - return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration); - } else { - return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration); - } - } else { - const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; - LZ4_prepareTable(ctx, srcSize, tableType); - return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration); - } - } -} - - -int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) -{ - int result; -#if (LZ4_HEAPMODE) - LZ4_stream_t* ctxPtr = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ - if (ctxPtr == NULL) return 0; -#else - LZ4_stream_t ctx; - LZ4_stream_t* const ctxPtr = &ctx; -#endif - result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration); - -#if (LZ4_HEAPMODE) - FREEMEM(ctxPtr); -#endif - return result; -} - - -int LZ4_compress_default(const char* src, char* dst, int srcSize, int maxOutputSize) -{ - return LZ4_compress_fast(src, dst, srcSize, maxOutputSize, 1); -} - - -/* Note!: This function leaves the stream in an unclean/broken state! - * It is not safe to subsequently use the same state with a _fastReset() or - * _continue() call without resetting it. */ -static int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize) -{ - void* const s = LZ4_initStream(state, sizeof (*state)); - assert(s != NULL); (void)s; - - if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */ - return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1); - } else { - if (*srcSizePtr < LZ4_64Klimit) { - return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, 1); - } else { - tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; - return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, 1); - } } -} - - -int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize) -{ -#if (LZ4_HEAPMODE) - LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ - if (ctx == NULL) return 0; -#else - LZ4_stream_t ctxBody; - LZ4_stream_t* ctx = &ctxBody; -#endif - - int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize); - -#if (LZ4_HEAPMODE) - FREEMEM(ctx); -#endif - return result; -} - - - -/*-****************************** -* Streaming functions -********************************/ - -LZ4_stream_t* LZ4_createStream(void) -{ - LZ4_stream_t* const lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); - LZ4_STATIC_ASSERT(sizeof(LZ4_stream_t) >= sizeof(LZ4_stream_t_internal)); - DEBUGLOG(4, "LZ4_createStream %p", lz4s); - if (lz4s == NULL) return NULL; - LZ4_initStream(lz4s, sizeof(*lz4s)); - return lz4s; -} - -static size_t LZ4_stream_t_alignment(void) -{ -#if LZ4_ALIGN_TEST - typedef struct { char c; LZ4_stream_t t; } t_a; - return sizeof(t_a) - sizeof(LZ4_stream_t); -#else - return 1; /* effectively disabled */ -#endif -} - -LZ4_stream_t* LZ4_initStream (void* buffer, size_t size) -{ - DEBUGLOG(5, "LZ4_initStream"); - if (buffer == NULL) { return NULL; } - if (size < sizeof(LZ4_stream_t)) { return NULL; } - if (!LZ4_isAligned(buffer, LZ4_stream_t_alignment())) return NULL; - MEM_INIT(buffer, 0, sizeof(LZ4_stream_t_internal)); - return (LZ4_stream_t*)buffer; -} - -/* resetStream is now deprecated, - * prefer initStream() which is more general */ -void LZ4_resetStream (LZ4_stream_t* LZ4_stream) -{ - DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream); - MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t_internal)); -} - -void LZ4_resetStream_fast(LZ4_stream_t* ctx) { - LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32); -} - -int LZ4_freeStream (LZ4_stream_t* LZ4_stream) -{ - if (!LZ4_stream) return 0; /* support free on NULL */ - DEBUGLOG(5, "LZ4_freeStream %p", LZ4_stream); - FREEMEM(LZ4_stream); - return (0); -} - - -#define HASH_UNIT sizeof(reg_t) -int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) -{ - LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse; - const tableType_t tableType = byU32; - const BYTE* p = (const BYTE*)dictionary; - const BYTE* const dictEnd = p + dictSize; - const BYTE* base; - - DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict); - - /* It's necessary to reset the context, - * and not just continue it with prepareTable() - * to avoid any risk of generating overflowing matchIndex - * when compressing using this dictionary */ - LZ4_resetStream(LZ4_dict); - - /* We always increment the offset by 64 KB, since, if the dict is longer, - * we truncate it to the last 64k, and if it's shorter, we still want to - * advance by a whole window length so we can provide the guarantee that - * there are only valid offsets in the window, which allows an optimization - * in LZ4_compress_fast_continue() where it uses noDictIssue even when the - * dictionary isn't a full 64k. */ - dict->currentOffset += 64 KB; - - if (dictSize < (int)HASH_UNIT) { - return 0; - } - - if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB; - base = dictEnd - dict->currentOffset; - dict->dictionary = p; - dict->dictSize = (U32)(dictEnd - p); - dict->tableType = (U32)tableType; - - while (p <= dictEnd-HASH_UNIT) { - LZ4_putPosition(p, dict->hashTable, tableType, base); - p+=3; - } - - return (int)dict->dictSize; -} - -void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream) -{ - const LZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL : - &(dictionaryStream->internal_donotuse); - - DEBUGLOG(4, "LZ4_attach_dictionary (%p, %p, size %u)", - workingStream, dictionaryStream, - dictCtx != NULL ? dictCtx->dictSize : 0); - - if (dictCtx != NULL) { - /* If the current offset is zero, we will never look in the - * external dictionary context, since there is no value a table - * entry can take that indicate a miss. In that case, we need - * to bump the offset to something non-zero. - */ - if (workingStream->internal_donotuse.currentOffset == 0) { - workingStream->internal_donotuse.currentOffset = 64 KB; - } - - /* Don't actually attach an empty dictionary. - */ - if (dictCtx->dictSize == 0) { - dictCtx = NULL; - } - } - workingStream->internal_donotuse.dictCtx = dictCtx; -} - - -static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize) -{ - assert(nextSize >= 0); - if (LZ4_dict->currentOffset + (unsigned)nextSize > 0x80000000) { /* potential ptrdiff_t overflow (32-bits mode) */ - /* rescale hash table */ - U32 const delta = LZ4_dict->currentOffset - 64 KB; - const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize; - int i; - DEBUGLOG(4, "LZ4_renormDictT"); - for (i=0; ihashTable[i] < delta) LZ4_dict->hashTable[i]=0; - else LZ4_dict->hashTable[i] -= delta; - } - LZ4_dict->currentOffset = 64 KB; - if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB; - LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize; - } -} - - -int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, - const char* source, char* dest, - int inputSize, int maxOutputSize, - int acceleration) -{ - const tableType_t tableType = byU32; - LZ4_stream_t_internal* const streamPtr = &LZ4_stream->internal_donotuse; - const char* dictEnd = streamPtr->dictSize ? (const char*)streamPtr->dictionary + streamPtr->dictSize : NULL; - - DEBUGLOG(5, "LZ4_compress_fast_continue (inputSize=%i, dictSize=%u)", inputSize, streamPtr->dictSize); - - LZ4_renormDictT(streamPtr, inputSize); /* fix index overflow */ - if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; - if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; - - /* invalidate tiny dictionaries */ - if ( (streamPtr->dictSize < 4) /* tiny dictionary : not enough for a hash */ - && (dictEnd != source) /* prefix mode */ - && (inputSize > 0) /* tolerance : don't lose history, in case next invocation would use prefix mode */ - && (streamPtr->dictCtx == NULL) /* usingDictCtx */ - ) { - DEBUGLOG(5, "LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small", streamPtr->dictSize, streamPtr->dictionary); - /* remove dictionary existence from history, to employ faster prefix mode */ - streamPtr->dictSize = 0; - streamPtr->dictionary = (const BYTE*)source; - dictEnd = source; - } - - /* Check overlapping input/dictionary space */ - { const char* const sourceEnd = source + inputSize; - if ((sourceEnd > (const char*)streamPtr->dictionary) && (sourceEnd < dictEnd)) { - streamPtr->dictSize = (U32)(dictEnd - sourceEnd); - if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB; - if (streamPtr->dictSize < 4) streamPtr->dictSize = 0; - streamPtr->dictionary = (const BYTE*)dictEnd - streamPtr->dictSize; - } - } - - /* prefix mode : source data follows dictionary */ - if (dictEnd == source) { - if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) - return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration); - else - return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration); - } - - /* external dictionary mode */ - { int result; - if (streamPtr->dictCtx) { - /* We depend here on the fact that dictCtx'es (produced by - * LZ4_loadDict) guarantee that their tables contain no references - * to offsets between dictCtx->currentOffset - 64 KB and - * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe - * to use noDictIssue even when the dict isn't a full 64 KB. - */ - if (inputSize > 4 KB) { - /* For compressing large blobs, it is faster to pay the setup - * cost to copy the dictionary's tables into the active context, - * so that the compression loop is only looking into one table. - */ - LZ4_memcpy(streamPtr, streamPtr->dictCtx, sizeof(*streamPtr)); - result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); - } else { - result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration); - } - } else { /* small data <= 4 KB */ - if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { - result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration); - } else { - result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); - } - } - streamPtr->dictionary = (const BYTE*)source; - streamPtr->dictSize = (U32)inputSize; - return result; - } -} - - -/* Hidden debug function, to force-test external dictionary mode */ -int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize) -{ - LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse; - int result; - - LZ4_renormDictT(streamPtr, srcSize); - - if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { - result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, dictSmall, 1); - } else { - result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, noDictIssue, 1); - } - - streamPtr->dictionary = (const BYTE*)source; - streamPtr->dictSize = (U32)srcSize; - - return result; -} - - -/*! LZ4_saveDict() : - * If previously compressed data block is not guaranteed to remain available at its memory location, - * save it into a safer place (char* safeBuffer). - * Note : no need to call LZ4_loadDict() afterwards, dictionary is immediately usable, - * one can therefore call LZ4_compress_fast_continue() right after. - * @return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error. - */ -int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) -{ - LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse; - - DEBUGLOG(5, "LZ4_saveDict : dictSize=%i, safeBuffer=%p", dictSize, safeBuffer); - - if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */ - if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; } - - if (safeBuffer == NULL) assert(dictSize == 0); - if (dictSize > 0) { - const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize; - assert(dict->dictionary); - memmove(safeBuffer, previousDictEnd - dictSize, dictSize); - } - - dict->dictionary = (const BYTE*)safeBuffer; - dict->dictSize = (U32)dictSize; - - return dictSize; -} - - - -/*-******************************* - * Decompression functions - ********************************/ - -typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive; -typedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive; - -#undef MIN -#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) - -/* Read the variable-length literal or match length. - * - * ip - pointer to use as input. - * lencheck - end ip. Return an error if ip advances >= lencheck. - * loop_check - check ip >= lencheck in body of loop. Returns loop_error if so. - * initial_check - check ip >= lencheck before start of loop. Returns initial_error if so. - * error (output) - error code. Should be set to 0 before call. - */ -typedef enum { loop_error = -2, initial_error = -1, ok = 0 } variable_length_error; -LZ4_FORCE_INLINE unsigned -read_variable_length(const BYTE**ip, const BYTE* lencheck, - int loop_check, int initial_check, - variable_length_error* error) -{ - U32 length = 0; - U32 s; - if (initial_check && unlikely((*ip) >= lencheck)) { /* overflow detection */ - *error = initial_error; - return length; - } - do { - s = **ip; - (*ip)++; - length += s; - if (loop_check && unlikely((*ip) >= lencheck)) { /* overflow detection */ - *error = loop_error; - return length; - } - } while (s==255); - - return length; -} - -/*! LZ4_decompress_generic() : - * This generic decompression function covers all use cases. - * It shall be instantiated several times, using different sets of directives. - * Note that it is important for performance that this function really get inlined, - * in order to remove useless branches during compilation optimization. - */ -LZ4_FORCE_INLINE int -LZ4_decompress_generic( - const char* const src, - char* const dst, - int srcSize, - int outputSize, /* If endOnInput==endOnInputSize, this value is `dstCapacity` */ - - endCondition_directive endOnInput, /* endOnOutputSize, endOnInputSize */ - earlyEnd_directive partialDecoding, /* full, partial */ - dict_directive dict, /* noDict, withPrefix64k, usingExtDict */ - const BYTE* const lowPrefix, /* always <= dst, == dst when no prefix */ - const BYTE* const dictStart, /* only if dict==usingExtDict */ - const size_t dictSize /* note : = 0 if noDict */ - ) -{ - if ((src == NULL) || (outputSize < 0)) { return -1; } - - { const BYTE* ip = (const BYTE*) src; - const BYTE* const iend = ip + srcSize; - - BYTE* op = (BYTE*) dst; - BYTE* const oend = op + outputSize; - BYTE* cpy; - - const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize; - - const int safeDecode = (endOnInput==endOnInputSize); - const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB))); - - - /* Set up the "end" pointers for the shortcut. */ - const BYTE* const shortiend = iend - (endOnInput ? 14 : 8) /*maxLL*/ - 2 /*offset*/; - const BYTE* const shortoend = oend - (endOnInput ? 14 : 8) /*maxLL*/ - 18 /*maxML*/; - - const BYTE* match; - size_t offset; - unsigned token; - size_t length; - - - DEBUGLOG(5, "LZ4_decompress_generic (srcSize:%i, dstSize:%i)", srcSize, outputSize); - - /* Special cases */ - assert(lowPrefix <= op); - if ((endOnInput) && (unlikely(outputSize==0))) { - /* Empty output buffer */ - if (partialDecoding) return 0; - return ((srcSize==1) && (*ip==0)) ? 0 : -1; - } - if ((!endOnInput) && (unlikely(outputSize==0))) { return (*ip==0 ? 1 : -1); } - if ((endOnInput) && unlikely(srcSize==0)) { return -1; } - - /* Currently the fast loop shows a regression on qualcomm arm chips. */ -#if LZ4_FAST_DEC_LOOP - if ((oend - op) < FASTLOOP_SAFE_DISTANCE) { - DEBUGLOG(6, "skip fast decode loop"); - goto safe_decode; - } - - /* Fast loop : decode sequences as long as output < iend-FASTLOOP_SAFE_DISTANCE */ - while (1) { - /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */ - assert(oend - op >= FASTLOOP_SAFE_DISTANCE); - if (endOnInput) { assert(ip < iend); } - token = *ip++; - length = token >> ML_BITS; /* literal length */ - - assert(!endOnInput || ip <= iend); /* ip < iend before the increment */ - - /* decode literal length */ - if (length == RUN_MASK) { - variable_length_error error = ok; - length += read_variable_length(&ip, iend-RUN_MASK, (int)endOnInput, (int)endOnInput, &error); - if (error == initial_error) { goto _output_error; } - if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ - if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ - - /* copy literals */ - cpy = op+length; - LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); - if (endOnInput) { /* LZ4_decompress_safe() */ - if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; } - LZ4_wildCopy32(op, ip, cpy); - } else { /* LZ4_decompress_fast() */ - if (cpy>oend-8) { goto safe_literal_copy; } - LZ4_wildCopy8(op, ip, cpy); /* LZ4_decompress_fast() cannot copy more than 8 bytes at a time : - * it doesn't know input length, and only relies on end-of-block properties */ - } - ip += length; op = cpy; - } else { - cpy = op+length; - if (endOnInput) { /* LZ4_decompress_safe() */ - DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length); - /* We don't need to check oend, since we check it once for each loop below */ - if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; } - /* Literals can only be 14, but hope compilers optimize if we copy by a register size */ - LZ4_memcpy(op, ip, 16); - } else { /* LZ4_decompress_fast() */ - /* LZ4_decompress_fast() cannot copy more than 8 bytes at a time : - * it doesn't know input length, and relies on end-of-block properties */ - LZ4_memcpy(op, ip, 8); - if (length > 8) { LZ4_memcpy(op+8, ip+8, 8); } - } - ip += length; op = cpy; - } - - /* get offset */ - offset = LZ4_readLE16(ip); ip+=2; - match = op - offset; - assert(match <= op); - - /* get matchlength */ - length = token & ML_MASK; - - if (length == ML_MASK) { - variable_length_error error = ok; - if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ - length += read_variable_length(&ip, iend - LASTLITERALS + 1, (int)endOnInput, 0, &error); - if (error != ok) { goto _output_error; } - if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */ - length += MINMATCH; - if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { - goto safe_match_copy; - } - } else { - length += MINMATCH; - if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { - goto safe_match_copy; - } - - /* Fastpath check: Avoids a branch in LZ4_wildCopy32 if true */ - if ((dict == withPrefix64k) || (match >= lowPrefix)) { - if (offset >= 8) { - assert(match >= lowPrefix); - assert(match <= op); - assert(op + 18 <= oend); - - LZ4_memcpy(op, match, 8); - LZ4_memcpy(op+8, match+8, 8); - LZ4_memcpy(op+16, match+16, 2); - op += length; - continue; - } } } - - if (checkOffset && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ - /* match starting within external dictionary */ - if ((dict==usingExtDict) && (match < lowPrefix)) { - if (unlikely(op+length > oend-LASTLITERALS)) { - if (partialDecoding) { - DEBUGLOG(7, "partialDecoding: dictionary match, close to dstEnd"); - length = MIN(length, (size_t)(oend-op)); - } else { - goto _output_error; /* end-of-block condition violated */ - } } - - if (length <= (size_t)(lowPrefix-match)) { - /* match fits entirely within external dictionary : just copy */ - memmove(op, dictEnd - (lowPrefix-match), length); - op += length; - } else { - /* match stretches into both external dictionary and current block */ - size_t const copySize = (size_t)(lowPrefix - match); - size_t const restSize = length - copySize; - LZ4_memcpy(op, dictEnd - copySize, copySize); - op += copySize; - if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ - BYTE* const endOfMatch = op + restSize; - const BYTE* copyFrom = lowPrefix; - while (op < endOfMatch) { *op++ = *copyFrom++; } - } else { - LZ4_memcpy(op, lowPrefix, restSize); - op += restSize; - } } - continue; - } - - /* copy match within block */ - cpy = op + length; - - assert((op <= oend) && (oend-op >= 32)); - if (unlikely(offset<16)) { - LZ4_memcpy_using_offset(op, match, cpy, offset); - } else { - LZ4_wildCopy32(op, match, cpy); - } - - op = cpy; /* wildcopy correction */ - } - safe_decode: -#endif - - /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */ - while (1) { - token = *ip++; - length = token >> ML_BITS; /* literal length */ - - assert(!endOnInput || ip <= iend); /* ip < iend before the increment */ - - /* A two-stage shortcut for the most common case: - * 1) If the literal length is 0..14, and there is enough space, - * enter the shortcut and copy 16 bytes on behalf of the literals - * (in the fast mode, only 8 bytes can be safely copied this way). - * 2) Further if the match length is 4..18, copy 18 bytes in a similar - * manner; but we ensure that there's enough space in the output for - * those 18 bytes earlier, upon entering the shortcut (in other words, - * there is a combined check for both stages). - */ - if ( (endOnInput ? length != RUN_MASK : length <= 8) - /* strictly "less than" on input, to re-enter the loop with at least one byte */ - && likely((endOnInput ? ip < shortiend : 1) & (op <= shortoend)) ) { - /* Copy the literals */ - LZ4_memcpy(op, ip, endOnInput ? 16 : 8); - op += length; ip += length; - - /* The second stage: prepare for match copying, decode full info. - * If it doesn't work out, the info won't be wasted. */ - length = token & ML_MASK; /* match length */ - offset = LZ4_readLE16(ip); ip += 2; - match = op - offset; - assert(match <= op); /* check overflow */ - - /* Do not deal with overlapping matches. */ - if ( (length != ML_MASK) - && (offset >= 8) - && (dict==withPrefix64k || match >= lowPrefix) ) { - /* Copy the match. */ - LZ4_memcpy(op + 0, match + 0, 8); - LZ4_memcpy(op + 8, match + 8, 8); - LZ4_memcpy(op +16, match +16, 2); - op += length + MINMATCH; - /* Both stages worked, load the next token. */ - continue; - } - - /* The second stage didn't work out, but the info is ready. - * Propel it right to the point of match copying. */ - goto _copy_match; - } - - /* decode literal length */ - if (length == RUN_MASK) { - variable_length_error error = ok; - length += read_variable_length(&ip, iend-RUN_MASK, (int)endOnInput, (int)endOnInput, &error); - if (error == initial_error) { goto _output_error; } - if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ - if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ - } - - /* copy literals */ - cpy = op+length; -#if LZ4_FAST_DEC_LOOP - safe_literal_copy: -#endif - LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); - if ( ((endOnInput) && ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) ) - || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) ) - { - /* We've either hit the input parsing restriction or the output parsing restriction. - * In the normal scenario, decoding a full block, it must be the last sequence, - * otherwise it's an error (invalid input or dimensions). - * In partialDecoding scenario, it's necessary to ensure there is no buffer overflow. - */ - if (partialDecoding) { - /* Since we are partial decoding we may be in this block because of the output parsing - * restriction, which is not valid since the output buffer is allowed to be undersized. - */ - assert(endOnInput); - DEBUGLOG(7, "partialDecoding: copying literals, close to input or output end") - DEBUGLOG(7, "partialDecoding: literal length = %u", (unsigned)length); - DEBUGLOG(7, "partialDecoding: remaining space in dstBuffer : %i", (int)(oend - op)); - DEBUGLOG(7, "partialDecoding: remaining space in srcBuffer : %i", (int)(iend - ip)); - /* Finishing in the middle of a literals segment, - * due to lack of input. - */ - if (ip+length > iend) { - length = (size_t)(iend-ip); - cpy = op + length; - } - /* Finishing in the middle of a literals segment, - * due to lack of output space. - */ - if (cpy > oend) { - cpy = oend; - assert(op<=oend); - length = (size_t)(oend-op); - } - } else { - /* We must be on the last sequence because of the parsing limitations so check - * that we exactly regenerate the original size (must be exact when !endOnInput). - */ - if ((!endOnInput) && (cpy != oend)) { goto _output_error; } - /* We must be on the last sequence (or invalid) because of the parsing limitations - * so check that we exactly consume the input and don't overrun the output buffer. - */ - if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) { - DEBUGLOG(6, "should have been last run of literals") - DEBUGLOG(6, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend); - DEBUGLOG(6, "or cpy(%p) > oend(%p)", cpy, oend); - goto _output_error; - } - } - memmove(op, ip, length); /* supports overlapping memory regions; only matters for in-place decompression scenarios */ - ip += length; - op += length; - /* Necessarily EOF when !partialDecoding. - * When partialDecoding, it is EOF if we've either - * filled the output buffer or - * can't proceed with reading an offset for following match. - */ - if (!partialDecoding || (cpy == oend) || (ip >= (iend-2))) { - break; - } - } else { - LZ4_wildCopy8(op, ip, cpy); /* may overwrite up to WILDCOPYLENGTH beyond cpy */ - ip += length; op = cpy; - } - - /* get offset */ - offset = LZ4_readLE16(ip); ip+=2; - match = op - offset; - - /* get matchlength */ - length = token & ML_MASK; - - _copy_match: - if (length == ML_MASK) { - variable_length_error error = ok; - length += read_variable_length(&ip, iend - LASTLITERALS + 1, (int)endOnInput, 0, &error); - if (error != ok) goto _output_error; - if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */ - } - length += MINMATCH; - -#if LZ4_FAST_DEC_LOOP - safe_match_copy: -#endif - if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error; /* Error : offset outside buffers */ - /* match starting within external dictionary */ - if ((dict==usingExtDict) && (match < lowPrefix)) { - if (unlikely(op+length > oend-LASTLITERALS)) { - if (partialDecoding) length = MIN(length, (size_t)(oend-op)); - else goto _output_error; /* doesn't respect parsing restriction */ - } - - if (length <= (size_t)(lowPrefix-match)) { - /* match fits entirely within external dictionary : just copy */ - memmove(op, dictEnd - (lowPrefix-match), length); - op += length; - } else { - /* match stretches into both external dictionary and current block */ - size_t const copySize = (size_t)(lowPrefix - match); - size_t const restSize = length - copySize; - LZ4_memcpy(op, dictEnd - copySize, copySize); - op += copySize; - if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ - BYTE* const endOfMatch = op + restSize; - const BYTE* copyFrom = lowPrefix; - while (op < endOfMatch) *op++ = *copyFrom++; - } else { - LZ4_memcpy(op, lowPrefix, restSize); - op += restSize; - } } - continue; - } - assert(match >= lowPrefix); - - /* copy match within block */ - cpy = op + length; - - /* partialDecoding : may end anywhere within the block */ - assert(op<=oend); - if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) { - size_t const mlen = MIN(length, (size_t)(oend-op)); - const BYTE* const matchEnd = match + mlen; - BYTE* const copyEnd = op + mlen; - if (matchEnd > op) { /* overlap copy */ - while (op < copyEnd) { *op++ = *match++; } - } else { - LZ4_memcpy(op, match, mlen); - } - op = copyEnd; - if (op == oend) { break; } - continue; - } - - if (unlikely(offset<8)) { - LZ4_write32(op, 0); /* silence msan warning when offset==0 */ - op[0] = match[0]; - op[1] = match[1]; - op[2] = match[2]; - op[3] = match[3]; - match += inc32table[offset]; - LZ4_memcpy(op+4, match, 4); - match -= dec64table[offset]; - } else { - LZ4_memcpy(op, match, 8); - match += 8; - } - op += 8; - - if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) { - BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1); - if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */ - if (op < oCopyLimit) { - LZ4_wildCopy8(op, match, oCopyLimit); - match += oCopyLimit - op; - op = oCopyLimit; - } - while (op < cpy) { *op++ = *match++; } - } else { - LZ4_memcpy(op, match, 8); - if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); } - } - op = cpy; /* wildcopy correction */ - } - - /* end of decoding */ - if (endOnInput) { - DEBUGLOG(5, "decoded %i bytes", (int) (((char*)op)-dst)); - return (int) (((char*)op)-dst); /* Nb of output bytes decoded */ - } else { - return (int) (((const char*)ip)-src); /* Nb of input bytes read */ - } - - /* Overflow error detected */ - _output_error: - return (int) (-(((const char*)ip)-src))-1; - } -} - - -/*===== Instantiate the API decoding functions. =====*/ - -LZ4_FORCE_O2 -int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, - endOnInputSize, decode_full_block, noDict, - (BYTE*)dest, NULL, 0); -} - -LZ4_FORCE_O2 -int LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity) -{ - dstCapacity = MIN(targetOutputSize, dstCapacity); - return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity, - endOnInputSize, partial_decode, - noDict, (BYTE*)dst, NULL, 0); -} - -LZ4_FORCE_O2 -int LZ4_decompress_fast(const char* source, char* dest, int originalSize) -{ - return LZ4_decompress_generic(source, dest, 0, originalSize, - endOnOutputSize, decode_full_block, withPrefix64k, - (BYTE*)dest - 64 KB, NULL, 0); -} - -/*===== Instantiate a few more decoding cases, used more than once. =====*/ - -LZ4_FORCE_O2 /* Exported, an obsolete API function. */ -int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, - endOnInputSize, decode_full_block, withPrefix64k, - (BYTE*)dest - 64 KB, NULL, 0); -} - -LZ4_FORCE_O2 -static int LZ4_decompress_safe_partial_withPrefix64k(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity) -{ - dstCapacity = MIN(targetOutputSize, dstCapacity); - return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity, - endOnInputSize, partial_decode, withPrefix64k, - (BYTE*)dest - 64 KB, NULL, 0); -} - -/* Another obsolete API function, paired with the previous one. */ -int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize) -{ - /* LZ4_decompress_fast doesn't validate match offsets, - * and thus serves well with any prefixed dictionary. */ - return LZ4_decompress_fast(source, dest, originalSize); -} - -LZ4_FORCE_O2 -static int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize, - size_t prefixSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, - endOnInputSize, decode_full_block, noDict, - (BYTE*)dest-prefixSize, NULL, 0); -} - -LZ4_FORCE_O2 -static int LZ4_decompress_safe_partial_withSmallPrefix(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, - size_t prefixSize) -{ - dstCapacity = MIN(targetOutputSize, dstCapacity); - return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity, - endOnInputSize, partial_decode, noDict, - (BYTE*)dest-prefixSize, NULL, 0); -} - -LZ4_FORCE_O2 -int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, - int compressedSize, int maxOutputSize, - const void* dictStart, size_t dictSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, - endOnInputSize, decode_full_block, usingExtDict, - (BYTE*)dest, (const BYTE*)dictStart, dictSize); -} - -LZ4_FORCE_O2 -int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest, - int compressedSize, int targetOutputSize, int dstCapacity, - const void* dictStart, size_t dictSize) -{ - dstCapacity = MIN(targetOutputSize, dstCapacity); - return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity, - endOnInputSize, partial_decode, usingExtDict, - (BYTE*)dest, (const BYTE*)dictStart, dictSize); -} - -LZ4_FORCE_O2 -static int LZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize, - const void* dictStart, size_t dictSize) -{ - return LZ4_decompress_generic(source, dest, 0, originalSize, - endOnOutputSize, decode_full_block, usingExtDict, - (BYTE*)dest, (const BYTE*)dictStart, dictSize); -} - -/* The "double dictionary" mode, for use with e.g. ring buffers: the first part - * of the dictionary is passed as prefix, and the second via dictStart + dictSize. - * These routines are used only once, in LZ4_decompress_*_continue(). - */ -LZ4_FORCE_INLINE -int LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compressedSize, int maxOutputSize, - size_t prefixSize, const void* dictStart, size_t dictSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, - endOnInputSize, decode_full_block, usingExtDict, - (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize); -} - -LZ4_FORCE_INLINE -int LZ4_decompress_fast_doubleDict(const char* source, char* dest, int originalSize, - size_t prefixSize, const void* dictStart, size_t dictSize) -{ - return LZ4_decompress_generic(source, dest, 0, originalSize, - endOnOutputSize, decode_full_block, usingExtDict, - (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize); -} - -/*===== streaming decompression functions =====*/ - -LZ4_streamDecode_t* LZ4_createStreamDecode(void) -{ - LZ4_STATIC_ASSERT(sizeof(LZ4_streamDecode_t) >= sizeof(LZ4_streamDecode_t_internal)); - return (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t)); -} - -int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream) -{ - if (LZ4_stream == NULL) { return 0; } /* support free on NULL */ - FREEMEM(LZ4_stream); - return 0; -} - -/*! LZ4_setStreamDecode() : - * Use this function to instruct where to find the dictionary. - * This function is not necessary if previous data is still available where it was decoded. - * Loading a size of 0 is allowed (same effect as no dictionary). - * @return : 1 if OK, 0 if error - */ -int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize) -{ - LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; - lz4sd->prefixSize = (size_t)dictSize; - if (dictSize) { - assert(dictionary != NULL); - lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize; - } else { - lz4sd->prefixEnd = (const BYTE*) dictionary; - } - lz4sd->externalDict = NULL; - lz4sd->extDictSize = 0; - return 1; -} - -/*! LZ4_decoderRingBufferSize() : - * when setting a ring buffer for streaming decompression (optional scenario), - * provides the minimum size of this ring buffer - * to be compatible with any source respecting maxBlockSize condition. - * Note : in a ring buffer scenario, - * blocks are presumed decompressed next to each other. - * When not enough space remains for next block (remainingSize < maxBlockSize), - * decoding resumes from beginning of ring buffer. - * @return : minimum ring buffer size, - * or 0 if there is an error (invalid maxBlockSize). - */ -int LZ4_decoderRingBufferSize(int maxBlockSize) -{ - if (maxBlockSize < 0) return 0; - if (maxBlockSize > LZ4_MAX_INPUT_SIZE) return 0; - if (maxBlockSize < 16) maxBlockSize = 16; - return LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize); -} - -/* -*_continue() : - These decoding functions allow decompression of multiple blocks in "streaming" mode. - Previously decoded blocks must still be available at the memory position where they were decoded. - If it's not possible, save the relevant part of decoded data into a safe buffer, - and indicate where it stands using LZ4_setStreamDecode() -*/ -LZ4_FORCE_O2 -int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize) -{ - LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; - int result; - - if (lz4sd->prefixSize == 0) { - /* The first call, no dictionary yet. */ - assert(lz4sd->extDictSize == 0); - result = LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize); - if (result <= 0) return result; - lz4sd->prefixSize = (size_t)result; - lz4sd->prefixEnd = (BYTE*)dest + result; - } else if (lz4sd->prefixEnd == (BYTE*)dest) { - /* They're rolling the current segment. */ - if (lz4sd->prefixSize >= 64 KB - 1) - result = LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize); - else if (lz4sd->extDictSize == 0) - result = LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, - lz4sd->prefixSize); - else - result = LZ4_decompress_safe_doubleDict(source, dest, compressedSize, maxOutputSize, - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); - if (result <= 0) return result; - lz4sd->prefixSize += (size_t)result; - lz4sd->prefixEnd += result; - } else { - /* The buffer wraps around, or they're switching to another buffer. */ - lz4sd->extDictSize = lz4sd->prefixSize; - lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; - result = LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, - lz4sd->externalDict, lz4sd->extDictSize); - if (result <= 0) return result; - lz4sd->prefixSize = (size_t)result; - lz4sd->prefixEnd = (BYTE*)dest + result; - } - - return result; -} - -LZ4_FORCE_O2 -int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize) -{ - LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; - int result; - assert(originalSize >= 0); - - if (lz4sd->prefixSize == 0) { - assert(lz4sd->extDictSize == 0); - result = LZ4_decompress_fast(source, dest, originalSize); - if (result <= 0) return result; - lz4sd->prefixSize = (size_t)originalSize; - lz4sd->prefixEnd = (BYTE*)dest + originalSize; - } else if (lz4sd->prefixEnd == (BYTE*)dest) { - if (lz4sd->prefixSize >= 64 KB - 1 || lz4sd->extDictSize == 0) - result = LZ4_decompress_fast(source, dest, originalSize); - else - result = LZ4_decompress_fast_doubleDict(source, dest, originalSize, - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); - if (result <= 0) return result; - lz4sd->prefixSize += (size_t)originalSize; - lz4sd->prefixEnd += originalSize; - } else { - lz4sd->extDictSize = lz4sd->prefixSize; - lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; - result = LZ4_decompress_fast_extDict(source, dest, originalSize, - lz4sd->externalDict, lz4sd->extDictSize); - if (result <= 0) return result; - lz4sd->prefixSize = (size_t)originalSize; - lz4sd->prefixEnd = (BYTE*)dest + originalSize; - } - - return result; -} - - -/* -Advanced decoding functions : -*_usingDict() : - These decoding functions work the same as "_continue" ones, - the dictionary must be explicitly provided within parameters -*/ - -int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) -{ - if (dictSize==0) - return LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize); - if (dictStart+dictSize == dest) { - if (dictSize >= 64 KB - 1) { - return LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize); - } - assert(dictSize >= 0); - return LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, (size_t)dictSize); - } - assert(dictSize >= 0); - return LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, dictStart, (size_t)dictSize); -} - -int LZ4_decompress_safe_partial_usingDict(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, const char* dictStart, int dictSize) -{ - if (dictSize==0) - return LZ4_decompress_safe_partial(source, dest, compressedSize, targetOutputSize, dstCapacity); - if (dictStart+dictSize == dest) { - if (dictSize >= 64 KB - 1) { - return LZ4_decompress_safe_partial_withPrefix64k(source, dest, compressedSize, targetOutputSize, dstCapacity); - } - assert(dictSize >= 0); - return LZ4_decompress_safe_partial_withSmallPrefix(source, dest, compressedSize, targetOutputSize, dstCapacity, (size_t)dictSize); - } - assert(dictSize >= 0); - return LZ4_decompress_safe_partial_forceExtDict(source, dest, compressedSize, targetOutputSize, dstCapacity, dictStart, (size_t)dictSize); -} - -int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize) -{ - if (dictSize==0 || dictStart+dictSize == dest) - return LZ4_decompress_fast(source, dest, originalSize); - assert(dictSize >= 0); - return LZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize); -} - - -/*=************************************************* -* Obsolete Functions -***************************************************/ -/* obsolete compression functions */ -int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) -{ - return LZ4_compress_default(source, dest, inputSize, maxOutputSize); -} -int LZ4_compress(const char* src, char* dest, int srcSize) -{ - return LZ4_compress_default(src, dest, srcSize, LZ4_compressBound(srcSize)); -} -int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) -{ - return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); -} -int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) -{ - return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); -} -int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int dstCapacity) -{ - return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, dstCapacity, 1); -} -int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) -{ - return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); -} - -/* -These decompression functions are deprecated and should no longer be used. -They are only provided here for compatibility with older user programs. -- LZ4_uncompress is totally equivalent to LZ4_decompress_fast -- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe -*/ -int LZ4_uncompress (const char* source, char* dest, int outputSize) -{ - return LZ4_decompress_fast(source, dest, outputSize); -} -int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) -{ - return LZ4_decompress_safe(source, dest, isize, maxOutputSize); -} - -/* Obsolete Streaming functions */ - -int LZ4_sizeofStreamState(void) { return sizeof(LZ4_stream_t); } - -int LZ4_resetStreamState(void* state, char* inputBuffer) -{ - (void)inputBuffer; - LZ4_resetStream((LZ4_stream_t*)state); - return 0; -} - -void* LZ4_create (char* inputBuffer) -{ - (void)inputBuffer; - return LZ4_createStream(); -} - -char* LZ4_slideInputBuffer (void* state) -{ - /* avoid const char * -> char * conversion warning */ - return (char *)(uptrval)((LZ4_stream_t*)state)->internal_donotuse.dictionary; -} - -#endif /* LZ4_COMMONDEFS_ONLY */ diff --git a/src/core/compress/lz4/third_party/lib/lz4.h b/src/core/compress/lz4/third_party/lib/lz4.h deleted file mode 100644 index 7081e76..0000000 --- a/src/core/compress/lz4/third_party/lib/lz4.h +++ /dev/null @@ -1,778 +0,0 @@ -/* - * LZ4 - Fast LZ compression algorithm - * Header File - * Copyright (C) 2011-2020, Yann Collet. - - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - - 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. - - You can contact the author at : - - LZ4 homepage : http://www.lz4.org - - LZ4 source repository : https://github.com/lz4/lz4 -*/ -#if defined (__cplusplus) -extern "C" { -#endif - -#ifndef LZ4_H_2983827168210 -#define LZ4_H_2983827168210 - -/* --- Dependency --- */ -#include /* size_t */ - - -/** - Introduction - - LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core, - scalable with multi-cores CPU. It features an extremely fast decoder, with speed in - multiple GB/s per core, typically reaching RAM speed limits on multi-core systems. - - The LZ4 compression library provides in-memory compression and decompression functions. - It gives full buffer control to user. - Compression can be done in: - - a single step (described as Simple Functions) - - a single step, reusing a context (described in Advanced Functions) - - unbounded multiple steps (described as Streaming compression) - - lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md). - Decompressing such a compressed block requires additional metadata. - Exact metadata depends on exact decompression function. - For the typical case of LZ4_decompress_safe(), - metadata includes block's compressed size, and maximum bound of decompressed size. - Each application is free to encode and pass such metadata in whichever way it wants. - - lz4.h only handle blocks, it can not generate Frames. - - Blocks are different from Frames (doc/lz4_Frame_format.md). - Frames bundle both blocks and metadata in a specified manner. - Embedding metadata is required for compressed data to be self-contained and portable. - Frame format is delivered through a companion API, declared in lz4frame.h. - The `lz4` CLI can only manage frames. -*/ - -/*^*************************************************************** -* Export parameters -*****************************************************************/ -/* -* LZ4_DLL_EXPORT : -* Enable exporting of functions when building a Windows DLL -* LZ4LIB_VISIBILITY : -* Control library symbols visibility. -*/ -#ifndef LZ4LIB_VISIBILITY -# if defined(__GNUC__) && (__GNUC__ >= 4) -# define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default"))) -# else -# define LZ4LIB_VISIBILITY -# endif -#endif -#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1) -# define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY -#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1) -# define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ -#else -# define LZ4LIB_API LZ4LIB_VISIBILITY -#endif - -/*------ Version ------*/ -#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ -#define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */ -#define LZ4_VERSION_RELEASE 4 /* for tweaks, bug-fixes, or development */ - -#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) - -#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE -#define LZ4_QUOTE(str) #str -#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str) -#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) /* requires v1.7.3+ */ - -LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version; requires v1.3.0+ */ -LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version; requires v1.7.5+ */ - - -/*-************************************ -* Tuning parameter -**************************************/ -#define LZ4_MEMORY_USAGE_MIN 10 -#define LZ4_MEMORY_USAGE_DEFAULT 14 -#define LZ4_MEMORY_USAGE_MAX 20 - -/*! - * LZ4_MEMORY_USAGE : - * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; ) - * Increasing memory usage improves compression ratio, at the cost of speed. - * Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality. - * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache - */ -#ifndef LZ4_MEMORY_USAGE -# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT -#endif - -#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN) -# error "LZ4_MEMORY_USAGE is too small !" -#endif - -#if (LZ4_MEMORY_USAGE > LZ4_MEMORY_USAGE_MAX) -# error "LZ4_MEMORY_USAGE is too large !" -#endif - -/*-************************************ -* Simple Functions -**************************************/ -/*! LZ4_compress_default() : - * Compresses 'srcSize' bytes from buffer 'src' - * into already allocated 'dst' buffer of size 'dstCapacity'. - * Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize). - * It also runs faster, so it's a recommended setting. - * If the function cannot compress 'src' into a more limited 'dst' budget, - * compression stops *immediately*, and the function result is zero. - * In which case, 'dst' content is undefined (invalid). - * srcSize : max supported value is LZ4_MAX_INPUT_SIZE. - * dstCapacity : size of buffer 'dst' (which must be already allocated) - * @return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity) - * or 0 if compression fails - * Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer). - */ -LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity); - -/*! LZ4_decompress_safe() : - * compressedSize : is the exact complete size of the compressed block. - * dstCapacity : is the size of destination buffer (which must be already allocated), presumed an upper bound of decompressed size. - * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity) - * If destination buffer is not large enough, decoding will stop and output an error code (negative value). - * If the source stream is detected malformed, the function will stop decoding and return a negative result. - * Note 1 : This function is protected against malicious data packets : - * it will never writes outside 'dst' buffer, nor read outside 'source' buffer, - * even if the compressed block is maliciously modified to order the decoder to do these actions. - * In such case, the decoder stops immediately, and considers the compressed block malformed. - * Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them. - * The implementation is free to send / store / derive this information in whichever way is most beneficial. - * If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead. - */ -LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity); - - -/*-************************************ -* Advanced Functions -**************************************/ -#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ -#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) - -/*! LZ4_compressBound() : - Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible) - This function is primarily useful for memory allocation purposes (destination buffer size). - Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example). - Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize) - inputSize : max supported value is LZ4_MAX_INPUT_SIZE - return : maximum output size in a "worst case" scenario - or 0, if input size is incorrect (too large or negative) -*/ -LZ4LIB_API int LZ4_compressBound(int inputSize); - -/*! LZ4_compress_fast() : - Same as LZ4_compress_default(), but allows selection of "acceleration" factor. - The larger the acceleration value, the faster the algorithm, but also the lesser the compression. - It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. - An acceleration value of "1" is the same as regular LZ4_compress_default() - Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c). - Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c). -*/ -LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - - -/*! LZ4_compress_fast_extState() : - * Same as LZ4_compress_fast(), using an externally allocated memory space for its state. - * Use LZ4_sizeofState() to know how much memory must be allocated, - * and allocate it on 8-bytes boundaries (using `malloc()` typically). - * Then, provide this buffer as `void* state` to compression function. - */ -LZ4LIB_API int LZ4_sizeofState(void); -LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - - -/*! LZ4_compress_destSize() : - * Reverse the logic : compresses as much data as possible from 'src' buffer - * into already allocated buffer 'dst', of size >= 'targetDestSize'. - * This function either compresses the entire 'src' content into 'dst' if it's large enough, - * or fill 'dst' buffer completely with as much data as possible from 'src'. - * note: acceleration parameter is fixed to "default". - * - * *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'. - * New value is necessarily <= input value. - * @return : Nb bytes written into 'dst' (necessarily <= targetDestSize) - * or 0 if compression fails. - * - * Note : from v1.8.2 to v1.9.1, this function had a bug (fixed un v1.9.2+): - * the produced compressed content could, in specific circumstances, - * require to be decompressed into a destination buffer larger - * by at least 1 byte than the content to decompress. - * If an application uses `LZ4_compress_destSize()`, - * it's highly recommended to update liblz4 to v1.9.2 or better. - * If this can't be done or ensured, - * the receiving decompression function should provide - * a dstCapacity which is > decompressedSize, by at least 1 byte. - * See https://github.com/lz4/lz4/issues/859 for details - */ -LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize); - - -/*! LZ4_decompress_safe_partial() : - * Decompress an LZ4 compressed block, of size 'srcSize' at position 'src', - * into destination buffer 'dst' of size 'dstCapacity'. - * Up to 'targetOutputSize' bytes will be decoded. - * The function stops decoding on reaching this objective. - * This can be useful to boost performance - * whenever only the beginning of a block is required. - * - * @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize) - * If source stream is detected malformed, function returns a negative result. - * - * Note 1 : @return can be < targetOutputSize, if compressed block contains less data. - * - * Note 2 : targetOutputSize must be <= dstCapacity - * - * Note 3 : this function effectively stops decoding on reaching targetOutputSize, - * so dstCapacity is kind of redundant. - * This is because in older versions of this function, - * decoding operation would still write complete sequences. - * Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize, - * it could write more bytes, though only up to dstCapacity. - * Some "margin" used to be required for this operation to work properly. - * Thankfully, this is no longer necessary. - * The function nonetheless keeps the same signature, in an effort to preserve API compatibility. - * - * Note 4 : If srcSize is the exact size of the block, - * then targetOutputSize can be any value, - * including larger than the block's decompressed size. - * The function will, at most, generate block's decompressed size. - * - * Note 5 : If srcSize is _larger_ than block's compressed size, - * then targetOutputSize **MUST** be <= block's decompressed size. - * Otherwise, *silent corruption will occur*. - */ -LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity); - - -/*-********************************************* -* Streaming Compression Functions -***********************************************/ -typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */ - -LZ4LIB_API LZ4_stream_t* LZ4_createStream(void); -LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr); - -/*! LZ4_resetStream_fast() : v1.9.0+ - * Use this to prepare an LZ4_stream_t for a new chain of dependent blocks - * (e.g., LZ4_compress_fast_continue()). - * - * An LZ4_stream_t must be initialized once before usage. - * This is automatically done when created by LZ4_createStream(). - * However, should the LZ4_stream_t be simply declared on stack (for example), - * it's necessary to initialize it first, using LZ4_initStream(). - * - * After init, start any new stream with LZ4_resetStream_fast(). - * A same LZ4_stream_t can be re-used multiple times consecutively - * and compress multiple streams, - * provided that it starts each new stream with LZ4_resetStream_fast(). - * - * LZ4_resetStream_fast() is much faster than LZ4_initStream(), - * but is not compatible with memory regions containing garbage data. - * - * Note: it's only useful to call LZ4_resetStream_fast() - * in the context of streaming compression. - * The *extState* functions perform their own resets. - * Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive. - */ -LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr); - -/*! LZ4_loadDict() : - * Use this function to reference a static dictionary into LZ4_stream_t. - * The dictionary must remain available during compression. - * LZ4_loadDict() triggers a reset, so any previous data will be forgotten. - * The same dictionary will have to be loaded on decompression side for successful decoding. - * Dictionary are useful for better compression of small data (KB range). - * While LZ4 accept any input as dictionary, - * results are generally better when using Zstandard's Dictionary Builder. - * Loading a size of 0 is allowed, and is the same as reset. - * @return : loaded dictionary size, in bytes (necessarily <= 64 KB) - */ -LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); - -/*! LZ4_compress_fast_continue() : - * Compress 'src' content using data from previously compressed blocks, for better compression ratio. - * 'dst' buffer must be already allocated. - * If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. - * - * @return : size of compressed block - * or 0 if there is an error (typically, cannot fit into 'dst'). - * - * Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block. - * Each block has precise boundaries. - * Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata. - * It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together. - * - * Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory ! - * - * Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB. - * Make sure that buffers are separated, by at least one byte. - * This construction ensures that each block only depends on previous block. - * - * Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB. - * - * Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed. - */ -LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - -/*! LZ4_saveDict() : - * If last 64KB data cannot be guaranteed to remain available at its current memory location, - * save it into a safer place (char* safeBuffer). - * This is schematically equivalent to a memcpy() followed by LZ4_loadDict(), - * but is much faster, because LZ4_saveDict() doesn't need to rebuild tables. - * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error. - */ -LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize); - - -/*-********************************************** -* Streaming Decompression Functions -* Bufferless synchronous API -************************************************/ -typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */ - -/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() : - * creation / destruction of streaming decompression tracking context. - * A tracking context can be re-used multiple times. - */ -LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void); -LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream); - -/*! LZ4_setStreamDecode() : - * An LZ4_streamDecode_t context can be allocated once and re-used multiple times. - * Use this function to start decompression of a new stream of blocks. - * A dictionary can optionally be set. Use NULL or size 0 for a reset order. - * Dictionary is presumed stable : it must remain accessible and unmodified during next decompression. - * @return : 1 if OK, 0 if error - */ -LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize); - -/*! LZ4_decoderRingBufferSize() : v1.8.2+ - * Note : in a ring buffer scenario (optional), - * blocks are presumed decompressed next to each other - * up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize), - * at which stage it resumes from beginning of ring buffer. - * When setting such a ring buffer for streaming decompression, - * provides the minimum size of this ring buffer - * to be compatible with any source respecting maxBlockSize condition. - * @return : minimum ring buffer size, - * or 0 if there is an error (invalid maxBlockSize). - */ -LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize); -#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize)) /* for static allocation; maxBlockSize presumed valid */ - -/*! LZ4_decompress_*_continue() : - * These decoding functions allow decompression of consecutive blocks in "streaming" mode. - * A block is an unsplittable entity, it must be presented entirely to a decompression function. - * Decompression functions only accepts one block at a time. - * The last 64KB of previously decoded data *must* remain available and unmodified at the memory position where they were decoded. - * If less than 64KB of data has been decoded, all the data must be present. - * - * Special : if decompression side sets a ring buffer, it must respect one of the following conditions : - * - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize). - * maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes. - * In which case, encoding and decoding buffers do not need to be synchronized. - * Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize. - * - Synchronized mode : - * Decompression buffer size is _exactly_ the same as compression buffer size, - * and follows exactly same update rule (block boundaries at same positions), - * and decoding function is provided with exact decompressed size of each block (exception for last block of the stream), - * _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB). - * - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes. - * In which case, encoding and decoding buffers do not need to be synchronized, - * and encoding ring buffer can have any size, including small ones ( < 64 KB). - * - * Whenever these conditions are not possible, - * save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression, - * then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block. -*/ -LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int srcSize, int dstCapacity); - - -/*! LZ4_decompress_*_usingDict() : - * These decoding functions work the same as - * a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue() - * They are stand-alone, and don't need an LZ4_streamDecode_t structure. - * Dictionary is presumed stable : it must remain accessible and unmodified during decompression. - * Performance tip : Decompression speed can be substantially increased - * when dst == dictStart + dictSize. - */ -LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* src, char* dst, int srcSize, int dstCapcity, const char* dictStart, int dictSize); - -LZ4LIB_API int LZ4_decompress_safe_partial_usingDict(const char* source, char* dest, int compressedSize, int targetOutputSize, int maxOutputSize, const char* dictStart, int dictSize); -#endif /* LZ4_H_2983827168210 */ - - -/*^************************************* - * !!!!!! STATIC LINKING ONLY !!!!!! - ***************************************/ - -/*-**************************************************************************** - * Experimental section - * - * Symbols declared in this section must be considered unstable. Their - * signatures or semantics may change, or they may be removed altogether in the - * future. They are therefore only safe to depend on when the caller is - * statically linked against the library. - * - * To protect against unsafe usage, not only are the declarations guarded, - * the definitions are hidden by default - * when building LZ4 as a shared/dynamic library. - * - * In order to access these declarations, - * define LZ4_STATIC_LINKING_ONLY in your application - * before including LZ4's headers. - * - * In order to make their implementations accessible dynamically, you must - * define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library. - ******************************************************************************/ - -#ifdef LZ4_STATIC_LINKING_ONLY - -#ifndef LZ4_STATIC_3504398509 -#define LZ4_STATIC_3504398509 - -#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS -#define LZ4LIB_STATIC_API LZ4LIB_API -#else -#define LZ4LIB_STATIC_API -#endif - - -/*! LZ4_compress_fast_extState_fastReset() : - * A variant of LZ4_compress_fast_extState(). - * - * Using this variant avoids an expensive initialization step. - * It is only safe to call if the state buffer is known to be correctly initialized already - * (see above comment on LZ4_resetStream_fast() for a definition of "correctly initialized"). - * From a high level, the difference is that - * this function initializes the provided state with a call to something like LZ4_resetStream_fast() - * while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream(). - */ -LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - -/*! LZ4_attach_dictionary() : - * This is an experimental API that allows - * efficient use of a static dictionary many times. - * - * Rather than re-loading the dictionary buffer into a working context before - * each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a - * working LZ4_stream_t, this function introduces a no-copy setup mechanism, - * in which the working stream references the dictionary stream in-place. - * - * Several assumptions are made about the state of the dictionary stream. - * Currently, only streams which have been prepared by LZ4_loadDict() should - * be expected to work. - * - * Alternatively, the provided dictionaryStream may be NULL, - * in which case any existing dictionary stream is unset. - * - * If a dictionary is provided, it replaces any pre-existing stream history. - * The dictionary contents are the only history that can be referenced and - * logically immediately precede the data compressed in the first subsequent - * compression call. - * - * The dictionary will only remain attached to the working stream through the - * first compression call, at the end of which it is cleared. The dictionary - * stream (and source buffer) must remain in-place / accessible / unchanged - * through the completion of the first compression call on the stream. - */ -LZ4LIB_STATIC_API void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream); - - -/*! In-place compression and decompression - * - * It's possible to have input and output sharing the same buffer, - * for highly constrained memory environments. - * In both cases, it requires input to lay at the end of the buffer, - * and decompression to start at beginning of the buffer. - * Buffer size must feature some margin, hence be larger than final size. - * - * |<------------------------buffer--------------------------------->| - * |<-----------compressed data--------->| - * |<-----------decompressed size------------------>| - * |<----margin---->| - * - * This technique is more useful for decompression, - * since decompressed size is typically larger, - * and margin is short. - * - * In-place decompression will work inside any buffer - * which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize). - * This presumes that decompressedSize > compressedSize. - * Otherwise, it means compression actually expanded data, - * and it would be more efficient to store such data with a flag indicating it's not compressed. - * This can happen when data is not compressible (already compressed, or encrypted). - * - * For in-place compression, margin is larger, as it must be able to cope with both - * history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX, - * and data expansion, which can happen when input is not compressible. - * As a consequence, buffer size requirements are much higher, - * and memory savings offered by in-place compression are more limited. - * - * There are ways to limit this cost for compression : - * - Reduce history size, by modifying LZ4_DISTANCE_MAX. - * Note that it is a compile-time constant, so all compressions will apply this limit. - * Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX, - * so it's a reasonable trick when inputs are known to be small. - * - Require the compressor to deliver a "maximum compressed size". - * This is the `dstCapacity` parameter in `LZ4_compress*()`. - * When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail, - * in which case, the return code will be 0 (zero). - * The caller must be ready for these cases to happen, - * and typically design a backup scheme to send data uncompressed. - * The combination of both techniques can significantly reduce - * the amount of margin required for in-place compression. - * - * In-place compression can work in any buffer - * which size is >= (maxCompressedSize) - * with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success. - * LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX, - * so it's possible to reduce memory requirements by playing with them. - */ - -#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize) (((compressedSize) >> 8) + 32) -#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize) ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize)) /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */ - -#ifndef LZ4_DISTANCE_MAX /* history window size; can be user-defined at compile time */ -# define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */ -#endif - -#define LZ4_COMPRESS_INPLACE_MARGIN (LZ4_DISTANCE_MAX + 32) /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */ -#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize) ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN) /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */ - -#endif /* LZ4_STATIC_3504398509 */ -#endif /* LZ4_STATIC_LINKING_ONLY */ - - - -#ifndef LZ4_H_98237428734687 -#define LZ4_H_98237428734687 - -/*-************************************************************ - * Private Definitions - ************************************************************** - * Do not use these definitions directly. - * They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`. - * Accessing members will expose user code to API and/or ABI break in future versions of the library. - **************************************************************/ -#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2) -#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE) -#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */ - -#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) -# include - typedef int8_t LZ4_i8; - typedef uint8_t LZ4_byte; - typedef uint16_t LZ4_u16; - typedef uint32_t LZ4_u32; -#else - typedef signed char LZ4_i8; - typedef unsigned char LZ4_byte; - typedef unsigned short LZ4_u16; - typedef unsigned int LZ4_u32; -#endif - -/*! LZ4_stream_t : - * Never ever use below internal definitions directly ! - * These definitions are not API/ABI safe, and may change in future versions. - * If you need static allocation, declare or allocate an LZ4_stream_t object. -**/ - -typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; -struct LZ4_stream_t_internal { - LZ4_u32 hashTable[LZ4_HASH_SIZE_U32]; - const LZ4_byte* dictionary; - const LZ4_stream_t_internal* dictCtx; - LZ4_u32 currentOffset; - LZ4_u32 tableType; - LZ4_u32 dictSize; - /* Implicit padding to ensure structure is aligned */ -}; - -#define LZ4_STREAM_MINSIZE ((1UL << LZ4_MEMORY_USAGE) + 32) /* static size, for inter-version compatibility */ -union LZ4_stream_u { - char minStateSize[LZ4_STREAM_MINSIZE]; - LZ4_stream_t_internal internal_donotuse; -}; /* previously typedef'd to LZ4_stream_t */ - - -/*! LZ4_initStream() : v1.9.0+ - * An LZ4_stream_t structure must be initialized at least once. - * This is automatically done when invoking LZ4_createStream(), - * but it's not when the structure is simply declared on stack (for example). - * - * Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t. - * It can also initialize any arbitrary buffer of sufficient size, - * and will @return a pointer of proper type upon initialization. - * - * Note : initialization fails if size and alignment conditions are not respected. - * In which case, the function will @return NULL. - * Note2: An LZ4_stream_t structure guarantees correct alignment and size. - * Note3: Before v1.9.0, use LZ4_resetStream() instead -**/ -LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size); - - -/*! LZ4_streamDecode_t : - * Never ever use below internal definitions directly ! - * These definitions are not API/ABI safe, and may change in future versions. - * If you need static allocation, declare or allocate an LZ4_streamDecode_t object. -**/ -typedef struct { - const LZ4_byte* externalDict; - const LZ4_byte* prefixEnd; - size_t extDictSize; - size_t prefixSize; -} LZ4_streamDecode_t_internal; - -#define LZ4_STREAMDECODE_MINSIZE 32 -union LZ4_streamDecode_u { - char minStateSize[LZ4_STREAMDECODE_MINSIZE]; - LZ4_streamDecode_t_internal internal_donotuse; -} ; /* previously typedef'd to LZ4_streamDecode_t */ - - - -/*-************************************ -* Obsolete Functions -**************************************/ - -/*! Deprecation warnings - * - * Deprecated functions make the compiler generate a warning when invoked. - * This is meant to invite users to update their source code. - * Should deprecation warnings be a problem, it is generally possible to disable them, - * typically with -Wno-deprecated-declarations for gcc - * or _CRT_SECURE_NO_WARNINGS in Visual. - * - * Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS - * before including the header file. - */ -#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS -# define LZ4_DEPRECATED(message) /* disable deprecation warnings */ -#else -# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ -# define LZ4_DEPRECATED(message) [[deprecated(message)]] -# elif defined(_MSC_VER) -# define LZ4_DEPRECATED(message) __declspec(deprecated(message)) -# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45)) -# define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) -# elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31) -# define LZ4_DEPRECATED(message) __attribute__((deprecated)) -# else -# pragma message("WARNING: LZ4_DEPRECATED needs custom implementation for this compiler") -# define LZ4_DEPRECATED(message) /* disabled */ -# endif -#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */ - -/*! Obsolete compression functions (since v1.7.3) */ -LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* src, char* dest, int srcSize); -LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); - -/*! Obsolete decompression functions (since v1.8.0) */ -LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize); -LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); - -/* Obsolete streaming functions (since v1.7.0) - * degraded functionality; do not use! - * - * In order to perform streaming compression, these functions depended on data - * that is no longer tracked in the state. They have been preserved as well as - * possible: using them will still produce a correct output. However, they don't - * actually retain any history between compression calls. The compression ratio - * achieved will therefore be no better than compressing each chunk - * independently. - */ -LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer); -LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStreamState(void); -LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer); -LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state); - -/*! Obsolete streaming decoding functions (since v1.7.0) */ -LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize); -LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize); - -/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) : - * These functions used to be faster than LZ4_decompress_safe(), - * but this is no longer the case. They are now slower. - * This is because LZ4_decompress_fast() doesn't know the input size, - * and therefore must progress more cautiously into the input buffer to not read beyond the end of block. - * On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability. - * As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated. - * - * The last remaining LZ4_decompress_fast() specificity is that - * it can decompress a block without knowing its compressed size. - * Such functionality can be achieved in a more secure manner - * by employing LZ4_decompress_safe_partial(). - * - * Parameters: - * originalSize : is the uncompressed size to regenerate. - * `dst` must be already allocated, its size must be >= 'originalSize' bytes. - * @return : number of bytes read from source buffer (== compressed size). - * The function expects to finish at block's end exactly. - * If the source stream is detected malformed, the function stops decoding and returns a negative result. - * note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer. - * However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds. - * Also, since match offsets are not validated, match reads from 'src' may underflow too. - * These issues never happen if input (compressed) data is correct. - * But they may happen if input data is invalid (error or intentional tampering). - * As a consequence, use these functions in trusted environments with trusted data **only**. - */ -LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead") -LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize); -LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead") -LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize); -LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_usingDict() instead") -LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize); - -/*! LZ4_resetStream() : - * An LZ4_stream_t structure must be initialized at least once. - * This is done with LZ4_initStream(), or LZ4_resetStream(). - * Consider switching to LZ4_initStream(), - * invoking LZ4_resetStream() will trigger deprecation warnings in the future. - */ -LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr); - - -#endif /* LZ4_H_98237428734687 */ - - -#if defined (__cplusplus) -} -#endif diff --git a/src/core/compress/lz4/third_party/mod.rs b/src/core/compress/lz4/third_party/mod.rs deleted file mode 100644 index 53b3036..0000000 --- a/src/core/compress/lz4/third_party/mod.rs +++ /dev/null @@ -1,72 +0,0 @@ -/// lz4: https://github.com/lz4/lz4 -use std::ffi::c_void; - -#[link(name = "lib_third_party_compress_lz4", kind = "static")] -#[allow(unused)] -extern "C" { - pub(super) fn LZ4_compress_default( - src: *const u8, - dst: *mut u8, - srcSize: i32, - dstCapacity: i32, - ) -> i32; - - pub(super) fn LZ4_decompress_safe( - src: *const u8, - dst: *mut u8, - compressedSize: i32, - dstCapacity: i32, - ) -> i32; - - pub(super) fn LZ4_compress_fast( - src: *const u8, - dst: *mut u8, - srcSize: i32, - dstCapacity: i32, - acceleration: i32, - ) -> i32; - - pub(super) fn LZ4_createStream() -> *mut c_void; - - pub(super) fn LZ4_freeStream(streamPtr: *mut c_void) -> i32; - - pub(super) fn LZ4_resetStream_fast(streamPtr: *mut c_void); - - pub(super) fn LZ4_createStreamDecode() -> *mut c_void; - - pub(super) fn LZ4_freeStreamDecode(LZ4_stream: *mut c_void) -> i32; - - pub(super) fn LZ4_compressBound(inputSize: i32) -> i32; - - pub(super) fn LZ4_compress_fast_continue( - streamPtr: *mut c_void, - src: *const u8, - dst: *mut u8, - srcSize: i32, - dstCapacity: i32, - acceleration: i32, - ) -> i32; - - pub(super) fn LZ4_decompress_safe_continue( - LZ4_streamDecode: *mut c_void, - src: *const u8, - dst: *mut u8, - srcSize: i32, - dstCapacity: i32, - ) -> i32; - -} - -#[cfg(test)] -mod tests { - - use super::LZ4_compress_default; - - #[test] - fn test_lz4_compress() { - unsafe { - let ret = LZ4_compress_default(std::ptr::null(), std::ptr::null_mut(), 0, 0); - assert_eq!(ret, 0) - } - } -} diff --git a/src/core/compress/mod.rs b/src/core/compress/mod.rs deleted file mode 100644 index 1cc651d..0000000 --- a/src/core/compress/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -mod lz4; -pub use lz4::Lz4Compress; - -use std::{ - pin::Pin, - task::{Context, Poll}, -}; - -pub trait Encoder { - fn poll_encode_write( - self: Pin<&mut Self>, - cx: &mut Context, - buf: &[u8], - ) -> Poll>; -} - -pub trait Decoder { - fn poll_decode_read( - self: Pin<&mut Self>, - cx: &mut Context, - buf: &mut crate::ReadBuf<'_>, - ) -> Poll>; -} diff --git a/src/core/controller.rs b/src/core/controller.rs deleted file mode 100644 index 8f32379..0000000 --- a/src/core/controller.rs +++ /dev/null @@ -1,46 +0,0 @@ -use std::{future::Future, pin::Pin, sync::Arc}; - -use crate::Address; - -type BoxedFuture = Pin> + Send + 'static>>; - -pub trait Controller { - fn register( - &self, - env: Arc, - fut: BoxedFuture<()>, - ) -> crate::Result<()>; -} - -pub trait Environ { - fn conn(&self) -> Address; - - fn info(&self) -> crate::Result; -} - -impl Controller for Arc -where - C: Controller, -{ - #[inline] - fn register( - &self, - env: Arc, - fut: BoxedFuture<()>, - ) -> crate::Result<()> { - (**self).register(env, fut) - } -} - -impl Environ for Arc -where - E: Environ, -{ - fn conn(&self) -> Address { - (**self).conn() - } - - fn info(&self) -> crate::Result { - (**self).info() - } -} diff --git a/src/core/encryption/aes/mod.rs b/src/core/encryption/aes/mod.rs deleted file mode 100644 index 1f64862..0000000 --- a/src/core/encryption/aes/mod.rs +++ /dev/null @@ -1,244 +0,0 @@ -use std::{pin::Pin, task::Poll}; - -use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit}; - -use crate::{guard::buffer::Buffer, AsyncRead, AsyncWrite, NetSocket, ReadBuf}; - -use super::{Decrypt, Encrypt}; - -// ref https://docs.rs/cbc/0.1.2/cbc/ - -type Aes128CbcEnc = cbc::Encryptor; -type Aes128CbcDec = cbc::Decryptor; - -pub struct AESEncryptor { - target: T, - iv: [u8; 16], - key: [u8; 16], - aes_ebuf: Option>, - aes_dbuf: Buffer, - aes_rbuf: Option>, - aes_epos: usize, - aes_rpos: usize, - aes_dinit: bool, -} - -impl AESEncryptor { - pub fn new(target: T, iv: [u8; 16], key: [u8; 16]) -> Self { - Self { - target, - iv, - key, - aes_ebuf: Default::default(), - aes_dbuf: Default::default(), - aes_rbuf: Default::default(), - aes_epos: Default::default(), - aes_rpos: Default::default(), - aes_dinit: Default::default(), - } - } -} - -impl NetSocket for AESEncryptor -where - T: NetSocket, -{ - fn peer_addr(&self) -> crate::Result { - self.target.peer_addr() - } - - fn local_addr(&self) -> crate::Result { - self.target.local_addr() - } -} - -impl AsyncRead for AESEncryptor -where - T: AsyncRead + Unpin, -{ - fn poll_read( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &mut crate::ReadBuf<'_>, - ) -> std::task::Poll> { - log::debug!("aes decrypt buf: {}bytes", buf.len()); - - if !self.aes_dbuf.is_empty() { - let n = self.aes_dbuf.read_to_buffer(buf.initialize_unfilled()); - buf.advance(n); - return Poll::Ready(Ok(n)); - } else { - self.poll_decrypt_read(cx, buf) - } - } -} - -impl AsyncWrite for AESEncryptor -where - T: AsyncWrite + Unpin, -{ - fn poll_write( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> std::task::Poll> { - log::debug!("aes encrypt data: {}bytes", buf.len()); - - if let Some(ebuf) = self.aes_ebuf.take() { - loop { - let epos = self.aes_epos; - match Pin::new(&mut self.target).poll_write(cx, &ebuf[epos..])? { - Poll::Ready(0) => break Poll::Ready(Ok(0)), - Poll::Ready(n) => { - self.aes_epos += n; - if self.aes_epos == ebuf.len() { - break Poll::Ready(Ok(buf.len())); - } - } - Poll::Pending => { - drop(std::mem::replace(&mut self.aes_ebuf, Some(ebuf))); - break Poll::Pending; - } - } - } - } else { - self.poll_encrypt_write(cx, buf) - } - } - - fn poll_flush( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - Pin::new(&mut self.target).poll_flush(cx) - } - - fn poll_close( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - Pin::new(&mut self.target).poll_close(cx) - } -} - -impl Encrypt for AESEncryptor -where - T: AsyncWrite + Unpin, -{ - fn poll_encrypt_write( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> std::task::Poll> { - let data_len = buf.len() + 4; - let mut encrypted_buf = unsafe { - let mut buf = Vec::with_capacity(data_len + 16); - buf.set_len(data_len + 16); - buf - }; - - encrypted_buf[4..data_len].copy_from_slice(buf); - - let encrypted_len = { - let encrypted = Aes128CbcEnc::new_from_slices(&self.key, &self.iv)? - .encrypt_padded_mut::(&mut encrypted_buf[4..], buf.len())?; - encrypted.len() as u32 - }; - - encrypted_buf[..4].copy_from_slice(&encrypted_len.to_le_bytes()); - - let mut epos = 0; - let encrypted_len = encrypted_len as usize + 4; - - loop { - match Pin::new(&mut self.target).poll_write(cx, &encrypted_buf[epos..encrypted_len])? { - Poll::Ready(0) => break Poll::Ready(Ok(0)), - Poll::Ready(n) => { - epos += n; - if epos == encrypted_len { - break Poll::Ready(Ok(buf.len())); - } - } - Poll::Pending => { - drop(std::mem::replace( - &mut self.aes_ebuf, - Some(encrypted_buf[epos..encrypted_len].to_vec()), - )); - self.aes_epos = 0; - break Poll::Pending; - } - } - } - } -} - -impl Decrypt for AESEncryptor -where - T: AsyncRead + Unpin, -{ - fn poll_decrypt_read( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &mut crate::ReadBuf<'_>, - ) -> std::task::Poll> { - let rbuf = self.aes_rbuf.take(); - let mut rbuf = rbuf.unwrap_or_else(|| { - let mut buf = Vec::new(); - buf.resize(4, 0); - buf - }); - - loop { - let mut rpos = self.aes_rpos; - - if !self.aes_dinit && rpos == rbuf.len() { - let len = unsafe { *(rbuf.as_ptr() as *const u32) } as usize; - rbuf = unsafe { - let mut data_buf = Vec::with_capacity(len); - data_buf.set_len(len); - data_buf - }; - - rpos = 0; - self.aes_rpos = 0; - self.aes_dinit = true; - } else if self.aes_dinit && rpos == rbuf.len() { - self.aes_dinit = false; - self.aes_rpos = 0; - - let rem = buf.remaining(); - let decrypted = Aes128CbcDec::new_from_slices(&self.key, &self.iv)? - .decrypt_padded_mut::(&mut rbuf)?; - if rem >= decrypted.len() { - unsafe { - let unfilled = buf.initialize_unfilled(); - std::ptr::copy(decrypted.as_ptr(), unfilled.as_mut_ptr(), decrypted.len()); - buf.advance(decrypted.len()); - return Poll::Ready(Ok(decrypted.len())); - } - } else { - unsafe { - let unfilled = buf.initialize_unfilled(); - let unfilled_len = unfilled.len(); - std::ptr::copy(decrypted.as_ptr(), unfilled.as_mut_ptr(), unfilled_len); - self.aes_dbuf.push_back(&decrypted[unfilled_len..]); - buf.advance(unfilled_len); - return Poll::Ready(Ok(unfilled_len)); - } - } - } - - let mut read_buf = ReadBuf::new(&mut rbuf[rpos..]); - match Pin::new(&mut self.target).poll_read(cx, &mut read_buf)? { - Poll::Ready(0) => return Poll::Ready(Ok(0)), - Poll::Ready(n) => { - self.aes_rpos += n; - } - Poll::Pending => { - drop(std::mem::replace(&mut self.aes_rbuf, Some(rbuf))); - return Poll::Pending; - } - } - } - } -} diff --git a/src/core/encryption/mod.rs b/src/core/encryption/mod.rs deleted file mode 100644 index 95285af..0000000 --- a/src/core/encryption/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -mod aes; -mod rsa; - -pub use crate::core::encryption::{aes::AESEncryptor, rsa::RSAEncryptor}; - -use std::{ - pin::Pin, - task::{Context, Poll}, -}; - -use crate::ReadBuf; - -pub trait Decrypt { - fn poll_decrypt_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll>; -} - -pub trait Encrypt { - fn poll_encrypt_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll>; -} diff --git a/src/core/encryption/rsa/mod.rs b/src/core/encryption/rsa/mod.rs deleted file mode 100644 index f67dc00..0000000 --- a/src/core/encryption/rsa/mod.rs +++ /dev/null @@ -1,239 +0,0 @@ -use std::{pin::Pin, task::Poll}; - -use rsa::{PaddingScheme, PublicKey, RsaPrivateKey, RsaPublicKey}; - -use crate::{guard::buffer::Buffer, AsyncRead, AsyncWrite, NetSocket, ReadBuf}; - -use super::{Decrypt, Encrypt}; - -pub struct RSAEncryptor { - target: T, - cache: Buffer, - rbuf: Option>, - wbuf: Option>, - wpos: usize, - rpos: usize, - dinit: bool, - rsa_priv: RsaPrivateKey, - rsa_publ: RsaPublicKey, -} - -impl RSAEncryptor { - pub fn new(target: T, publ_key: RsaPublicKey, priv_key: RsaPrivateKey) -> Self { - Self { - target, - rsa_priv: priv_key, - rsa_publ: publ_key, - cache: Default::default(), - rbuf: Default::default(), - wbuf: Default::default(), - wpos: Default::default(), - rpos: Default::default(), - dinit: Default::default(), - } - } -} - -impl NetSocket for RSAEncryptor -where - T: NetSocket, -{ - fn peer_addr(&self) -> crate::Result { - self.target.peer_addr() - } - - fn local_addr(&self) -> crate::Result { - self.target.local_addr() - } -} - -impl AsyncRead for RSAEncryptor -where - T: AsyncRead + Unpin, -{ - fn poll_read( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &mut crate::ReadBuf<'_>, - ) -> std::task::Poll> { - log::debug!("rsa decrypt buf: {}bytes", buf.len()); - - if !self.cache.is_empty() { - let unfilled = buf.initialize_unfilled(); - let len = self.cache.read_to_buffer(unfilled); - buf.advance(len); - return Poll::Ready(Ok(len)); - } else { - self.poll_decrypt_read(cx, buf) - } - } -} - -impl AsyncWrite for RSAEncryptor -where - T: AsyncWrite + Unpin, -{ - fn poll_write( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> std::task::Poll> { - log::debug!("rsa encrypt data: {}bytes", buf.len()); - - if let Some(wbuf) = self.wbuf.take() { - loop { - let pos = self.wpos; - match Pin::new(&mut self.target).poll_write(cx, &wbuf[pos..])? { - Poll::Ready(0) => break Poll::Ready(Ok(0)), - Poll::Ready(n) => { - self.wpos += n; - if self.wpos == wbuf.len() { - break Poll::Ready(Ok(buf.len())); - } - } - Poll::Pending => { - drop(std::mem::replace(&mut self.wbuf, Some(wbuf))); - break Poll::Pending; - } - } - } - } else { - self.poll_encrypt_write(cx, buf) - } - } - - fn poll_flush( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - Pin::new(&mut self.target).poll_flush(cx) - } - - fn poll_close( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - Pin::new(&mut self.target).poll_close(cx) - } -} - -impl Encrypt for RSAEncryptor -where - T: AsyncWrite + Unpin, -{ - fn poll_encrypt_write( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> std::task::Poll> { - let mut rng = rand::thread_rng(); - let ps = PaddingScheme::new_pkcs1v15_encrypt(); - let encrypted_data = self.rsa_publ.encrypt(&mut rng, ps, buf)?; - let encrypted_len = encrypted_data.len() as u32; - let mut encrypted_buf = Vec::new(); - - encrypted_buf.extend(&encrypted_len.to_le_bytes()); - encrypted_buf.extend(encrypted_data); - - let mut pos = 0; - - loop { - match Pin::new(&mut self.target).poll_write(cx, &encrypted_buf[pos..])? { - Poll::Ready(0) => return Poll::Ready(Ok(0)), - std::task::Poll::Ready(n) => { - pos += n; - if pos == encrypted_buf.len() { - break Poll::Ready(Ok(buf.len())); - } - } - std::task::Poll::Pending => { - self.wpos = 0; - drop(std::mem::replace( - &mut self.wbuf, - Some(encrypted_buf[pos..].to_vec()), - )); - break Poll::Pending; - } - } - } - } -} - -impl Decrypt for RSAEncryptor -where - T: AsyncRead + Unpin, -{ - fn poll_decrypt_read( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &mut crate::ReadBuf<'_>, - ) -> std::task::Poll> { - let mut rbuf = self.rbuf.take().unwrap_or_else(|| { - let mut buf = Vec::new(); - buf.resize(4, 0); - buf - }); - - loop { - let mut rpos = self.rpos; - - if rpos == rbuf.len() && !self.dinit { - let len = unsafe { *(rbuf.as_ptr() as *const u32) } as usize; - rbuf = unsafe { - let mut data_buf = Vec::with_capacity(len); - data_buf.set_len(len); - data_buf - }; - - rpos = 0; - self.rpos = 0; - self.dinit = true; - } else if rpos == rbuf.len() && self.dinit { - self.rpos = 0; - self.dinit = false; - - let ps = PaddingScheme::new_pkcs1v15_encrypt(); - let rem = buf.remaining(); - let decrypted = self.rsa_priv.decrypt(ps, &rbuf)?; - - if rem >= decrypted.len() { - unsafe { - let unfilled = buf.initialize_unfilled(); - std::ptr::copy(decrypted.as_ptr(), unfilled.as_mut_ptr(), decrypted.len()); - buf.advance(decrypted.len()); - return Poll::Ready(Ok(decrypted.len())); - } - } else { - unsafe { - { - let unfilled = buf.initialize_unfilled(); - std::ptr::copy( - decrypted.as_ptr(), - unfilled.as_mut_ptr(), - unfilled.len(), - ); - } - - buf.advance(rem); - - self.cache.push_back(&decrypted[rem..]); - - return Poll::Ready(Ok(rem)); - } - } - } - - let mut read_buf = ReadBuf::new(&mut rbuf[rpos..]); - match Pin::new(&mut self.target).poll_read(cx, &mut read_buf)? { - Poll::Ready(0) => return Poll::Ready(Ok(0)), - Poll::Ready(n) => { - self.rpos += n; - } - Poll::Pending => { - drop(std::mem::replace(&mut self.rbuf, Some(rbuf))); - return Poll::Pending; - } - } - } - } -} diff --git a/src/core/future.rs b/src/core/future.rs new file mode 100644 index 0000000..7f8df2e --- /dev/null +++ b/src/core/future.rs @@ -0,0 +1,32 @@ +use std::{ + pin::Pin, + task::{Context, Poll}, +}; + +use std::future::Future; + +pub struct StoredFuture<'a, O>(Option + 'a>>>); + +unsafe impl<'a, O> Send for StoredFuture<'a, O> {} +unsafe impl<'a, O> Sync for StoredFuture<'a, O> {} + +impl<'a, O> StoredFuture<'a, O> { + pub fn poll(&mut self, cx: &mut Context<'_>, f: F) -> Poll + where + F: Fn() -> Fut, + Fut: std::future::Future + 'a, + { + let mut fut = match self.0.take() { + Some(fut) => fut, + None => Box::pin(f()), + }; + + Pin::new(&mut fut).poll(cx) + } +} + +impl<'a, O> StoredFuture<'a, O> { + pub fn new() -> Self { + Self(Default::default()) + } +} diff --git a/src/core/generator.rs b/src/core/generator.rs deleted file mode 100644 index a0170a4..0000000 --- a/src/core/generator.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::future::Future; -use std::pin::Pin; - -use std::task::{Context, Poll}; - -pub struct Generate<'a, G>(&'a mut G); - -pub trait Generator { - type Output; - - fn poll_generate(self: Pin<&mut Self>, cx: &mut Context) -> Poll>; -} - -pub trait GeneratorEx: Generator { - fn next<'a>(&'a mut self) -> Generate<'a, Self> - where - Self: Sized, - { - Generate(self) - } -} - -impl<'a, G> Future for Generate<'a, G> -where - G: Generator + Unpin, -{ - type Output = crate::Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> std::task::Poll { - Pin::new(&mut *self.0).poll_generate(cx) - } -} - -impl GeneratorEx for T where T: Generator + Unpin {} diff --git a/src/core/guard/buffer.rs b/src/core/guard/buffer.rs deleted file mode 100644 index 312a9f8..0000000 --- a/src/core/guard/buffer.rs +++ /dev/null @@ -1,105 +0,0 @@ -use std::collections::VecDeque; - -#[derive(Debug, Default)] -pub struct Buffer { - len: usize, - buf: VecDeque>, -} - -impl Buffer -where - T: Clone, -{ - #[inline] - pub fn new() -> Self { - Self { - len: 0, - buf: Default::default(), - } - } - - #[inline] - pub fn is_empty(&self) -> bool { - self.len == 0 - } - - #[inline] - pub fn len(&self) -> usize { - self.len - } - - #[inline] - pub fn clear(&mut self) { - self.buf.clear(); - self.len = 0; - } - - pub fn push_all(&mut self, data: Vec) { - let len = data.len(); - self.buf.push_back(data); - self.len += len; - } - - #[inline] - pub fn push_back(&mut self, data: &[T]) { - self.buf.push_back(data.to_vec()); - self.len += data.len(); - } - - #[inline] - pub fn push_front(&mut self, data: &[T]) { - self.buf.push_front(data.to_vec()); - self.len += data.len(); - } -} - -impl Buffer { - #[inline] - pub fn read_to_buffer(&mut self, target: &mut [u8]) -> usize { - let mut remaining = target.len(); - let mut read_len = 0; - let buf = &mut self.buf; - - loop { - if remaining == 0 { - if self.len != 0 { - self.len -= read_len; - } - break read_len; - } - - let data = buf.pop_front(); - - if data.is_none() { - remaining = 0; - continue; - } - - let data = unsafe { data.unwrap_unchecked() }; - - if data.len() >= remaining { - let n = unsafe { - let ptr = &mut target[read_len..]; - std::ptr::copy((&data[..remaining]).as_ptr(), ptr.as_mut_ptr(), remaining); - remaining - }; - - remaining -= n; - read_len += n; - - if data.len() != n { - buf.push_front(data[n..].to_vec()) - } - } else { - let n = unsafe { - let ptr = &mut target[read_len..]; - std::ptr::copy(data.as_ptr(), ptr.as_mut_ptr(), data.len()); - data.len() - }; - - read_len += n; - remaining -= n; - } - } - } -} diff --git a/src/core/guard/fallback.rs b/src/core/guard/fallback.rs deleted file mode 100644 index 48b154b..0000000 --- a/src/core/guard/fallback.rs +++ /dev/null @@ -1,221 +0,0 @@ -use std::future::Future; - -use std::task::Poll; -use std::{pin::Pin, task::Context}; - -use crate::r#async::{AsyncRead, AsyncWrite}; -use crate::{Kind, NetSocket, Stream}; - -use super::buffer::Buffer; - -pub struct Fallback { - target: T, - strict: bool, - backed_buf: Option>, - marked_buf: Option>, -} - -pub struct Mark<'a, IO>(&'a mut Fallback); - -pub struct Backward<'a, IO>(&'a mut Fallback); - -impl Fallback { - #[inline] - pub fn new(t: T, strict: bool) -> Self { - Fallback { - target: t, - strict: strict, - backed_buf: Default::default(), - marked_buf: Default::default(), - } - } - - pub fn with_strict(t: T) -> Self { - Fallback { - target: t, - strict: true, - backed_buf: Default::default(), - marked_buf: Default::default(), - } - } -} - -impl Fallback { - #[inline] - pub fn into_inner(self) -> T { - self.target - } - - #[inline] - pub fn mark<'a>(&'a mut self) -> Mark<'a, T> - where - Self: Sized + Unpin, - { - Mark(self) - } - - #[inline] - pub fn backward<'a>(&'a mut self) -> Backward<'a, T> - where - Self: Sized + Unpin, - { - Backward(self) - } - - pub fn consume_back_data(&mut self) { - self.marked_buf.take(); - self.backed_buf.take(); - } - - pub fn back_data(&mut self) -> Option> { - self.backed_buf.take().map_or(None, |mut buf| { - let mut backed = Vec::with_capacity(buf.len()); - unsafe { - backed.set_len(buf.len()); - } - - let _ = buf.read_to_buffer(&mut backed); - - if backed.is_empty() { - None - } else { - Some(backed) - } - }) - } -} - -impl NetSocket for Fallback -where - T: Stream, -{ - fn peer_addr(&self) -> crate::Result { - self.target.peer_addr() - } - - fn local_addr(&self) -> crate::Result { - self.target.local_addr() - } -} - -impl AsyncRead for Fallback -where - T: AsyncRead + Unpin, -{ - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut crate::ReadBuf<'_>, - ) -> std::task::Poll> { - let poll = { - match self.backed_buf.take() { - None => Pin::new(&mut self.target).poll_read(cx, buf), - Some(mut backed) => { - let n = backed.read_to_buffer(buf.initialize_unfilled()); - buf.advance(n); - if !backed.is_empty() { - drop(std::mem::replace(&mut self.backed_buf, Some(backed))); - } - Poll::Ready(Ok(n)) - } - } - }; - - match poll { - Poll::Pending => Poll::Pending, - Poll::Ready(Err(e)) => Poll::Ready(Err(e)), - Poll::Ready(Ok(n)) => match self.marked_buf.as_mut() { - None => Poll::Ready(Ok(n)), - Some(marked) => { - marked.push_back(buf.iter_mut()); - Poll::Ready(Ok(n)) - } - }, - } - } -} - -impl AsyncWrite for Fallback -where - T: AsyncWrite + Unpin, -{ - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> std::task::Poll> { - if self.strict && (self.backed_buf.is_some() || self.marked_buf.is_some()) { - log::warn!("write data in strict mode while buffer data has not been processed"); - return Poll::Ready(Err(Kind::Fallback.into())); - } else { - drop(self.marked_buf.take()); - drop(self.backed_buf.take()); - } - - Pin::new(&mut self.target).poll_write(cx, buf) - } - - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> std::task::Poll> { - Pin::new(&mut self.target).poll_flush(cx) - } - - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> std::task::Poll> { - Pin::new(&mut self.target).poll_close(cx) - } -} - -impl<'a, IO> Future for Mark<'a, IO> -where - IO: Unpin, -{ - type Output = crate::Result<()>; - - fn poll( - mut self: std::pin::Pin<&mut Self>, - _: &mut std::task::Context<'_>, - ) -> std::task::Poll { - let this = &mut self.0; - - if !this.marked_buf.is_some() { - drop(std::mem::replace(&mut this.marked_buf, Some(Buffer::new()))); - } - - Poll::Ready(Ok(())) - } -} - -impl<'a, IO> Future for Backward<'a, IO> -where - IO: Unpin, -{ - type Output = crate::Result<()>; - - fn poll( - mut self: std::pin::Pin<&mut Self>, - _: &mut std::task::Context<'_>, - ) -> std::task::Poll { - let this = &mut self.0; - if let Some(mut marked) = this.marked_buf.take() { - let backed_buf = if let Some(mut backed) = this.backed_buf.take() { - let mut buf = Vec::with_capacity(marked.len()); - unsafe { - buf.set_len(marked.len()); - } - marked.read_to_buffer(&mut buf); - backed.push_all(buf); - backed - } else { - marked - }; - drop(std::mem::replace(&mut this.backed_buf, Some(backed_buf))); - } - - Poll::Ready(Ok(())) - } -} diff --git a/src/core/guard/mod.rs b/src/core/guard/mod.rs deleted file mode 100644 index 5ceaf7c..0000000 --- a/src/core/guard/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod buffer; - -mod fallback; -pub use fallback::*; - -mod timer; -pub use timer::*; diff --git a/src/core/guard/timer.rs b/src/core/guard/timer.rs deleted file mode 100644 index 90c9a48..0000000 --- a/src/core/guard/timer.rs +++ /dev/null @@ -1,222 +0,0 @@ -use std::cell::RefCell; -use std::rc::Rc; -use std::{future::Future, pin::Pin, task::Poll, time::Duration}; - -use crate::{AsyncRead, AsyncWrite, BoxedFuture}; - -pub struct Timer { - target: Rc>, - read_timeout: Option, - write_timeout: Option, - read_fut: Option>>, - write_fut: Option>>, -} - -unsafe impl Send for Timer {} -unsafe impl Sync for Timer {} - -impl Timer -where - T: 'static, -{ - pub fn into_inner(self) -> T { - unsafe { self.target.as_ptr().read() } - } - - pub fn new(target: T, read_timeout: Option, write_timeout: Option) -> Self { - Self { - target: Rc::new(RefCell::new(target)), - read_timeout, - write_timeout, - read_fut: None, - write_fut: None, - } - } - - pub fn with_read_write(target: T, timeout: Duration) -> Self { - log::debug!( - "[timer] overtime time read={}ms, write={}ms", - timeout.as_millis(), - timeout.as_millis() - ); - - Self { - target: Rc::new(RefCell::new(target)), - read_fut: None, - write_fut: None, - read_timeout: Some(timeout), - write_timeout: Some(timeout), - } - } - - pub fn with_read(target: T, timeout: Duration) -> Self { - log::debug!("[timer] overtime time read={}ms", timeout.as_millis()); - - Self { - target: Rc::new(RefCell::new(target)), - read_fut: None, - write_fut: None, - read_timeout: Some(timeout), - write_timeout: None, - } - } - - pub fn with_write(target: T, timeout: Duration) -> Self { - log::debug!("[timer] overtime time write={}ms", timeout.as_millis()); - - Self { - target: Rc::new(RefCell::new(target)), - read_fut: None, - write_fut: None, - read_timeout: None, - write_timeout: Some(timeout), - } - } - - pub fn read_timeout(mut self, timeout: Duration) -> Self { - self.read_timeout = Some(timeout); - self - } - - pub fn write_timeout(mut self, timeout: Duration) -> Self { - self.write_timeout = Some(timeout); - self - } - - pub fn reset_read_timeout(mut self) -> Self { - self.read_timeout = None; - self - } - - pub fn reset_write_timeout(mut self) -> Self { - self.write_timeout = None; - self - } -} - -#[allow(unused)] -impl AsyncWrite for Timer -where - T: AsyncWrite + Unpin + 'static, -{ - fn poll_write( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> std::task::Poll> { - log::debug!("poll timer write"); - let timeout = match self.read_timeout.as_ref() { - None => { - let mut target = self.target.try_borrow_mut()?; - return Pin::new(&mut *target).poll_write(cx, buf); - } - Some(timeout) => timeout.clone(), - }; - - let target = self.target.clone(); - let len = buf.len(); - let buf = buf.as_ptr(); - - let mut fut: BoxedFuture<'static, crate::Result> = match self.write_fut.take() { - Some(fut) => fut, - None => Box::pin(async move { - // let mut target = target.try_borrow_mut()?; - // let buf = unsafe { - // match std::ptr::slice_from_raw_parts(buf, len).as_ref() { - // Some(buf) => buf, - // None => return Err(crate::error::Kind::Memory.into()), - // } - // }; - - // match time::wait_for(timeout, target.write(buf)).await { - // Ok(ready) => ready, - // Err(e) => Err({ - // log::warn!("[timer] write timeout {}", e); - // std::io::ErrorKind::TimedOut.into() - // }), - // } - unimplemented!() - }), - }; - - match Pin::new(&mut fut).poll(cx) { - Poll::Ready(ready) => Poll::Ready(ready), - Poll::Pending => { - drop(std::mem::replace(&mut self.read_fut, Some(fut))); - Poll::Pending - } - } - } - - fn poll_flush( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - let mut target = self.target.try_borrow_mut()?; - return Pin::new(&mut *target).poll_flush(cx); - } - - fn poll_close( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - let mut target = self.target.try_borrow_mut()?; - return Pin::new(&mut *target).poll_close(cx); - } -} - -#[allow(unused)] -impl AsyncRead for Timer -where - T: AsyncRead + Unpin + 'static, -{ - fn poll_read( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &mut crate::ReadBuf<'_>, - ) -> std::task::Poll> { - log::debug!("poll timer read"); - let timeout = match self.read_timeout.as_ref() { - None => { - let mut target = self.target.try_borrow_mut()?; - return Pin::new(&mut *target).poll_read(cx, buf); - } - Some(timeout) => timeout.clone(), - }; - - let target = self.target.clone(); - let buf = buf.initialize_unfilled(); - let len = buf.len(); - let buf = buf.as_mut_ptr(); - - let mut fut: BoxedFuture<'static, crate::Result> = match self.read_fut.take() { - Some(fut) => fut, - None => Box::pin(async move { - let mut target = target.try_borrow_mut()?; - let buf = unsafe { - match std::ptr::slice_from_raw_parts_mut(buf, len).as_mut() { - Some(buf) => buf, - None => return Err(crate::error::Kind::Memory.into()), - } - }; - - // match time::wait_for(timeout, target.read(buf)).await { - // Ok(ready) => ready, - // Err(e) => Err({ - // log::warn!("[timer] read timeout {}", e); - // std::io::ErrorKind::TimedOut.into() - // }), - // } - unimplemented!() - }), - }; - - match Pin::new(&mut fut).poll(cx) { - Poll::Ready(ready) => Poll::Ready(ready), - Poll::Pending => { - drop(std::mem::replace(&mut self.read_fut, Some(fut))); - Poll::Pending - } - } - } -} diff --git a/src/core/handshake.rs b/src/core/handshake.rs new file mode 100644 index 0000000..ea5a1d2 --- /dev/null +++ b/src/core/handshake.rs @@ -0,0 +1,46 @@ +use std::pin::Pin; + +use crate::{ + config::Crypto, + core::{ + io::AsyncReadExt, + stream::{compress, crypto}, + }, + error, +}; + +use super::{ + io::{AsyncRead, AsyncWrite}, + BoxedFuture, BoxedStream, +}; + +pub trait Handshake<'l> { + type C; + type Output; + fn do_handshake(self, conf: &'l Self::C) -> Self::Output; +} + +impl<'l, T> Handshake<'l> for T +where + T: AsyncWrite + AsyncRead + Unpin + Send + 'l, +{ + type C = (); + + type Output = BoxedFuture<'l, error::Result>>; + + fn do_handshake(self, conf: &'l Self::C) -> Self::Output { + Box::pin(async move { + let stream = crypto::encrypt_stream( + compress::compress_stream( + self, + vec![crate::config::Compress::Lz4, crate::config::Compress::Lz4], + ) + .await?, + vec![crate::config::Crypto::Aes, crate::config::Crypto::Rsa], + ) + .await?; + + Ok(BoxedStream::new(stream)) + }) + } +} diff --git a/src/core/io/mod.rs b/src/core/io/mod.rs new file mode 100644 index 0000000..41b6b26 --- /dev/null +++ b/src/core/io/mod.rs @@ -0,0 +1,137 @@ +use std::{ + pin::Pin, + task::{Context, Poll}, +}; + +use crate::error; + +pub trait AsyncRead { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll>; +} + +pub trait AsyncWrite { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll>; + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; +} + +pub trait AsyncReadExt: AsyncRead + Unpin { + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Read<'a, Self> + where + Self: Sized, + { + Read { reader: self, buf } + } +} + +pub trait AsyncWriteExt: AsyncWrite + Unpin { + fn write<'a>(&'a mut self, buf: &'a [u8]) -> Write<'a, Self> + where + Self: Sized, + { + Write { writer: self, buf } + } + + fn flush<'a>(&'a mut self) -> Flush<'a, Self> + where + Self: Sized, + { + Flush { writer: self } + } +} + +#[pin_project::pin_project] +pub struct Read<'a, R> { + reader: &'a mut R, + #[pin] + buf: &'a mut [u8], +} + +#[pin_project::pin_project] +pub struct Write<'a, W> { + writer: &'a mut W, + #[pin] + buf: &'a [u8], +} + +#[pin_project::pin_project] +pub struct Flush<'a, W> { + writer: &'a mut W, +} + +impl<'a, R> std::future::Future for Read<'a, R> +where + R: AsyncRead + Unpin, +{ + type Output = error::Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + Pin::new(&mut **this.reader).poll_read(cx, &mut *this.buf) + } +} + +impl<'a, W> std::future::Future for Write<'a, W> +where + W: AsyncWrite + Unpin, +{ + type Output = error::Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + Pin::new(&mut **this.writer).poll_write(cx, *this.buf) + } +} + +impl<'a, W> std::future::Future for Flush<'a, W> +where + W: AsyncWrite + Unpin, +{ + type Output = error::Result<()>; + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Pin::new(&mut *self.writer).poll_flush(cx) + } +} + +impl AsyncWriteExt for T where T: AsyncWrite + Unpin {} + +impl AsyncReadExt for T where T: AsyncRead + Unpin {} + +impl AsyncWrite for &mut T +where + T: AsyncWrite + Unpin, +{ + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut **self).poll_write(cx, buf) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut **self).poll_flush(cx) + } +} + +impl AsyncRead for &mut T +where + T: AsyncRead + Unpin, +{ + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + Pin::new(&mut **self).poll_read(cx, buf) + } +} + \ No newline at end of file diff --git a/src/core/mixing.rs b/src/core/mixing.rs deleted file mode 100644 index 6a25228..0000000 --- a/src/core/mixing.rs +++ /dev/null @@ -1,125 +0,0 @@ -use std::{future::Future, marker::PhantomData, pin::Pin, sync::Arc, task::Poll}; - -use crate::{ - server::ServerBuilder, Accepter, AccepterWrapper, Address, NetSocket, Provider, Socket, -}; - -macro_rules! mix { - ($fut: expr, $futures: expr) => { - match $fut.await { - Ok(a1) => $futures.push(AccepterWrapper::wrap(a1)), - Err(e) => { - if !e.is_not_support() { - return Err(e); - } - } - } - }; -} - -type BoxedFuture = Pin> + Send + 'static>>; - -#[derive(Clone)] -pub struct MixListener { - left: Arc, - right: Arc, - _marked: PhantomData, -} - -pub struct MixAccepter(Vec>); - -impl Provider for MixListener -where - F1: Provider> + Send + Sync + 'static, - F2: Provider> + Send + Sync + 'static, - A1: Accepter + Unpin + Send + 'static, - A2: Accepter + Unpin + Send + 'static, - S: 'static, -{ - type Output = BoxedFuture>; - - fn call(&self, socket: Socket) -> Self::Output { - let f1 = self.left.call(socket.clone()); - let f2 = self.right.call(socket); - - Box::pin(async move { - let mut futures = Vec::new(); - - mix!(f1, futures); - mix!(f2, futures); - - Ok(MixAccepter(futures)) - }) - } -} - -impl NetSocket for MixAccepter { - fn local_addr(&self) -> crate::Result
{ - let mut addrs = Vec::new(); - for accepter in self.0.iter() { - match accepter.local_addr()? { - Address::One(socket) => addrs.push(socket), - Address::Many(sockets) => addrs.extend(sockets), - } - } - - Ok(Address::Many(addrs)) - } - - fn peer_addr(&self) -> crate::Result
{ - let mut addrs = Vec::new(); - for accepter in self.0.iter() { - match accepter.peer_addr()? { - Address::One(socket) => addrs.push(socket), - Address::Many(sockets) => addrs.extend(sockets), - } - } - - Ok(Address::Many(addrs)) - } -} - -impl Accepter for MixAccepter -where - S: Unpin, -{ - type Stream = S; - - fn poll_accept( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - for accepter in self.0.iter_mut() { - match Pin::new(accepter).poll_accept(cx)? { - Poll::Ready(s) => return Poll::Ready(Ok(s)), - Poll::Pending => {} - } - } - - Poll::Pending - } -} - -impl ServerBuilder -where - P: Provider> + Send + Sync + 'static, - A: Accepter + Unpin + Send + 'static, -{ - pub fn add_accepter(self, provider: P1) -> ServerBuilder, S, O> - where - P1: Provider> + Send + Sync + 'static, - A1: Accepter + Unpin + Send + 'static, - { - ServerBuilder { - executor: self.executor, - handshake: self.handshake, - observer: self.observer, - is_mixed: true, - server_provider: Arc::new(MixListener { - left: self.server_provider, - right: Arc::new(provider), - _marked: PhantomData, - }), - } - } -} diff --git a/src/core/mod.rs b/src/core/mod.rs index d2e2d87..78867e5 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,186 +1,62 @@ -mod controller; -pub use controller::*; - -mod provider; -pub use provider::*; - -mod observer; -mod processor; - -pub use processor::*; - -pub use observer::*; -pub mod compress; - -mod accepter; -pub use accepter::*; - -mod boxed; -pub use boxed::*; - -mod pool; -pub use pool::*; - -mod socket; -use serde::{Deserialize, Serialize}; -pub use socket::*; - -pub mod encryption; -pub mod generator; -pub mod guard; -pub mod mixing; -pub mod protocol; - -use std::marker::PhantomData; -use std::sync::Arc; -use std::{fmt::Display, pin::Pin}; - -use std::future::Future; - -pub struct Fuso(pub(crate) T); - -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub enum Arch { - X86, - X86_64, - Mips, - Arm, - AArch64, -} - -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub enum Platform { - Linux(Arch), - Macos(Arch), - Windows(Arch), - Android(Arch), -} - -impl Default for Arch { - #[cfg(target_arch = "x86")] - fn default() -> Self { - Self::X86 - } - - #[cfg(target_arch = "x86_64")] - fn default() -> Self { - Self::X86_64 - } - - #[cfg(target_arch = "mips")] - fn default() -> Self { - Self::Mips - } - - #[cfg(target_arch = "arm")] - fn default() -> Self { - Self::Arm - } - - #[cfg(target_arch = "aarch64")] - fn default() -> Self { - Self::AArch64 - } -} - -impl Default for Platform { - #[cfg(target_os = "windows")] - fn default() -> Self { - Self::Windows(Default::default()) - } - - #[cfg(target_os = "macos")] - fn default() -> Self { - Self::Macos(Default::default()) - } - - #[cfg(target_os = "linux")] - fn default() -> Self { - Self::Linux(Default::default()) - } - - #[cfg(target_os = "android")] - fn default() -> Self { - Self::Android(Default::default()) - } -} - -pub struct Serve { - pub(crate) fut: Pin> + 'static>>, -} - -unsafe impl Send for Serve {} - -unsafe impl Sync for Serve {} - -pub struct Task { - pub abort_fn: Option>, - pub detach_fn: Option>, - pub _marked: PhantomData, -} - -unsafe impl Sync for Task {} - -pub trait ResultDisplay { - fn display(&self) -> String; -} - -pub trait Executor { - fn spawn(&self, fut: F) -> Task - where - F: Future + Send + 'static, - O: Send + 'static; -} - -impl Executor for Arc -where - E: Executor + Send + ?Sized, -{ - fn spawn(&self, fut: F) -> Task - where - F: Future + Send + 'static, - O: Send + 'static, - { - (**self).spawn(fut) - } -} - -impl Future for Fuso { - type Output = crate::Result<()>; - - fn poll( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - Pin::new(&mut self.0.fut).poll(cx) - } -} - -impl Task { - pub fn abort(&mut self) { - if let Some(abort_callback) = self.abort_fn.take() { - abort_callback() - } - } -} - -impl Drop for Task { - fn drop(&mut self) { - if let Some(detach_callback) = self.detach_fn.take() { - detach_callback() - } - } -} - -impl ResultDisplay for std::result::Result -where - T: Display, - E: Display, -{ - fn display(&self) -> String { - match self { - Ok(fmt) => format!("{}", fmt), - Err(fmt) => format!("err: {}", fmt), - } - } -} +use std::{future::Future, pin::Pin}; + +pub mod io; +pub mod net; +pub mod rpc; +pub mod task; +pub mod split; +pub mod future; +pub mod stream; +pub mod accepter; +pub mod handshake; + + +pub type BoxedFuture<'a, O> = Pin + Send + 'a>>; + +pub trait Provider { + type Arg; + + fn call(arg: Self::Arg) -> R; +} + +pub trait Stream: io::AsyncRead + io::AsyncWrite {} + +pub struct BoxedStream<'a>(Box); + +impl<'a> BoxedStream<'a> { + pub fn new(stream: S) -> Self + where + S: Stream + Unpin + Send + 'a, + { + Self(Box::new(stream)) + } +} + +impl Stream for T where T: io::AsyncRead + io::AsyncWrite + Unpin {} + +impl<'a> io::AsyncRead for BoxedStream<'a> { + fn poll_read( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + Pin::new(&mut *self.0).poll_read(cx, buf) + } +} + +impl<'a> io::AsyncWrite for BoxedStream<'a> { + fn poll_write( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + Pin::new(&mut *self.0).poll_write(cx, buf) + } + + fn poll_flush( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + Pin::new(&mut *self.0).poll_flush(cx) + } +} diff --git a/src/core/net/kcp.rs b/src/core/net/kcp.rs new file mode 100644 index 0000000..43807ea --- /dev/null +++ b/src/core/net/kcp.rs @@ -0,0 +1,121 @@ +use std::{ + net::SocketAddr, + pin::Pin, + sync::Arc, + task::{Context, Poll}, +}; + +use crate::{ + core::{ + accepter::Accepter, + future::StoredFuture, + io::{AsyncRead, AsyncWrite}, + }, + error, +}; + +use super::{UdpProvider, UdpSocket}; + +pub trait KcpProvider: UdpProvider { + type Runtime: kcp_rust::KcpRuntime; +} + +#[pin_project::pin_project] +pub struct KcpListener { + #[pin] + pub(crate) inner: Arc>>, + pub(crate) stored: StoredFuture< + 'static, + std::io::Result<(SocketAddr, kcp_rust::KcpStream)>, + >, +} + +pub enum KcpStream { + Client(kcp_rust::KcpStream), + Server(kcp_rust::KcpStream), +} + +impl KcpListener { + pub async fn bind(conf: kcp_rust::Config, addr: A) -> error::Result + where + P: KcpProvider, + A: Into, + { + let udp = UdpSocket::bind::(addr).await?; + + let listener = kcp_rust::KcpListener::new::(udp, conf)?; + + Ok(KcpListener { + inner: Arc::new(listener), + stored: StoredFuture::new(), + }) + } +} + +impl Accepter for KcpListener { + type Output = (SocketAddr, KcpStream); + + fn poll_accept( + self: std::pin::Pin<&mut Self>, + ctx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let this = self.project(); + let poll = this.stored.poll(ctx, || { + let listener = this.inner.clone(); + async move { + let (_, addr, stream) = listener.accept().await?; + Ok((addr, stream)) + } + })?; + + match poll { + Poll::Pending => Poll::Pending, + Poll::Ready((addr, stream)) => Poll::Ready(Ok((addr, KcpStream::Server(stream)))), + } + } +} + +impl AsyncRead for KcpStream { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + match &mut *self { + KcpStream::Client(s) => { + kcp_rust::AsyncRead::poll_read(Pin::new(s), cx, buf).map_err(Into::into) + } + KcpStream::Server(s) => { + kcp_rust::AsyncRead::poll_read(Pin::new(s), cx, buf).map_err(Into::into) + } + } + } +} + +impl AsyncWrite for KcpStream { + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match &mut *self { + KcpStream::Client(s) => { + kcp_rust::AsyncWrite::poll_flush(Pin::new(s), cx).map_err(Into::into) + } + KcpStream::Server(s) => { + kcp_rust::AsyncWrite::poll_flush(Pin::new(s), cx).map_err(Into::into) + } + } + } + + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + match &mut *self { + KcpStream::Client(s) => { + kcp_rust::AsyncWrite::poll_write(Pin::new(s), cx, buf).map_err(Into::into) + } + KcpStream::Server(s) => { + kcp_rust::AsyncWrite::poll_write(Pin::new(s), cx, buf).map_err(Into::into) + } + } + } +} diff --git a/src/core/net/mod.rs b/src/core/net/mod.rs new file mode 100644 index 0000000..21d2b67 --- /dev/null +++ b/src/core/net/mod.rs @@ -0,0 +1,7 @@ +mod tcp; +mod kcp; +mod udp; + +pub use tcp::*; +pub use udp::*; +pub use kcp::*; diff --git a/src/core/net/tcp.rs b/src/core/net/tcp.rs new file mode 100644 index 0000000..53d6f7c --- /dev/null +++ b/src/core/net/tcp.rs @@ -0,0 +1,96 @@ +use std::{net::SocketAddr, pin::Pin, task::Poll}; + +use crate::error; + +use crate::core::{ + accepter::{Accepter, BoxedAccepter}, + io::{AsyncRead, AsyncWrite}, + BoxedFuture, BoxedStream, Provider, +}; + +pub trait TcpProvider { + type Connector: Provider< + BoxedFuture<'static, error::Result>>, + Arg = SocketAddr, + >; + + type Listener: Provider< + BoxedFuture< + 'static, + error::Result)>>, + >, + Arg = SocketAddr, + >; +} + +pub struct TcpListener { + pub(crate) accepter: BoxedAccepter<'static, (SocketAddr, BoxedStream<'static>)>, +} + +pub struct TcpStream { + pub(crate) stream: BoxedStream<'static>, +} + +impl TcpStream { + pub async fn connect(addr: A) -> error::Result + where + P: TcpProvider, + A: Into, + { + Ok(TcpStream { + stream: P::Connector::call(addr.into()).await?, + }) + } +} + +impl TcpListener { + pub async fn bind(addr: A) -> error::Result + where + P: TcpProvider, + A: Into, + { + Ok(TcpListener { + accepter: P::Listener::call(addr.into()).await?, + }) + } +} + +impl AsyncWrite for TcpStream { + fn poll_write( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + Pin::new(&mut self.stream).poll_write(cx, buf) + } + + fn poll_flush( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + Pin::new(&mut self.stream).poll_flush(cx) + } +} + +impl AsyncRead for TcpStream { + fn poll_read( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + Pin::new(&mut self.stream).poll_read(cx, buf) + } +} + +impl Accepter for TcpListener { + type Output = (SocketAddr, TcpStream); + fn poll_accept( + mut self: Pin<&mut Self>, + ctx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match Pin::new(&mut self.accepter).poll_accept(ctx)? { + std::task::Poll::Pending => Poll::Pending, + std::task::Poll::Ready((addr, stream)) => Poll::Ready(Ok((addr, TcpStream { stream }))), + } + } +} diff --git a/src/core/net/udp.rs b/src/core/net/udp.rs new file mode 100644 index 0000000..3f6bab7 --- /dev/null +++ b/src/core/net/udp.rs @@ -0,0 +1,165 @@ +use std::{net::SocketAddr, pin::Pin}; + +use crate::{core::{BoxedFuture, Provider}, error}; + +pub type BoxedDatagram<'a> = Box; + +pub trait UdpProvider { + type Binder: Provider>>, Arg = SocketAddr>; + type Connect: Provider>>, Arg = SocketAddr>; +} + +pub struct UdpSocket<'a> { + inner: BoxedDatagram<'a>, +} + +pub trait AsyncRecvfrom { + fn poll_recvfrom( + &mut self, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll>; +} + +pub trait AsyncSendTo { + fn poll_sendto( + &mut self, + cx: &mut std::task::Context<'_>, + addr: &std::net::SocketAddr, + buf: &[u8], + ) -> std::task::Poll>; +} + +pub trait AsyncRecv { + fn poll_recv( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll>; +} + +pub trait AsyncSend { + fn poll_send( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll>; +} + +pub trait Datagram: AsyncRecvfrom + AsyncSendTo + AsyncRecv + AsyncSend { + fn boxed_clone(&self) -> Box; +} + + + +impl<'a> kcp_rust::AsyncRecvfrom for UdpSocket<'a> { + fn poll_recvfrom( + &mut self, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + AsyncRecvfrom::poll_recvfrom(self, cx, buf) + } +} + +impl<'a> kcp_rust::AsyncSendTo for UdpSocket<'a> { + fn poll_sendto( + &mut self, + cx: &mut std::task::Context<'_>, + addr: &std::net::SocketAddr, + buf: &[u8], + ) -> std::task::Poll> { + AsyncSendTo::poll_sendto(self, cx, addr, buf) + } +} + +impl<'a> kcp_rust::AsyncRecv for UdpSocket<'a> { + fn poll_recv( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + AsyncRecv::poll_recv(self, cx, buf) + } +} + +impl<'a> kcp_rust::AsyncSend for UdpSocket<'a> { + fn poll_send( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + AsyncSend::poll_send(self, cx, buf) + } +} + +impl<'a> AsyncRecvfrom for UdpSocket<'a> { + fn poll_recvfrom( + &mut self, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + Box::pin(&mut *self.inner).poll_recvfrom(cx, buf) + } +} + +impl<'a> AsyncSendTo for UdpSocket<'a> { + fn poll_sendto( + &mut self, + cx: &mut std::task::Context<'_>, + addr: &std::net::SocketAddr, + buf: &[u8], + ) -> std::task::Poll> { + self.inner.poll_sendto(cx, addr, buf) + } +} + +impl<'a> AsyncRecv for UdpSocket<'a> { + fn poll_recv( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + Pin::new(&mut *self.inner).poll_recv(cx, buf) + } +} + +impl<'a> AsyncSend for UdpSocket<'a> { + fn poll_send( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + Pin::new(&mut *self.inner).poll_send(cx, buf) + } +} + +impl<'a> Clone for UdpSocket<'a> { + fn clone(&self) -> Self { + Self { + inner: self.inner.boxed_clone(), + } + } +} + +impl<'a> UdpSocket<'a> { + pub async fn bind(addr: A) -> error::Result + where + P: UdpProvider, + A: Into, + { + Ok(UdpSocket { + inner: P::Binder::call(addr.into()).await?, + }) + } + + pub async fn connect(addr: A) -> error::Result + where + P: UdpProvider, + A: Into, + { + Ok(UdpSocket { + inner: P::Connect::call(addr.into()).await?, + }) + } +} diff --git a/src/core/observer.rs b/src/core/observer.rs deleted file mode 100644 index 0aa17b8..0000000 --- a/src/core/observer.rs +++ /dev/null @@ -1,89 +0,0 @@ -use std::{sync::Arc, time::Instant}; - -use crate::Address; - -pub trait Observer { - #[inline] - fn on_connect(&self, address: &Address) - where - Self: Sized, - { - #[cfg(debug_assertions)] - log::debug!("on_connect {}", address); - } - - #[inline] - fn on_handshake(&self, address: &Address) - where - Self: Sized, - { - #[cfg(debug_assertions)] - log::debug!("on_handshake {}", address); - } - - #[inline] - fn on_stop(&self, time: Instant, address: &Address) - where - Self: Sized, - { - #[cfg(debug_assertions)] - log::debug!("on_stop {:?} {}", time.elapsed(), address) - } - - #[inline] - fn on_error(&self, error: &crate::Error, address: &Address) - where - Self: Sized, - { - #[cfg(debug_assertions)] - log::debug!("on_error {:?} {}", error, address) - } -} - -impl Observer for Option -where - T: Observer, -{ - #[inline] - fn on_connect(&self, address: &Address) { - self.as_ref().map(|obs| obs.on_connect(address)); - } - - #[inline] - fn on_error(&self, error: &crate::Error, address: &Address) - where - Self: Sized, - { - self.as_ref().map(|obs| obs.on_error(error, address)); - } - - #[inline] - fn on_stop(&self, time: Instant, address: &Address) { - self.as_ref().map(|obs| obs.on_stop(time, address)); - } -} - -impl Observer for () {} - -impl Observer for Arc -where - T: Observer, -{ - #[inline] - fn on_stop(&self, time: Instant, address: &Address) { - (**self).on_stop(time, address) - } - - #[inline] - fn on_connect(&self, address: &Address) { - (**self).on_connect(address) - } - - #[inline] - fn on_error(&self, error: &crate::Error, address: &Address) - where - Self: Sized, - { - (**self).on_error(error, address) - } -} diff --git a/src/core/pool/mod.rs b/src/core/pool/mod.rs deleted file mode 100644 index f40a9d5..0000000 --- a/src/core/pool/mod.rs +++ /dev/null @@ -1,85 +0,0 @@ -#[allow(unused)] - -mod stream; -use stream::*; - -use crate::{ - protocol::AsyncRecvPacket, sync::Mutex, FusoStream, Provider, Socket, WrappedProvider, -}; -use std::{collections::VecDeque, future::Future, io, pin::Pin, sync::Arc, task::Poll}; - -use crate::{AsyncRead, AsyncWrite, Executor, Kind, Task}; - -type BoxedFuture = Pin> + Send + 'static>>; - -macro_rules! reset { - () => { - io::Error::from(io::ErrorKind::ConnectionReset).into() - }; -} - -enum RW { - Read, - Write, -} - -pub struct ConnectionPool { - executor: E, - connector: C, - guard_task: Task<()>, - connections: Arc, RW)>>>, -} - -impl ConnectionPool -where - E: Executor + Send + 'static, - S: AsyncRead + AsyncWrite + Unpin + 'static, -{ - pub fn new(executor: E) -> Self { - let connections: Arc, RW)>>> = Default::default(); - - let guard_task = { - let connections = connections.clone(); - executor.spawn(async move { - let mut connections = connections.lock().await; - for (connection, rw) in connections.iter_mut() { - match rw { - RW::Read => todo!(), - RW::Write => todo!(), - } - } - }) - }; - - Self { - executor, - guard_task, - connector: todo!(), - connections, - } - } -} - - - -impl Provider for ConnectionPool, S> -where S: Send + 'static{ - type Output = BoxedFuture; - fn call(&self, socket: Socket) -> Self::Output { - let connector = self.connector.clone(); - let connections = self.connections.clone(); - Box::pin(async move { - match connections.lock().await.pop_front() { - Some((stream, rw)) => { - unimplemented!() - } - None => { - let socket = connector.call(socket).await?; - - } - } - - unimplemented!() - }) - } -} diff --git a/src/core/pool/stream.rs b/src/core/pool/stream.rs deleted file mode 100644 index c6bf51e..0000000 --- a/src/core/pool/stream.rs +++ /dev/null @@ -1,86 +0,0 @@ -use std::{io, pin::Pin, task::Poll}; - -use crate::{AsyncRead, AsyncWrite}; - -macro_rules! reset { - () => { - io::Error::from(io::ErrorKind::ConnectionReset).into() - }; -} - -pub struct PoolStream { - unique: u64, - stream: Option, - retreat_fn: Option>, -} - -unsafe impl Send for PoolStream {} -unsafe impl Sync for PoolStream {} - -impl AsyncRead for PoolStream -where - S: AsyncRead + Unpin, -{ - fn poll_read( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &mut crate::ReadBuf<'_>, - ) -> std::task::Poll> { - match self.stream.as_mut() { - None => Poll::Ready(Err(reset!())), - Some(stream) => Pin::new(stream).poll_read(cx, buf), - } - } -} - -impl AsyncWrite for PoolStream -where - S: AsyncWrite + Unpin, -{ - fn poll_close( - mut self: Pin<&mut Self>, - _: &mut std::task::Context<'_>, - ) -> Poll> { - match self.stream.take() { - None => Poll::Ready(Err(reset!())), - Some(stream) => match self.retreat_fn.take() { - None => Poll::Ready(Err(reset!())), - Some(retreat_fn) => { - retreat_fn(stream, self.unique); - Poll::Ready(Ok(())) - } - }, - } - } - - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> Poll> { - match self.stream.as_mut() { - None => Poll::Ready(Err(reset!())), - Some(stream) => Pin::new(stream).poll_flush(cx), - } - } - - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> Poll> { - match self.stream.as_mut() { - None => Poll::Ready(Err(reset!())), - Some(stream) => Pin::new(stream).poll_write(cx, buf), - } - } -} - -impl Drop for PoolStream { - fn drop(&mut self) { - if let Some(stream) = self.stream.take() { - if let Some(retreat_fn) = self.retreat_fn.take() { - retreat_fn(stream, self.unique) - } - } - } -} diff --git a/src/core/processor.rs b/src/core/processor.rs deleted file mode 100644 index 5ea617b..0000000 --- a/src/core/processor.rs +++ /dev/null @@ -1,63 +0,0 @@ -use std::{ops::Deref, pin::Pin, sync::Arc}; - -use crate::{ClientProvider, DecorateProvider, Provider, Socket}; - -type BoxedFuture = Pin> + Send + 'static>>; - -pub struct Processor { - provider: Arc

, - observer: Option>, - decorator: Option>, -} - -impl Processor { - pub fn new( - provider: Arc

-where - P: Provider>, - O: Send + 'static -{ - type Output = BoxedFuture; - fn call(&self, t: O) -> Self::Output { - match self { - None => Box::pin(async move { Ok(t) }), - Some(provider) => provider.call(t), - } - } -} - -impl Clone for WrappedProvider { - #[inline] - fn clone(&self) -> Self { - Self { - provider: self.provider.clone(), - } - } -} - -impl Clone for DecorateProvider { - fn clone(&self) -> Self { - Self { - provider: self.provider.clone(), - } - } -} - -impl Clone for ProviderChain { - #[inline] - fn clone(&self) -> Self { - Self { - left: self.left.clone(), - right: self.right.clone(), - } - } -} diff --git a/src/core/rpc/mod.rs b/src/core/rpc/mod.rs new file mode 100644 index 0000000..ef2edfd --- /dev/null +++ b/src/core/rpc/mod.rs @@ -0,0 +1,12 @@ +use std::{pin::Pin, task::Poll}; + +use crate::error; + +pub trait AsyncCaller { + fn poll_call(self: Pin<&mut Self>, raw_arg: &[u8]) -> Poll>>; +} + + +pub struct Remote{ + +} \ No newline at end of file diff --git a/src/core/socket.rs b/src/core/socket.rs deleted file mode 100644 index 0ea2e01..0000000 --- a/src/core/socket.rs +++ /dev/null @@ -1,533 +0,0 @@ -use std::{ - fmt::{Debug, Display}, - net::{IpAddr, SocketAddr}, - ops::{Add, Deref, DerefMut}, - str::FromStr, -}; - -use rand::seq::IteratorRandom; -use serde::{Deserialize, Serialize}; - -use crate::{Error, InvalidAddr, Kind}; - -macro_rules! impl_socket { - ($name: ident, $is: ident, $typ: ident) => { - impl Socket { - pub fn $name>(addr: A) -> Self { - Self { - target: addr.into(), - kind: SocketKind::$typ, - origin: None, - is_mixed: false, - } - } - - pub fn $is(&self) -> bool { - self.kind == SocketKind::$typ - } - } - }; -} - -#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] -pub enum Address { - One(Socket), - Many(Vec), -} - -#[derive(Clone, PartialEq, Eq, Deserialize, Serialize, Hash)] -pub enum InnerAddr { - Socket(SocketAddr), - Domain(String, u16), -} - -#[derive(Clone, PartialEq, Eq, Deserialize, Serialize, Hash)] -pub struct Addr(InnerAddr); - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Hash)] -pub enum SocketKind { - Kcp, - Udp, - Tcp, - Quic, - /// udp forward - Ufd, -} - -#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)] -pub struct Socket { - kind: SocketKind, - origin: Option, - target: Addr, - is_mixed: bool, -} - -impl_socket!(udp, is_udp, Udp); -impl_socket!(kcp, is_kcp, Kcp); -impl_socket!(tcp, is_tcp, Tcp); -impl_socket!(quic, is_quic, Quic); -impl_socket!(ufd, is_ufd, Ufd); - -impl From for Addr { - fn from(addr: SocketAddr) -> Self { - Self(InnerAddr::Socket(addr)) - } -} - -impl From<([u8; 4], u16)> for Addr { - fn from(addr: ([u8; 4], u16)) -> Self { - Self(InnerAddr::Socket(SocketAddr::from(addr))) - } -} - -impl From<([u8; 16], u16)> for Addr { - fn from(addr: ([u8; 16], u16)) -> Self { - Self(InnerAddr::Socket(SocketAddr::from(addr))) - } -} - -impl From<(String, u16)> for Addr { - fn from(addr: (String, u16)) -> Self { - Self(InnerAddr::Domain(addr.0, addr.1)) - } -} - -impl From<(IpAddr, u16)> for Addr { - fn from(addr: (IpAddr, u16)) -> Self { - Self(InnerAddr::Socket((addr.0, addr.1).into())) - } -} - -impl From for Addr { - fn from(port: u16) -> Self { - ([0, 0, 0, 0], port).into() - } -} - -impl Display for Address { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let fmt = match self { - Address::One(addr) => format!("{}", addr), - Address::Many(address) => { - format!("{}", { - address - .iter() - .map(ToString::to_string) - .collect::>() - .join(" | ") - }) - } - }; - - write!(f, "{}", fmt) - } -} - -impl FromStr for Addr { - type Err = Error; - fn from_str(s: &str) -> Result { - match s.parse::() { - Ok(socket) => Ok(socket.into()), - Err(e) => { - let index = s - .find(":") - .ok_or_else(|| Error::from(InvalidAddr::Socket(e)))?; - - let (host, port) = s.split_at(index + 1); - let port = port - .parse::() - .map_err(|_| Error::from(InvalidAddr::Domain(format!("{}", s))))?; - - log::debug!("{}:{}", host, port); - - Ok((host.replace(":", ""), port).into()) - } - } - } -} - -impl Debug for Addr { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self) - } -} - -impl Display for Addr { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let fmt = match &self.0 { - InnerAddr::Socket(addr) => { - if addr.ip().is_unspecified() { - format!("[::{}]", addr.port()) - } else { - format!("{}", addr) - } - } - InnerAddr::Domain(domain, port) => format!("{}:{}", domain, port), - }; - write!(f, "{}", fmt) - } -} - -impl SocketKind { - pub fn is_kcp(&self) -> bool { - self == &Self::Kcp - } - - pub fn is_udp(&self) -> bool { - self == &Self::Udp - } - - pub fn is_tcp(&self) -> bool { - self == &Self::Tcp - } - - pub fn is_quic(&self) -> bool { - self == &Self::Quic - } -} - -#[cfg(debug_assertions)] -impl Display for SocketKind { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let fmt = match self { - SocketKind::Kcp => "KCP", - SocketKind::Udp => "UDP", - SocketKind::Tcp => "TCP", - SocketKind::Quic => "QUIC", - SocketKind::Ufd => "UFD", - }; - - write!(f, "{}", fmt) - } -} - -#[cfg(not(debug_assertions))] -impl Display for SocketKind { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let fmt = match self { - SocketKind::Kcp => "K", - SocketKind::Udp => "U", - SocketKind::Tcp => "T", - SocketKind::Quic => "Q", - SocketKind::Ufd => "F", - }; - - write!(f, "{}", fmt) - } -} - -#[cfg(debug_assertions)] -impl Display for Socket { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let fmt_addr = { - if self.is_default() { - format!("--") - } else { - format!("{}", self.addr()) - } - }; - - if self.is_mixed() && !self.is_ufd() { - write!(f, "{} MIX", fmt_addr) - } else { - write!(f, "{} {}", fmt_addr, self.kind) - } - } -} - -#[cfg(not(debug_assertions))] -impl Display for Socket { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let fmt_addr = { - if self.is_default() { - format!("--") - } else { - format!("{}", self.addr()) - } - }; - - if self.is_mixed() && !self.is_ufd() { - write!(f, "{} M", fmt_addr) - } else { - write!(f, " {} {}", fmt_addr, self.kind) - } - } -} - -impl Addr { - pub fn as_string(&self) -> String { - match &self.0 { - InnerAddr::Socket(addr) => format!("{}", addr), - InnerAddr::Domain(host, port) => format!("{}:{}", host, port), - } - } - - pub fn is_ip(&self) -> bool { - match &self.0 { - InnerAddr::Socket(_) => true, - InnerAddr::Domain(_, _) => false, - } - } - - pub fn is_domain(&self) -> bool { - match self.0 { - InnerAddr::Socket(_) => false, - InnerAddr::Domain(_, _) => true, - } - } - - pub fn ip(&self) -> Option { - match &self.0 { - InnerAddr::Socket(addr) => Some(addr.ip()), - InnerAddr::Domain(_, _) => None, - } - } - - pub fn domain(&self) -> Option<&str> { - match &self.0 { - InnerAddr::Socket(_) => None, - InnerAddr::Domain(domain, _) => Some(domain), - } - } - - pub fn is_ip_unspecified(&self) -> bool { - match self.0 { - InnerAddr::Domain(_, _) => false, - InnerAddr::Socket(socket) => socket.ip().is_unspecified(), - } - } - - pub fn port(&self) -> u16 { - match &self.0 { - InnerAddr::Socket(addr) => addr.port(), - InnerAddr::Domain(_, port) => *port, - } - } - - pub fn set_ip>(&mut self, ip: IP) { - self.0 = InnerAddr::Socket(SocketAddr::new(ip.into(), self.port())); - } - - pub fn from_set_host(&mut self, socket: &Addr) { - if socket.is_domain() { - self.set_domain(unsafe { socket.domain().unwrap_unchecked() }); - } else { - self.set_ip(unsafe { socket.ip().unwrap_unchecked() }) - } - } - - pub fn set_domain(&mut self, domain: &str) { - self.0 = InnerAddr::Domain(format!("{}", domain), self.port()); - } - - pub fn set_port(&mut self, new_port: u16) { - match &mut self.0 { - InnerAddr::Socket(socket) => { - socket.set_port(new_port); - } - InnerAddr::Domain(_, old_port) => { - *old_port = new_port; - } - } - } - - pub fn is_default(&self) -> bool { - match &self.0 { - InnerAddr::Domain(_, _) => false, - InnerAddr::Socket(addr) => addr.port() == 0 && addr.ip().is_unspecified(), - } - } - - pub fn inner(&self) -> &InnerAddr { - &self.0 - } - - pub fn into_inner(self) -> InnerAddr { - self.0 - } -} - -impl Socket { - pub fn is_mixed(&self) -> bool { - self.is_mixed - } - - pub fn into_addr(self) -> Addr { - self.target - } - - pub fn addr(&self) -> &Addr { - &self - } - - pub fn kind(&self) -> SocketKind { - self.kind - } - - pub fn set_origin(&mut self, origin: Option) { - self.origin = origin; - } - - pub fn with_kind(mut self, kind: SocketKind) -> Self { - self.kind = kind; - self - } - - pub fn if_stream_mixed(mut self, mixed: bool) -> Self { - self.is_mixed = mixed; - self - } - - pub fn default_or>(self, socket: S) -> Self { - if self.is_default() { - socket - .into() - .if_stream_mixed(self.is_mixed) - .with_kind(self.kind) - } else { - self - } - } -} - -impl Address { - pub fn first_addr(&self) -> Option { - match self { - Address::One(socket) => Some(socket.addr().clone()), - Address::Many(addrs) => addrs.first().map(|socket| socket.addr().clone()), - } - } - - pub fn if_stream_mixed(self, mixed: bool) -> Self { - match self { - Address::One(socket) => Address::One(socket.if_stream_mixed(mixed)), - Address::Many(addrs) => Address::Many( - addrs - .into_iter() - .map(|addr| addr.if_stream_mixed(mixed)) - .collect(), - ), - } - } - - pub fn with_kind(self, kind: SocketKind) -> Self { - match self { - Address::One(socket) => Address::One(socket.with_kind(kind)), - Address::Many(addrs) => { - Address::Many(addrs.into_iter().map(|addr| addr.with_kind(kind)).collect()) - } - } - } - - pub fn set_ip>(&mut self, ip: IP) { - match self { - Address::One(socket) => socket.set_ip(ip), - Address::Many(addrs) => { - let ip: IpAddr = ip.into(); - for addr in addrs { - addr.set_ip(ip.clone()) - } - } - } - } - - pub fn from_set_host(&mut self, addr: &Addr) { - match self { - Address::One(socket) => socket.from_set_host(addr), - Address::Many(addrs) => { - for socket in addrs { - socket.from_set_host(addr) - } - } - } - } - - pub fn is_ip_unspecified(&self) -> bool { - match self { - Address::One(socket) => socket.is_ip_unspecified(), - Address::Many(sockets) => { - if let Some(socket) = sockets.first() { - socket.is_ip_unspecified() - } else { - false - } - } - } - } -} - -impl Deref for Socket { - type Target = Addr; - - fn deref(&self) -> &Self::Target { - &self.target - } -} - -impl DerefMut for Socket { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.target - } -} - -impl Default for Socket { - fn default() -> Self { - Self { - target: 0.into(), - origin: None, - kind: SocketKind::Tcp, - is_mixed: false, - } - } -} - -impl Address { - pub fn select(&self, socket: &Socket) -> crate::Result { - match self { - Address::One(socket) => Ok(socket.clone()), - Address::Many(addrs) => { - let addrs = addrs.into_iter().filter(|addr| match socket.kind() { - SocketKind::Kcp => addr.is_kcp(), - SocketKind::Udp => addr.is_udp(), - SocketKind::Tcp => addr.is_tcp(), - SocketKind::Quic => addr.is_quic(), - SocketKind::Ufd => { - addr.is_kcp() - || addr.is_ufd() - || addr.is_udp() - || addr.is_tcp() - || addr.is_quic() - } - }); - - addrs - .clone() - .filter(|addr| addr.is_kcp()) - .choose(&mut rand::thread_rng()) - .or_else(|| addrs.choose(&mut rand::thread_rng())) - .map(Clone::clone) - .ok_or_else(|| Error::from(Kind::Improper(socket.clone()))) - } - } - } -} - -impl Add for Address { - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - let mut address = Vec::new(); - - match self { - Address::One(socket) => address.push(socket), - Address::Many(sockets) => address = sockets, - } - - match rhs { - Address::One(socket) => address.push(socket), - Address::Many(sockets) => address.extend(sockets), - } - - Self::Many(address) - } -} diff --git a/src/core/split.rs b/src/core/split.rs new file mode 100644 index 0000000..0987db1 --- /dev/null +++ b/src/core/split.rs @@ -0,0 +1,83 @@ +use std::{pin::Pin, sync::Arc}; + +use parking_lot::Mutex; + +use super::io::{AsyncRead, AsyncWrite}; + +pub struct ReadHalf { + reader: Arc>, +} + +pub struct WriteHalf { + writer: Arc>, +} + +pub trait SplitStream: AsyncWrite + AsyncRead { + fn split(self) -> (ReadHalf, WriteHalf) + where + Self: Sized, + { + let stream = Arc::new(Mutex::new(self)); + + ( + ReadHalf { + reader: stream.clone(), + }, + WriteHalf { writer: stream }, + ) + } +} + +impl SplitStream for T where T: AsyncWrite + AsyncRead + Unpin {} + +impl AsyncRead for ReadHalf +where + S: AsyncRead + Unpin, +{ + fn poll_read( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + let mut reader = self.reader.lock(); + Pin::new(&mut *reader).poll_read(cx, buf) + } +} + +impl AsyncWrite for WriteHalf +where + S: AsyncWrite + Unpin, +{ + fn poll_flush( + self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let mut reader = self.writer.lock(); + Pin::new(&mut *reader).poll_flush(cx) + } + + fn poll_write( + self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + let mut reader = self.writer.lock(); + Pin::new(&mut *reader).poll_write(cx, buf) + } +} + +impl Clone for ReadHalf { + fn clone(&self) -> Self { + Self { + reader: self.reader.clone(), + } + } +} + +impl Clone for WriteHalf { + fn clone(&self) -> Self { + Self { + writer: self.writer.clone(), + } + } +} diff --git a/src/core/stream/codec.rs b/src/core/stream/codec.rs new file mode 100644 index 0000000..a518c3d --- /dev/null +++ b/src/core/stream/codec.rs @@ -0,0 +1,171 @@ +use std::{ + pin::Pin, + task::{Context, Poll}, +}; + +use crate::{ + core::{ + io::{AsyncRead, AsyncWrite}, + BoxedStream, + }, + error, +}; + +pub trait AsyncEncoder { + fn poll_encode( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + stream: &mut BoxedStream<'_>, + buf: &[u8], + ) -> Poll>; +} + +pub trait AsyncDecoder { + fn poll_decode( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + stream: &mut BoxedStream<'_>, + buf: &mut [u8], + ) -> Poll>; +} + +pub trait Codec: AsyncDecoder + AsyncEncoder {} + +pub struct BoxedCodec<'a>(pub(crate) Box); + +#[pin_project::pin_project] +pub struct PairCodec<'a> { + #[pin] + pub(crate) first: BoxedCodec<'a>, + pub(crate) second: BoxedCodec<'a>, +} + +#[pin_project::pin_project] +pub struct CodecStream { + #[pin] + codec: C, + stream: S, +} + +pub struct EmptyCodec; + +impl Codec for T where T: AsyncDecoder + AsyncEncoder + Unpin {} + +impl<'a> AsyncEncoder for BoxedCodec<'a> { + #[inline] + fn poll_encode( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + stream: &mut BoxedStream<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut *self.0).poll_encode(cx, stream, buf) + } +} + +impl<'a> AsyncDecoder for BoxedCodec<'a> { + #[inline] + fn poll_decode( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + stream: &mut BoxedStream<'_>, + buf: &mut [u8], + ) -> Poll> { + Pin::new(&mut *self.0).poll_decode(cx, stream, buf) + } +} + +impl<'a> AsyncEncoder for PairCodec<'a> { + #[inline] + fn poll_encode( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + stream: &mut BoxedStream<'_>, + buf: &[u8], + ) -> Poll> { + let mut this = self.project(); + + let mut stream = BoxedStream::new(CodecStream { + stream, + codec: &mut *this.first, + }); + + Pin::new(&mut *this.second).poll_encode(cx, &mut stream, buf) + } +} + +impl<'a> AsyncDecoder for PairCodec<'a> { + #[inline] + fn poll_decode( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + stream: &mut BoxedStream<'_>, + buf: &mut [u8], + ) -> Poll> { + let mut this = self.project(); + + let mut stream = BoxedStream::new(CodecStream { + stream, + codec: &mut *this.first, + }); + + Pin::new(&mut *this.second).poll_decode(cx, &mut stream, buf) + } +} + +impl<'s, 'c> AsyncWrite for CodecStream<&mut BoxedStream<'s>, &mut BoxedCodec<'c>> { + #[inline] + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + let mut this = self.project(); + match Pin::new(&mut **this.codec).poll_encode(cx, &mut this.stream, buf)? { + Poll::Pending => Poll::Pending, + Poll::Ready(_) => Poll::Ready(Ok(buf.len())), + } + } + + #[inline] + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + Pin::new(&mut *this.stream).poll_flush(cx) + } +} + +impl<'s, 'c> AsyncRead for CodecStream<&mut BoxedStream<'s>, &mut BoxedCodec<'c>> { + #[inline] + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + let mut this = self.project(); + Pin::new(&mut **this.codec).poll_decode(cx, &mut *this.stream, buf) + } +} + +impl AsyncEncoder for EmptyCodec { + #[inline] + fn poll_encode( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + stream: &mut BoxedStream<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(stream).poll_write(cx, buf) + } +} + +impl AsyncDecoder for EmptyCodec { + #[inline] + fn poll_decode( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + stream: &mut BoxedStream<'_>, + buf: &mut [u8], + ) -> Poll> { + Pin::new(stream).poll_read(cx, buf) + } +} diff --git a/src/core/stream/compress/lz4/mod.rs b/src/core/stream/compress/lz4/mod.rs new file mode 100644 index 0000000..54b8420 --- /dev/null +++ b/src/core/stream/compress/lz4/mod.rs @@ -0,0 +1,30 @@ +use std::pin::Pin; + +use crate::core::{ + io::{AsyncRead, AsyncWrite}, + stream::codec::{AsyncDecoder, AsyncEncoder}, +}; + +pub struct Lz4Compressor {} + +impl AsyncEncoder for Lz4Compressor { + fn poll_encode( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + stream: &mut crate::core::BoxedStream<'_>, + buf: &[u8], + ) -> std::task::Poll> { + Pin::new(stream).poll_write(cx, buf) + } +} + +impl AsyncDecoder for Lz4Compressor { + fn poll_decode( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + stream: &mut crate::core::BoxedStream<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + Pin::new(stream).poll_read(cx, buf) + } +} diff --git a/src/core/stream/compress/mod.rs b/src/core/stream/compress/mod.rs new file mode 100644 index 0000000..8e8cd41 --- /dev/null +++ b/src/core/stream/compress/mod.rs @@ -0,0 +1,92 @@ +mod lz4; + +use std::pin::Pin; + +use crate::core::stream::codec::{EmptyCodec, PairCodec}; +use crate::{config::Compress, error}; + +use crate::core::{ + io::{AsyncRead, AsyncWrite}, + BoxedStream, +}; + +use super::codec::{AsyncDecoder, AsyncEncoder, BoxedCodec}; + +#[pin_project::pin_project] +pub struct CompressedStream<'a> { + #[pin] + stream: BoxedStream<'a>, + compressor: BoxedCodec<'a>, +} + +pub async fn compress_stream<'a, S>( + stream: S, + compress: Vec, +) -> error::Result> +where + S: AsyncRead + AsyncWrite + Send + Unpin + 'a, +{ + fn use_compress<'a>(compress: &Compress) -> BoxedCodec<'a> { + match compress { + Compress::Lz4 => BoxedCodec(Box::new(lz4::Lz4Compressor {})), + } + } + + let compressor = if compress.is_empty() { + BoxedCodec(Box::new(EmptyCodec)) + } else if compress.len() == 1 { + use_compress(&compress[0]) + } else { + let mut compressor = BoxedCodec(Box::new({ + PairCodec { + first: use_compress(&compress[0]), + second: use_compress(&compress[1]), + } + })); + + for crypto_type in &compress[2..] { + compressor = BoxedCodec(Box::new(PairCodec { + first: compressor, + second: use_compress(crypto_type), + })) + } + + compressor + }; + + Ok(CompressedStream { + stream: BoxedStream::new(stream), + compressor, + }) +} + +impl<'a> AsyncRead for CompressedStream<'a> { + fn poll_read( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + let mut this = self.project(); + Pin::new(&mut *this.compressor).poll_decode(cx, &mut *this.stream, buf) + } +} + + +impl<'a> AsyncWrite for CompressedStream<'a> { + fn poll_flush( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let mut this = self.project(); + Pin::new(&mut *this.stream).poll_flush(cx) + } + + fn poll_write( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + let mut this = self.project(); + Pin::new(&mut *this.compressor).poll_encode(cx, &mut this.stream, buf) + } +} diff --git a/src/core/stream/crypto/aes/mod.rs b/src/core/stream/crypto/aes/mod.rs new file mode 100644 index 0000000..36b595c --- /dev/null +++ b/src/core/stream/crypto/aes/mod.rs @@ -0,0 +1,29 @@ +use std::pin::Pin; + +use crate::core::io::{AsyncRead, AsyncWrite}; + +use super::{AsyncDecrypt, AsyncEncrypt}; + +pub struct AesCrypto {} + +impl AsyncEncrypt for AesCrypto { + fn poll_encrypt( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + stream: &mut crate::core::BoxedStream<'_>, + buf: &[u8], + ) -> std::task::Poll> { + Pin::new(stream).poll_write(cx, buf) + } +} + +impl AsyncDecrypt for AesCrypto { + fn poll_decrypt( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + stream: &mut crate::core::BoxedStream<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + Pin::new(stream).poll_read(cx, buf) + } +} diff --git a/src/core/stream/crypto/mod.rs b/src/core/stream/crypto/mod.rs new file mode 100644 index 0000000..9338c07 --- /dev/null +++ b/src/core/stream/crypto/mod.rs @@ -0,0 +1,143 @@ +mod aes; +mod rsa; + +use std::{ + pin::Pin, + task::{Context, Poll}, +}; + +use crate::core::{ + io::{AsyncRead, AsyncWrite}, + stream::codec::{EmptyCodec, PairCodec}, +}; +use crate::{config::Crypto, core::BoxedStream, error}; + +use super::codec::{AsyncDecoder, AsyncEncoder, BoxedCodec}; + +pub trait AsyncEncrypt { + fn poll_encrypt( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + stream: &mut BoxedStream<'_>, + buf: &[u8], + ) -> Poll>; +} + +pub trait AsyncDecrypt { + fn poll_decrypt( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + stream: &mut BoxedStream<'_>, + buf: &mut [u8], + ) -> Poll>; +} + +pub trait AsyncCrypto: AsyncEncrypt + AsyncDecrypt {} + +#[pin_project::pin_project] +pub struct EncryptedStream<'a> { + #[pin] + stream: BoxedStream<'a>, + crypto: BoxedCodec<'a>, +} + +pub async fn encrypt_stream<'a, S>( + stream: S, + cryptos: Vec, +) -> error::Result> +where + S: AsyncRead + AsyncWrite + Send + Unpin + 'a, +{ + fn use_crypto<'a>(crypto: &Crypto) -> BoxedCodec<'a> { + match crypto { + Crypto::Rsa => BoxedCodec(Box::new(rsa::RsaCrypto {})), + Crypto::Aes => BoxedCodec(Box::new(aes::AesCrypto {})), + } + } + + let crypto = if cryptos.is_empty() { + BoxedCodec(Box::new(EmptyCodec)) + } else if cryptos.len() == 1 { + use_crypto(&cryptos[0]) + } else { + let mut crypto = BoxedCodec(Box::new({ + PairCodec { + first: use_crypto(&cryptos[0]), + second: use_crypto(&cryptos[1]), + } + })); + + for crypto_type in &cryptos[2..] { + crypto = BoxedCodec(Box::new(PairCodec { + first: crypto, + second: use_crypto(crypto_type), + })) + } + + crypto + }; + + Ok(EncryptedStream { + crypto, + stream: BoxedStream::new(stream), + }) +} + +impl AsyncCrypto for T where T: AsyncDecrypt + AsyncEncrypt {} + +impl AsyncDecoder for T +where + T: AsyncDecrypt + Unpin, +{ + fn poll_decode( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + stream: &mut BoxedStream<'_>, + buf: &mut [u8], + ) -> Poll> { + Pin::new(&mut *self).poll_decrypt(cx, stream, buf) + } +} + +impl AsyncEncoder for T +where + T: AsyncEncrypt + Unpin, +{ + fn poll_encode( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + stream: &mut BoxedStream<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut *self).poll_encrypt(cx, stream, buf) + } +} + +impl<'a> AsyncRead for EncryptedStream<'a> { + fn poll_read( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + let mut this = self.project(); + Pin::new(&mut *this.crypto).poll_decode(cx, &mut *this.stream, buf) + } +} + +impl<'a> AsyncWrite for EncryptedStream<'a> { + fn poll_flush( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + self.project().stream.poll_flush(cx) + } + + fn poll_write( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + let mut this = self.project(); + Pin::new(&mut *this.crypto).poll_encode(cx, &mut *this.stream, buf) + } +} diff --git a/src/core/stream/crypto/rsa/mod.rs b/src/core/stream/crypto/rsa/mod.rs new file mode 100644 index 0000000..f7e6976 --- /dev/null +++ b/src/core/stream/crypto/rsa/mod.rs @@ -0,0 +1,29 @@ +use std::pin::Pin; + +use crate::core::io::{AsyncRead, AsyncWrite}; + +use super::{AsyncDecrypt, AsyncEncrypt}; + +pub struct RsaCrypto {} + +impl AsyncEncrypt for RsaCrypto { + fn poll_encrypt( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + stream: &mut crate::core::BoxedStream<'_>, + buf: &[u8], + ) -> std::task::Poll> { + Pin::new(stream).poll_write(cx, buf) + } +} + +impl AsyncDecrypt for RsaCrypto { + fn poll_decrypt( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + stream: &mut crate::core::BoxedStream<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + Pin::new(stream).poll_read(cx, buf) + } +} diff --git a/src/core/stream/fallback.rs b/src/core/stream/fallback.rs new file mode 100644 index 0000000..38ec008 --- /dev/null +++ b/src/core/stream/fallback.rs @@ -0,0 +1,103 @@ +use std::{ + io::{Cursor, Read, Write}, + pin::Pin, + task::Poll, +}; + +use crate::core::io::{AsyncRead, AsyncWrite}; + +pub struct Fallback { + inner: S, + outed: Option>>, + cached: Option>>, +} + +impl Fallback { + pub fn mark(&mut self) { + if self.cached.is_none() { + self.cached.replace(Default::default()); + } + } + + pub fn reset(&mut self) { + if let Some(cached) = self.cached.take() { + match self.outed.as_mut() { + Some(outed) => { + let _ = outed.write_all(&cached.into_inner()); + } + None => { + self.outed.replace(cached); + } + } + } + } + + pub fn into_inner(mut self) -> (Option>, S) { + self.reset(); + match self.outed { + None => (None, self.inner), + Some(outed) => (Some(outed.into_inner()), self.inner), + } + } +} + +impl From for Fallback +where + S: AsyncRead + AsyncWrite + Unpin, +{ + fn from(value: S) -> Self { + Self { + inner: value, + outed: Default::default(), + cached: Default::default(), + } + } +} + +impl AsyncRead for Fallback +where + S: AsyncRead + Unpin, +{ + fn poll_read( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + if let Some(outed) = self.outed.as_mut() { + let n = outed.read(buf)?; + if n > 0 { + return Poll::Ready(Ok(n)); + } + } + + match Pin::new(&mut self.inner).poll_read(cx, buf)? { + Poll::Pending => Poll::Pending, + Poll::Ready(n) => { + if let Some(cached) = self.cached.as_mut() { + cached.write_all(&buf[..n])?; + } + Poll::Ready(Ok(n)) + } + } + } +} + +impl AsyncWrite for Fallback +where + S: AsyncWrite + Unpin, +{ + fn poll_flush( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + Pin::new(&mut self.inner).poll_flush(cx) + } + + fn poll_write( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + Pin::new(&mut self.inner).poll_write(cx, buf) + } +} diff --git a/src/core/stream/mod.rs b/src/core/stream/mod.rs new file mode 100644 index 0000000..dec0f31 --- /dev/null +++ b/src/core/stream/mod.rs @@ -0,0 +1,4 @@ +pub mod codec; +pub mod crypto; +pub mod fallback; +pub mod compress; \ No newline at end of file diff --git a/src/core/task/mod.rs b/src/core/task/mod.rs new file mode 100644 index 0000000..ae9dcde --- /dev/null +++ b/src/core/task/mod.rs @@ -0,0 +1,3 @@ +mod pool; + +pub use pool::*; \ No newline at end of file diff --git a/src/core/task/pool.rs b/src/core/task/pool.rs new file mode 100644 index 0000000..8f9f6e5 --- /dev/null +++ b/src/core/task/pool.rs @@ -0,0 +1,16 @@ +use std::future::Future; + +use crate::error; + +pub struct TaskPool {} + +impl Future for TaskPool { + type Output = error::Result<()>; + + fn poll( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + unimplemented!() + } +} diff --git a/src/error/mod.rs b/src/error/mod.rs index 4995462..9e5153c 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -1,467 +1,22 @@ -use std::fmt::Display; - -use crate::{kcp, Socket, SocketKind}; - -pub type Result = std::result::Result; - -#[derive(Debug)] -pub struct Error { - kind: Kind, -} - -#[derive(Debug)] -pub enum InvalidAddr { - Domain(String), - Socket(std::net::AddrParseError), -} - -#[derive(Debug)] -pub enum SyncErr { - Mutex, -} - -#[derive(Debug)] -pub enum Encoding { - FromUtf8(std::string::FromUtf8Error), -} - -#[derive(Debug)] -pub enum PacketErr { - Head([u8; 4]), -} - -#[derive(Debug)] -pub enum SocksErr { - Protocol, - Authenticate, - InvalidAddress, - BindNotSupport, - Socks5Frg, - Head { ver: u8, nmethod: u8 }, - Method(u8), - BadLength { expect: usize, current: usize }, -} - -#[derive(Debug)] -pub enum Lz4Err { - Compress, - Decompress, - DecodeReleased, - EncodeReleased, -} - -#[derive(Debug)] -pub enum CompressErr { - Lz4(Lz4Err), -} - -#[derive(Debug)] -pub enum SocketErr { - BadAddress(Socket), - NotSupport(Socket), - NotExpected { expect: SocketKind, current: Socket }, -} - -#[derive(Debug)] -pub enum AesErr { - Pad(cbc::cipher::inout::PadError), - UnPad(cbc::cipher::inout::block_padding::UnpadError), - NotEqual(cbc::cipher::inout::NotEqualError), - InvalidLength(cbc::cipher::InvalidLength), -} - -#[derive(Debug)] -pub enum EncryptionErr { - Aes(AesErr), - Rsa(rsa::errors::Error), - RsaPkcs7(rsa::pkcs1::Error), - RsaSpki(rsa::pkcs8::spki::Error), -} - -#[derive(Debug)] -pub enum MixErr { - Unsupported(Socket), -} - -#[derive(Debug)] -pub enum Kind { - Channel, - AlreadyUsed, - IO(std::io::Error), - #[cfg(feature = "fuso-rt-tokio")] - Timeout(tokio::time::error::Elapsed), - #[cfg(feature = "fuso-rt-smol")] - Timeout(std::time::Instant), - Memory, - Mark, - Sync(SyncErr), - Deserialize(String), - InvalidAddr(InvalidAddr), - Encoding(Encoding), - Packet(PacketErr), - Fallback, - Unexpected(String), - Message(String), - Socks(SocksErr), - Once, - BadForward, - Kcp(kcp::KcpErr), - Compress(CompressErr), - Socket(SocketErr), - Encryption(EncryptionErr), - Mix(MixErr), - Unsupported(Socket), - AddressLoop(Socket), - Improper(Socket), - Text(String), - MaxRetries(usize), -} - -impl Display for SyncErr { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", { - match self { - SyncErr::Mutex => "mutex", - } - }) - } -} - -impl Display for InvalidAddr { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", { - match self { - InvalidAddr::Domain(domain) => format!("invalid domain {}", domain), - InvalidAddr::Socket(addr) => format!("parse addr {}", addr), - } - }) - } -} - -impl Display for Encoding { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", { - match self { - Encoding::FromUtf8(utf8) => format!("encoding utf8 {}", utf8), - } - }) - } -} - -impl Display for PacketErr { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", { - match self { - PacketErr::Head(e) => format!("invalid packet head {:?}", e), - } - }) - } -} - -impl Display for SocksErr { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", { - match self { - SocksErr::Protocol => format!("invalid socks5 protocol"), - SocksErr::Authenticate => format!("socks5 authentication failed"), - SocksErr::InvalidAddress => format!("invalid address"), - SocksErr::BindNotSupport => format!("bind not support"), - SocksErr::Socks5Frg => format!("fragment not support"), - SocksErr::Head { ver, nmethod } => { - format!("invalid socks5 head ver={}, nmethod={}", ver, nmethod) - } - SocksErr::Method(e) => format!("method err {}", e), - SocksErr::BadLength { expect, current } => { - format!("bad socks5 packet expect {} , current={}", expect, current) - } - } - }) - } -} - -impl From for Error { - fn from(e: cbc::cipher::inout::NotEqualError) -> Self { - Kind::Encryption(EncryptionErr::Aes(AesErr::NotEqual(e))).into() - } -} - -impl From for Error { - fn from(e: cbc::cipher::inout::PadError) -> Self { - Kind::Encryption(EncryptionErr::Aes(AesErr::Pad(e))).into() - } -} - -impl From for Error { - fn from(e: cbc::cipher::InvalidLength) -> Self { - Kind::Encryption(EncryptionErr::Aes(AesErr::InvalidLength(e))).into() - } -} - -impl From for Error { - fn from(e: cbc::cipher::block_padding::UnpadError) -> Self { - Kind::Encryption(EncryptionErr::Aes(AesErr::UnPad(e))).into() - } -} - -impl From for Error { - fn from(e: rsa::pkcs1::Error) -> Self { - Kind::Encryption(EncryptionErr::RsaPkcs7(e)).into() - } -} - -impl From for Error { - fn from(e: rsa::pkcs8::spki::Error) -> Self { - Kind::Encryption(EncryptionErr::RsaSpki(e)).into() - } -} - -impl Display for SocketErr { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let fmt = match self { - SocketErr::BadAddress(socket) => format!("invalid socket {}", socket), - Self::NotSupport(socket) => format!("socket not support {}", socket), - SocketErr::NotExpected { expect, current } => format!( - "socket not expected ! expect: {}, current: {}, addr: {}", - expect, - current, - current.addr() - ), - }; - - writeln!(f, "{}", fmt) - } -} - -impl Display for AesErr { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", { - match self { - AesErr::Pad(e) => format!("{}", e), - AesErr::UnPad(e) => format!("{}", e), - AesErr::NotEqual(e) => format!("{}", e), - AesErr::InvalidLength(e) => format!("{}", e), - } - }) - } -} - -impl Display for EncryptionErr { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", { - match self { - EncryptionErr::Aes(e) => format!("{}", e), - EncryptionErr::Rsa(e) => format!("{}", e), - EncryptionErr::RsaPkcs7(e) => format!("{}", e), - EncryptionErr::RsaSpki(e) => format!("{}", e), - } - }) - } -} - -impl Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let fmt = match self.kind() { - Kind::Channel => format!("Channel"), - Kind::AlreadyUsed => format!("AlreadyUsed"), - Kind::IO(io) => format!("{}", io), - Kind::Timeout(timeout) => format!("{:?}", timeout), - Kind::Memory => format!(""), - Kind::Mark => format!("mark"), - Kind::Sync(e) => format!("{}", e), - Kind::Deserialize(e) => format!("{}", e), - Kind::InvalidAddr(e) => format!("{}", e), - Kind::Encoding(e) => format!("{}", e), - Kind::Packet(e) => format!("{}", e), - Kind::Fallback => format!("fallback"), - Kind::Unexpected(e) => format!("{}", e), - Kind::Message(message) => format!("{}", message), - Kind::Socks(e) => format!("{}", e), - Kind::Once => format!("call once"), - Kind::BadForward => format!("bad forward"), - Kind::Kcp(e) => format!("{}", e), - Kind::Compress(e) => { - format!("{:?}", e) - } - Kind::Socket(socket) => format!("{}", socket), - Kind::Encryption(e) => format!("{}", e), - Kind::Mix(e) => format!("{:?}", e), - Kind::Unsupported(e) => format!("Unsupported {}", e), - Kind::AddressLoop(e) => format!("address loop {}", e), - Kind::Improper(e) => format!("no suitable ones {}", e), - Kind::Text(txt) => format!("{}", txt), - Kind::MaxRetries(retry) => format!("exceeded maximum number of attempts {}", retry), - }; - write!(f, "{}", fmt) - } -} - -impl From for Error { - fn from(addr: InvalidAddr) -> Self { - Self { - kind: Kind::InvalidAddr(addr), - } - } -} - -impl From for Error { - fn from(s: String) -> Self { - Kind::Text(s).into() - } -} - -impl From for Error { - fn from(e: MixErr) -> Self { - Kind::Mix(e).into() - } -} - -impl From for Error { - fn from(e: rsa::errors::Error) -> Self { - Kind::Encryption(EncryptionErr::Rsa(e)).into() - } -} - -impl From for Error { - fn from(e: SyncErr) -> Self { - Self { - kind: Kind::Sync(e), - } - } -} - -impl From for Error { - fn from(e: SocksErr) -> Self { - Self { - kind: Kind::Socks(e), - } - } -} - -impl From for Error { - fn from(kind: Kind) -> Self { - Self { kind } - } -} - -impl From for Error { - fn from(socket: SocketErr) -> Self { - Self { - kind: Kind::Socket(socket), - } - } -} - -impl From for Error { - fn from(e: std::io::ErrorKind) -> Self { - Kind::IO(e.into()).into() - } -} - -impl From for Error { - fn from(e: Encoding) -> Self { - Kind::Encoding(e).into() - } -} - -impl From for Error { - fn from(e: std::string::FromUtf8Error) -> Self { - Encoding::FromUtf8(e).into() - } -} - -impl From for Error { - fn from(e: std::io::Error) -> Self { - Kind::IO(e).into() - } -} - -impl From for Error { - fn from(e: kcp::KcpErr) -> Self { - Kind::Kcp(e).into() - } -} - -impl From> for Error { - fn from(_: std::sync::PoisonError) -> Self { - SyncErr::Mutex.into() - } -} - -impl From for Error { - fn from(_: std::cell::BorrowMutError) -> Self { - SyncErr::Mutex.into() - } -} - -impl From for Error { - fn from(e: bincode::Error) -> Self { - Kind::Deserialize(e.to_string()).into() - } -} - -#[cfg(feature = "fuso-rt-tokio")] -impl From for Error { - fn from(e: tokio::time::error::Elapsed) -> Self { - Kind::Timeout(e).into() - } -} - -impl From for Error { - fn from(_: async_channel::RecvError) -> Self { - Kind::Channel.into() - } -} - -impl From> for Error { - fn from(_: async_channel::SendError) -> Self { - Kind::Channel.into() - } -} - -impl From for Error { - fn from(e: PacketErr) -> Self { - Kind::Packet(e).into() - } -} - -#[cfg(feature = "fuso-rt-smol")] -impl From for Error { - fn from(e: std::time::Instant) -> Self { - Kind::Timeout(e).into() - } -} - -impl From for Error { - fn from(e: Lz4Err) -> Self { - Kind::Compress(CompressErr::Lz4(e)).into() - } -} - -impl std::error::Error for Error {} - -impl Error { - pub fn kind(&self) -> &Kind { - &self.kind - } - - pub fn is_packet_err(&self) -> bool { - match &self.kind { - Kind::Packet(_) => true, - _ => false, - } - } - - pub fn is_socks_error(&self) -> bool { - match &self.kind { - Kind::Socks(SocksErr::Head { ver: _, nmethod: _ }) => true, - _ => false, - } - } - - pub fn is_not_support(&self) -> bool { - match &self.kind { - Kind::Unsupported(_) => true, - _ => false, - } - } -} +pub type Result = std::result::Result; + + +#[derive(Debug)] +pub enum FusoError{ + TomlDeError(toml::de::Error), + StdIo(std::io::Error), +} + + + +impl From for FusoError{ + fn from(value: std::io::Error) -> Self { + Self::StdIo(value) + } +} + +impl From for FusoError{ + fn from(value: toml::de::Error) -> Self { + Self::TomlDeError(value) + } +} \ No newline at end of file diff --git a/src/http/mod.rs b/src/http/mod.rs deleted file mode 100644 index 8deb2cc..0000000 --- a/src/http/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[cfg(feature = "fuso-api")] -pub mod routes; - -#[cfg(all(feature = "fuso-api", feature = "fuso-web"))] -pub mod pages; diff --git a/src/http/pages/mod.rs b/src/http/pages/mod.rs deleted file mode 100644 index 8b13789..0000000 --- a/src/http/pages/mod.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/http/routes/mod.rs b/src/http/routes/mod.rs deleted file mode 100644 index 38369be..0000000 --- a/src/http/routes/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::{future::Future, pin::Pin}; - -use crate::{server::Server, Controller, Fuso}; - -type BoxedFuture = Pin> + Send + 'static>>; - -pub struct FusoApi {} - -impl Controller for FusoApi { - fn register( - &self, - env: std::sync::Arc, - fut: BoxedFuture<()>, - ) -> crate::Result<()> { - unimplemented!() - } -} - -impl Fuso> { - -} diff --git a/src/lib.rs b/src/lib.rs index 627908e..a3ee7e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,19 +1,26 @@ -mod r#async; -mod core; -mod error; -mod net; -mod runtime; -pub mod observer; +#[cfg(feature = "fuso-cli")] +pub mod cli; + +pub mod core; + +#[cfg(feature = "fuso-config")] +pub mod config; + +pub mod service; -pub mod client; pub mod server; -#[cfg(any(feature = "fuso-web", feature = "fuso-api"))] -pub mod http; +pub mod client; + +pub mod error; + +pub mod runtime; -pub use crate::core::*; -pub use crate::error::*; -pub use crate::r#async::*; -pub use crate::runtime::*; -pub use crate::net::*; +pub mod channel; +pub fn enter_async_main(fut: F) -> error::Result<()> +where + F: std::future::Future> + Send, +{ + runtime::tokio::block_on(fut) +} diff --git a/src/net/kcp/builder.rs b/src/net/kcp/builder.rs deleted file mode 100644 index 64cbeaf..0000000 --- a/src/net/kcp/builder.rs +++ /dev/null @@ -1,93 +0,0 @@ -use std::{future::Future, pin::Pin, task::Poll}; - -use crate::{ - mixing::MixListener, server::ServerBuilder, Accepter, Executor, FusoStream, Kind, - NetSocket, Provider, Socket, SocketKind, ToBoxStream, UdpSocket, WrappedProvider, -}; - -use super::KcpListener; - -type BoxedFuture = Pin> + Send + 'static>>; - -pub struct KcpAccepterProvider { - provider: WrappedProvider, - executor: E, -} - -pub struct KcpAccepter(KcpListener); - -impl NetSocket for KcpAccepter -where - C: NetSocket, -{ - fn peer_addr(&self) -> crate::Result { - self.0 - .core - .peer_addr() - .map(|addr| addr.with_kind(SocketKind::Kcp)) - } - - fn local_addr(&self) -> crate::Result { - self.0 - .core - .local_addr() - .map(|addr| addr.with_kind(SocketKind::Kcp)) - } -} - -impl Accepter for KcpAccepter -where - C: UdpSocket + Clone + Sync + Unpin + Send + 'static, - E: Executor + Clone + Sync + Send + Unpin + 'static, -{ - type Stream = FusoStream; - - fn poll_accept( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> Poll> { - Pin::new(&mut self.0) - .poll_accept(cx)? - .map(|kcp| Ok(kcp.into_boxed_stream())) - } -} - -impl ServerBuilder -where - SP: Provider> + Send + Sync + 'static, - A1: Accepter + Unpin + Send + 'static, - E: Executor + Clone + Sync + Send + Unpin + 'static, -{ - pub fn using_kcp( - self, - provider: F, - executor: E, - ) -> ServerBuilder, FusoStream>, FusoStream, O> - where - F: Provider> + Send + Sync + 'static, - U: UdpSocket + Clone + Sync + Unpin + Send + 'static, - { - self.add_accepter(KcpAccepterProvider { - executor, - provider: WrappedProvider::wrap(provider), - }) - } -} - -impl Provider for KcpAccepterProvider -where - C: UdpSocket + Clone + Sync + Unpin + Send + 'static, - E: Executor + Clone + Sync + Send + 'static, -{ - type Output = BoxedFuture>; - - fn call(&self, socket: Socket) -> Self::Output { - if socket.is_kcp() || socket.is_mixed() { - let fut = self.provider.call(socket); - let executor = self.executor.clone(); - Box::pin(async move { Ok(KcpAccepter(KcpListener::bind(fut.await?, executor)?)) }) - } else { - Box::pin(async move { Err(Kind::Unsupported(socket).into()) }) - } - } -} diff --git a/src/net/kcp/mod.rs b/src/net/kcp/mod.rs deleted file mode 100644 index 4c0bb50..0000000 --- a/src/net/kcp/mod.rs +++ /dev/null @@ -1,824 +0,0 @@ -mod third_party; - -pub use third_party::KcpErr; - -mod stream; -pub use stream::*; - -mod builder; -pub use builder::*; - -use std::{ - collections::{hash_map::DefaultHasher, HashMap}, - future::Future, - hash::{Hash, Hasher}, - net::SocketAddr, - pin::Pin, - sync::Arc, - task::{Poll, Waker}, - time::{Duration, Instant}, -}; - -use async_mutex::Mutex; - -use crate::{ - guard::buffer::Buffer, time, Accepter, Address, Executor, NetSocket, Socket, SocketKind, Task, - UdpReceiverExt, UdpSocket, -}; - -type BoxedFuture = Pin + Send + 'static>>; - -type Manager = Arc>>>>; -type Callback = Option>; - -pub fn now_mills() -> u32 { - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() as u32 -} - -pub enum State { - Open((u64, u32, Session, Address)), - Close(u32), -} - -enum UpdateState { - Updating(Pin> + 'static>>), - WaitUpdate(Pin> + 'static>>), -} - -pub enum KLife { - Dead(Instant, Session), - Active(Session), -} - -pub struct KcpUpdate { - kcore: Arc>>, - kupdate: Option, -} - -#[derive(Debug, Clone)] -pub struct Increment(Arc>); - -pub struct Session { - pub(crate) conv: u32, - pub(crate) target: Option, - pub(crate) kcore: Arc>>, -} - -pub struct KcpListener { - pub(crate) core: C, - pub(crate) manager: Manager, - pub(crate) futures: Vec>>>, - pub(crate) executor: E, -} - -pub struct KcpConnector { - core: C, - task: Task>, - executor: E, - sessions: Arc>>>, - increment: Increment, -} - -impl Session -where - C: UdpSocket + Send + Unpin + 'static, -{ - pub async fn input(&mut self, data: Vec) -> crate::Result<()> { - let mut kcore = self.kcore.lock()?; - let mut next_input = &data[..]; - - while next_input.len() >= third_party::KCP_OVERHEAD { - let n = kcore.input(next_input)?; - next_input = &data[n..]; - } - - if kcore.peeksize().is_ok() { - kcore.try_wake_read(); - } - - if kcore.wait_snd() > 0 { - kcore.try_wake_write(); - } - - Ok(()) - } -} - -impl KcpStream -where - C: UdpSocket + Unpin + Send + Sync + 'static, -{ - pub fn new( - conv: u32, - local_addr: Address, - peer_addr: Address, - kcore: Arc>>, - close_callback: Callback, - ) -> Self { - Self { - conv, - kcore, - local_addr: local_addr.with_kind(SocketKind::Kcp), - peer_addr: peer_addr.with_kind(SocketKind::Kcp), - close_callback, - } - } -} - -impl KcpCore -where - C: UdpSocket + Send + Unpin + 'static, -{ - fn try_wake_read(&mut self) { - if let Some(waker) = self.read_waker.take() { - waker.wake(); - } - } - - fn try_wake_write(&mut self) { - if let Some(waker) = self.write_waker.take() { - waker.wake() - } - } -} - -impl Session -where - C: UdpSocket + Unpin + Send + Sync + 'static, -{ - fn new( - conv: u32, - target: Option, - output: C, - executor: E, - clean_callback: F, - ) -> crate::Result - where - E: Executor + Send + 'static, - F: FnOnce(u32) -> Fut + Sync + Send + 'static, - Fut: Future + Send + 'static, - { - let output = KOutput { - output, - target: target.clone(), - }; - - let mut kcp = third_party::Kcp::new(conv, output); - - kcp.set_wndsize(1024, 1024); - kcp.set_nodelay(true, 20, 2, true); - kcp.set_maximum_resend_times(10); - - let kcore = Arc::new(std::sync::Mutex::new(KcpCore { - kcp, - kbuf: Buffer::new(), - kupdate: None, - write_waker: None, - read_waker: None, - close_waker: None, - })); - - let kupdate = KcpUpdate { - kcore: kcore.clone(), - kupdate: None, - }; - - drop(std::mem::replace( - &mut kcore.lock()?.kupdate, - Some(executor.spawn(async move { - drop(kupdate.await); - log::debug!("stop update conv={}", conv); - clean_callback(conv).await; - Ok(()) - })), - )); - - Ok(Self { - conv, - target, - kcore: kcore.clone(), - }) - } - - fn force_close(&self) -> crate::Result<()> { - self.kcore.lock()?.close() - } - - async fn safe_close(&self) -> crate::Result<()> { - self.kcore.lock()?.close() - } - - fn stream( - &self, - local_addr: Address, - peer_addr: Address, - close_callback: Callback, - ) -> KcpStream { - KcpStream::new( - self.conv, - local_addr, - peer_addr, - self.kcore.clone(), - close_callback, - ) - } -} - -impl KcpListener -where - C: UdpSocket + Send + Sync + Clone + Unpin + 'static, - E: Executor + Clone + Send + Sync + 'static, -{ - pub fn bind(core: C, executor: E) -> crate::Result { - let manager: Manager = Default::default(); - - let core_fut = Box::pin(Self::run_accept( - core.clone(), - manager.clone(), - executor.clone(), - )); - - Ok(Self { - core, - manager, - executor, - futures: vec![core_fut], - }) - } - - async fn run_accept(core: C, manager: Manager, executor: E) -> crate::Result> { - loop { - let mut data = Vec::with_capacity(1500); - - unsafe { - data.set_len(1500); - } - - let (n, addr) = core.recv_from(&mut data).await?; - data.truncate(n); - - let id = { - let mut hasher = DefaultHasher::new(); - addr.hash(&mut hasher); - hasher.finish() - }; - - if n < third_party::KCP_OVERHEAD { - log::warn!("received an invalid kcp packet"); - continue; - } - - let conv = third_party::get_conv(&data); - - let mut sessions = manager.lock().await; - - let sessions = sessions.entry(id).or_default(); - - sessions.retain(|_, klife| match klife { - KLife::Dead(now, _) => now.elapsed().as_secs() < 30, - _ => true, - }); - - let new_kcp = !sessions.contains_key(&conv); - - if new_kcp { - let new_session = KLife::Active({ - Session::new(conv, Some(addr), core.clone(), executor.clone(), { - { - let manager = manager.clone(); - move |conv| async move { - let mut sessions = manager.lock().await; - let sessions = sessions.get_mut(&id); - - if let Some(sessions) = sessions { - if let Some(session) = sessions.remove(&conv) { - let dead_session = match session { - KLife::Dead(now, session) => KLife::Dead(now, session), - KLife::Active(session) => { - let _ = session.force_close(); - KLife::Dead(Instant::now(), session) - } - }; - - sessions.insert(conv, dead_session); - } - } - - () - } - } - })? - }); - - sessions.insert(conv, new_session); - } - - let klife = unsafe { sessions.get_mut(&conv).unwrap_unchecked() }; - - match klife { - KLife::Dead(now, session) => { - let _ = session.force_close(); - - if now.elapsed().as_secs() < 30 { - if let Some(target) = session.target.as_ref() { - core.send_to(target, &third_party::force_close(session.conv)) - .await?; - } - } - } - KLife::Active(session) => { - if let Err(e) = session.input(data).await { - log::warn!("failed to input {}", e); - if let Err(e) = session.force_close() { - log::warn!("failed to close {}", e); - }; - } else if new_kcp { - break Ok(State::Open(( - id, - conv, - session.clone(), - Address::One(Socket::udp(addr)), - ))); - } - } - } - } - } - - fn make_close_fn(&self, id: u64, conv: u32) -> Callback { - let manager = self.manager.clone(); - let executor = self.executor.clone(); - let close_callback = move || { - executor.spawn(async move { - let mut sessions = manager.lock().await; - let session = sessions - .get_mut(&id) - .map_or(None, |sessions| sessions.get_mut(&conv)); - - if let Some(session) = session { - match session { - KLife::Active(session) => { - if let Err(e) = session.safe_close().await { - log::warn!("failed to close {}", e); - } - } - _ => {} - } - } - }); - }; - - Some(Box::new(close_callback)) - } -} - -impl NetSocket for KcpListener -where - C: NetSocket, -{ - fn peer_addr(&self) -> crate::Result { - self.core - .peer_addr() - .map(|addr| addr.with_kind(crate::SocketKind::Kcp)) - } - - fn local_addr(&self) -> crate::Result { - self.core - .local_addr() - .map(|addr| addr.with_kind(crate::SocketKind::Kcp)) - } -} - -impl Accepter for KcpListener -where - C: UdpSocket + Send + Clone + Unpin + Sync + 'static, - E: Executor + Clone + Sync + Unpin + Send + 'static, -{ - type Stream = KcpStream; - - fn poll_accept( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - let mut futures = std::mem::replace(&mut self.futures, Default::default()); - - while let Some(mut future) = futures.pop() { - match Pin::new(&mut future).poll(cx) { - Poll::Ready(Err(e)) => { - log::warn!("{}", e) - } - Poll::Ready(Ok(State::Close(conv))) => { - log::debug!("kcp closed conv={}", conv); - } - Poll::Pending => { - self.futures.push(future); - } - Poll::Ready(Ok(State::Open((id, conv, kcp, peer_addr)))) => { - let accept_fut = Self::run_accept( - self.core.clone(), - self.manager.clone(), - self.executor.clone(), - ); - - self.futures.push(Box::pin(accept_fut)); - - self.futures.extend(futures); - - let close_fn = self.make_close_fn(id, conv); - - return Poll::Ready(Ok(kcp.stream( - self.core.local_addr()?, - peer_addr, - close_fn, - ))); - } - } - } - - Poll::Pending - } -} - -impl Clone for Session { - fn clone(&self) -> Self { - Self { - conv: self.conv, - kcore: self.kcore.clone(), - target: self.target.clone(), - } - } -} - -impl Default for Increment { - fn default() -> Self { - Self(Arc::new(Mutex::new(1))) - } -} - -impl KcpConnector -where - C: UdpSocket + Unpin + Clone + Send + Sync + 'static, - E: Executor + Clone + Sync + Send + 'static, -{ - pub fn new(core: C, executor: E) -> Self { - let sessions: Arc>>> = Default::default(); - - let task = { executor.spawn(Self::run_connect(core.clone(), sessions.clone())) }; - - Self { - core, - task, - executor, - sessions, - increment: Default::default(), - } - } - - fn run_connect( - core: C, - sessions: Arc>>>, - ) -> BoxedFuture> { - let fut = async move { - loop { - let mut buf = Vec::with_capacity(1500); - - unsafe { - buf.set_len(1500); - } - - let n = core.recv(&mut buf).await?; - buf.truncate(n); - - if n < third_party::KCP_OVERHEAD { - log::warn!("bad kcp packet"); - continue; - } - - let conv = third_party::get_conv(&buf); - - let mut sessions = sessions.lock().await; - - sessions.retain(|_, klife| match klife { - KLife::Dead(now, _) => now.elapsed().as_secs() < 30, - _ => true, - }); - - if let Some(session) = sessions.get_mut(&conv) { - match session { - KLife::Active(session) => { - if let Err(e) = session.input(buf).await { - log::warn!("failed to input {}", e); - if let Err(e) = session.force_close() { - log::warn!("force close failed {}", e); - } - } - } - KLife::Dead(_, session) => { - core.send(&third_party::force_close(session.conv)).await?; - } - } - } else { - // FIXME 需要处理time_wait - core.send(&third_party::force_close(conv)).await?; - } - } - }; - - Box::pin(fut) - } - - pub fn core(&self) -> &C { - &self.core - } - - pub async fn connect(&self) -> crate::Result> { - let conv = { - let sessions = self.sessions.lock().await; - let conv = self.increment.current().await; - let mut next_conv = conv; - loop { - log::trace!("current conv={}", next_conv); - - if !sessions.contains_key(&next_conv) { - break next_conv; - } - - next_conv = self.increment.next().await; - - if next_conv == conv { - return Err(KcpErr::NoMoreConv.into()); - } - } - }; - - let sessions = self.sessions.clone(); - let executor = self.executor.clone(); - - let session = Session::new(conv, None, self.core.clone(), self.executor.clone(), { - let sessions = self.sessions.clone(); - move |conv| async move { - let mut sessions = sessions.lock().await; - if let Some(session) = sessions.remove(&conv) { - let dead_session = match session { - KLife::Dead(now, session) => KLife::Dead(now, session), - KLife::Active(session) => { - let _ = session.force_close(); - KLife::Dead(Instant::now(), session) - } - }; - - sessions.insert(conv, dead_session); - log::debug!("clean the session conv={}", conv); - } - - () - } - })?; - - self.sessions - .lock() - .await - .insert(conv, KLife::Active(session.clone())); - - let close_callback = move || { - executor.spawn(async move { - log::debug!("closing conv {}", conv); - if let Some(session) = sessions.lock().await.get_mut(&conv) { - match session { - KLife::Active(session) => { - if let Err(e) = session.safe_close().await { - log::warn!("failed to close {}", e); - } - } - _ => {} - } - } - }); - }; - - Ok(session.stream( - self.core.local_addr()?, - self.core.peer_addr()?, - Some(Box::new(close_callback)), - )) - } -} - -impl Increment { - pub async fn current(&self) -> u32 { - *self.0.lock().await - } - - pub async fn next(&self) -> u32 { - let mut inc = self.0.lock().await; - let (_, overflow) = inc.overflowing_add(1); - *inc = if overflow { 0 } else { *inc + 1 }; - *inc - } -} - -unsafe impl Sync for KcpUpdate {} -unsafe impl Send for KcpUpdate {} - -impl Drop for KcpConnector { - fn drop(&mut self) { - self.task.abort(); - } -} - -impl Future for KcpUpdate -where - C: UdpSocket + Unpin + 'static, -{ - type Output = crate::Result<()>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { - loop { - let state = match self.kupdate.take() { - Some(inner) => inner, - None => UpdateState::WaitUpdate({ - let mut kcore = self.kcore.lock()?; - let next = kcore.check(now_mills()); - - if kcore.closed() { - kcore.close_waker.take().map(Waker::wake); - kcore.write_waker.take().map(Waker::wake); - kcore.read_waker.take().map(Waker::wake); - return Poll::Ready(Err(KcpErr::ConnectionReset.into())); - } - - log::trace!( - "next update {}ms conv={} cls={}", - next, - kcore.conv(), - kcore.close_state() - ); - - Box::pin(async move { - time::sleep(Duration::from_millis(next as u64)).await; - Ok(()) - }) - }), - }; - - match state { - UpdateState::Updating(mut fut) => match Pin::new(&mut fut).poll(cx)? { - Poll::Pending => { - drop(std::mem::replace( - &mut self.kupdate, - Some(UpdateState::Updating(fut)), - )); - - return Poll::Pending; - } - _ => {} - }, - UpdateState::WaitUpdate(mut fut) => match Pin::new(&mut fut).poll(cx)? { - Poll::Pending => { - drop(std::mem::replace( - &mut self.kupdate, - Some(UpdateState::WaitUpdate(fut)), - )); - - return Poll::Pending; - } - Poll::Ready(()) => { - let kcore = self.kcore.clone(); - - let fut = async move { - let mut kcore = kcore.lock()?; - - kcore.update(now_mills()).await?; - - if kcore.peeksize().is_ok() { - kcore.read_waker.take().map(Waker::wake); - } - - if kcore.wait_snd() < third_party::KCP_WND_RCV as usize { - kcore.write_waker.take().map(Waker::wake); - } - - if kcore.wait_snd() == 0 { - kcore.close_waker.take().map(Waker::wake); - } - - if kcore.is_dead_link() { - drop(kcore.force_close()); - kcore.close_waker.take().map(Waker::wake); - kcore.write_waker.take().map(Waker::wake); - kcore.read_waker.take().map(Waker::wake); - Err(KcpErr::IoError(std::io::ErrorKind::TimedOut.into()).into()) - } else { - Ok(()) - } - }; - - drop(std::mem::replace( - &mut self.kupdate, - Some(UpdateState::Updating(Box::pin(fut))), - )); - } - }, - } - } - } -} - -#[cfg(test)] -#[cfg(feature = "fuso-rt-tokio")] -mod tests { - use std::sync::Arc; - - use crate::{ - ext::{AsyncReadExt, AsyncWriteExt}, - io, AccepterExt, FusoExecutor, - }; - - use super::{KcpConnector, KcpListener}; - - fn init_logger() { - #[cfg(feature = "fuso-log")] - env_logger::builder() - .filter_module("fuso", log::LevelFilter::Debug) - .init(); - } - - #[test] - #[cfg(feature = "fuso-rt-tokio")] - pub fn test_kcp_server() { - init_logger(); - - tokio::runtime::Runtime::new() - .unwrap() - .block_on(async move { - let udp = tokio::net::UdpSocket::bind("0.0.0.0:7777").await.unwrap(); - let udp = Arc::new(udp); - - let mut kcp = KcpListener::bind(udp, FusoExecutor).unwrap(); - - loop { - match kcp.accept().await { - Ok(kcp) => { - tokio::spawn(async move { - let (mut reader, mut writer) = io::split(kcp); - - loop { - let mut buf = Vec::new(); - buf.resize(1500, 0); - let n = reader.read(&mut buf).await.unwrap(); - buf.truncate(n); - - let s = String::from_utf8_lossy(&buf); - - log::debug!("receive {:?}", s); - if s.contains("close") { - let _ = writer.close().await; - log::debug!("close ...."); - break; - } - - let mut writer = writer.clone(); - tokio::spawn(async move { - log::debug!("write hello world"); - let _ = writer.write_all(b"hello world").await; - }); - } - }); - } - Err(_) => {} - } - } - }); - } - - #[test] - #[cfg(feature = "fuso-rt-tokio")] - pub fn test_kcp_client() { - init_logger(); - - tokio::runtime::Runtime::new() - .unwrap() - .block_on(async move { - let udp = tokio::net::UdpSocket::bind("0.0.0.0:0").await.unwrap(); - udp.connect("127.0.0.1:7777").await.unwrap(); - - let kcp = KcpConnector::new(Arc::new(udp), FusoExecutor); - - let mut kcp = kcp.connect().await.unwrap(); - - loop { - let mut buf = String::new(); - std::io::stdin().read_line(&mut buf).unwrap(); - let _ = kcp.write_all(buf.as_bytes()).await; - let mut buf = Vec::new(); - buf.resize(1500, 0); - let n = kcp.read(&mut buf).await.unwrap(); - buf.truncate(n); - log::debug!("{:?}", buf) - } - }); - } -} diff --git a/src/net/kcp/stream.rs b/src/net/kcp/stream.rs deleted file mode 100644 index d9991af..0000000 --- a/src/net/kcp/stream.rs +++ /dev/null @@ -1,255 +0,0 @@ -use std::{ - net::SocketAddr, - ops::{Deref, DerefMut}, - pin::Pin, - sync::Arc, - task::{Poll, Waker}, -}; - -use crate::{ - guard::buffer::Buffer, Address, AsyncRead, AsyncWrite, Kind, NetSocket, Task, UdpSocket, -}; - -use super::KcpErr; - -type Callback = Option>; - -#[pin_project::pin_project] -pub struct KOutput { - #[pin] - pub(crate) output: C, - pub(crate) target: Option, -} - -pub struct KcpCore { - pub(crate) kcp: super::third_party::Kcp>, - pub(crate) kbuf: Buffer, - pub(crate) kupdate: Option>>, - pub(crate) read_waker: Option, - pub(crate) write_waker: Option, - pub(crate) close_waker: Option, -} - -pub struct KcpStream { - pub(crate) conv: u32, - pub(crate) local_addr: Address, - pub(crate) peer_addr: Address, - pub(crate) kcore: Arc>>, - /// 在被drop或手动调用close时触发通知内部进行更新 - pub(crate) close_callback: Callback, -} - -impl Deref for KcpCore { - type Target = super::third_party::Kcp>; - fn deref(&self) -> &Self::Target { - &self.kcp - } -} - -impl DerefMut for KcpCore { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.kcp - } -} - -impl AsyncWrite for KOutput -where - C: UdpSocket + Unpin, -{ - fn poll_write( - self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - data: &[u8], - ) -> Poll> { - let this = self.project(); - let output = Pin::new(&*this.output); - match this.target.as_ref() { - None => output.poll_send(cx, data), - Some(addr) => output.poll_send_to(cx, addr, data), - } - } - - fn poll_flush(self: Pin<&mut Self>, _: &mut std::task::Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn poll_close(self: Pin<&mut Self>, _: &mut std::task::Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } -} - -impl AsyncWrite for KcpCore -where - C: UdpSocket + Unpin + 'static, -{ - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> Poll> { - match self.kcp.send(buf) { - Ok(n) => Poll::Ready(Ok(n)), - Err(e) => match e.kind() { - Kind::Kcp(KcpErr::UserBufTooBig) => { - drop(std::mem::replace( - &mut self.write_waker, - Some(cx.waker().clone()), - )); - Poll::Pending - } - _ => Poll::Ready(Err(e)), - }, - } - } - - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> Poll> { - if self.kcp.wait_snd() > 0 { - drop(std::mem::replace( - &mut self.close_waker, - Some(cx.waker().clone()), - )); - Poll::Pending - } else { - self.kcp.force_close()?; - Poll::Ready(Ok(())) - } - } - - fn poll_flush(self: Pin<&mut Self>, _: &mut std::task::Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } -} - -impl AsyncRead for KcpCore -where - C: UdpSocket + Unpin + 'static, -{ - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &mut crate::ReadBuf<'_>, - ) -> Poll> { - let unfilled = buf.initialize_unfilled(); - - if !self.kbuf.is_empty() { - let n = self.kbuf.read_to_buffer(unfilled); - buf.advance(n); - return Poll::Ready(Ok(n)); - } - - match self.kcp.recv(unfilled) { - Ok(n) => { - buf.advance(n); - return Poll::Ready(Ok(n)); - } - Err(e) => match e.kind() { - Kind::Kcp(KcpErr::RecvQueueEmpty | KcpErr::ExpectingFragment) => { - let waker = cx.waker().clone(); - drop(std::mem::replace(&mut self.read_waker, Some(waker))); - return Poll::Pending; - } - Kind::Kcp(KcpErr::UserBufTooSmall) => unsafe { - let size = self.kcp.peeksize().unwrap_unchecked(); - let mut data = Vec::with_capacity(size); - - data.set_len(size); - - let n = self.kcp.recv(&mut data)?; - data.truncate(n); - let len = unfilled.len(); - - std::ptr::copy(data.as_ptr(), unfilled.as_mut_ptr(), len); - - buf.advance(len); - - self.kbuf.push_back(&data[len..]); - - return Poll::Ready(Ok(len)); - }, - _ => Poll::Ready(Err(e)), - }, - } - } -} - -impl AsyncRead for KcpStream -where - C: UdpSocket + Unpin + 'static, -{ - fn poll_read( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &mut crate::ReadBuf<'_>, - ) -> std::task::Poll> { - let mut kcore = self.kcore.lock()?; - Pin::new(&mut *kcore).poll_read(cx, buf) - } -} - -impl AsyncWrite for KcpStream -where - C: UdpSocket + Unpin + 'static, -{ - fn poll_write( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> std::task::Poll> { - let mut kcore = self.kcore.lock()?; - Pin::new(&mut *kcore).poll_write(cx, buf) - } - - fn poll_flush( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - let mut kcore = self.kcore.lock()?; - Pin::new(&mut *kcore).poll_flush(cx) - } - - fn poll_close( - mut self: std::pin::Pin<&mut Self>, - _: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - if let Some(close_callback) = self.close_callback.take() { - close_callback(); - } - - Poll::Ready(Ok(())) - } -} - -impl NetSocket for KcpStream { - fn peer_addr(&self) -> crate::Result { - Ok(self.peer_addr.clone()) - } - - fn local_addr(&self) -> crate::Result { - Ok(self.local_addr.clone()) - } -} - -impl Drop for KcpStream { - fn drop(&mut self) { - if let Some(close_callback) = self.close_callback.take() { - log::debug!("close read and write conv={}", self.conv); - close_callback(); - } else { - log::debug!("closed conv {}", self.conv); - } - } -} - -impl Drop for KcpCore { - fn drop(&mut self) { - if let Some(mut kupdate) = self.kupdate.take() { - log::debug!("abort execution {}", self.kcp); - kupdate.abort(); - } else { - log::debug!("kupdate finished"); - } - } -} diff --git a/src/net/kcp/third_party/LICENSE b/src/net/kcp/third_party/LICENSE deleted file mode 100644 index 5c806b1..0000000 --- a/src/net/kcp/third_party/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 Zhang Cheng - -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. \ No newline at end of file diff --git a/src/net/kcp/third_party/error.rs b/src/net/kcp/third_party/error.rs deleted file mode 100644 index fb16a12..0000000 --- a/src/net/kcp/third_party/error.rs +++ /dev/null @@ -1,95 +0,0 @@ -//! KCP https://github.com/Matrix-Zhang/kcp - -use std::error::Error as StdError; -use std::fmt; -use std::io::{self, ErrorKind}; - -/// KCP protocol errors -#[derive(Debug)] -pub enum KcpErr { - ConvInconsistent(u32, u32), - InvalidMtu(usize), - InvalidSegmentSize(usize), - InvalidSegmentDataSize(usize, usize), - IoError(io::Error), - NeedUpdate, - RecvQueueEmpty, - ExpectingFragment, - UnsupportedCmd(u8), - UserBufTooBig, - UserBufTooSmall, - NoMoreConv, - ConnectionReset, -} - -impl StdError for KcpErr { - fn cause(&self) -> Option<&dyn StdError> { - match *self { - KcpErr::IoError(ref e) => Some(e), - _ => None, - } - } -} - -impl fmt::Display for KcpErr { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - KcpErr::ConvInconsistent(ref s, ref o) => { - write!(f, "conv inconsistent, expected {}, found {}", *s, *o) - } - KcpErr::InvalidMtu(ref e) => write!(f, "invalid mtu {}", *e), - KcpErr::InvalidSegmentSize(ref e) => write!(f, "invalid segment size of {}", *e), - KcpErr::InvalidSegmentDataSize(ref s, ref o) => { - write!( - f, - "invalid segment data size, expected {}, found {}", - *s, *o - ) - } - KcpErr::IoError(ref e) => e.fmt(f), - KcpErr::UnsupportedCmd(ref e) => write!(f, "cmd {} is not supported", *e), - KcpErr::NeedUpdate => write!(f, "NeedUpdate"), - KcpErr::RecvQueueEmpty => write!(f, "RecvQueueEmpty"), - KcpErr::ExpectingFragment => write!(f, "ExpectingFragment"), - KcpErr::UserBufTooBig => write!(f, "UserBufTooBig"), - KcpErr::UserBufTooSmall => write!(f, "UserBufTooSmall"), - KcpErr::NoMoreConv => write!(f, "NoMoreConv"), - KcpErr::ConnectionReset => write!(f, "ConnectionReset"), - } - } -} - -fn make_io_error(kind: ErrorKind, msg: T) -> io::Error -where - T: Into>, -{ - io::Error::new(kind, msg) -} - -impl From for io::Error { - fn from(err: KcpErr) -> io::Error { - let kind = match err { - KcpErr::ConvInconsistent(..) => ErrorKind::Other, - KcpErr::InvalidMtu(..) => ErrorKind::Other, - KcpErr::InvalidSegmentSize(..) => ErrorKind::Other, - KcpErr::InvalidSegmentDataSize(..) => ErrorKind::Other, - KcpErr::IoError(err) => return err, - KcpErr::NeedUpdate => ErrorKind::Other, - KcpErr::RecvQueueEmpty => ErrorKind::WouldBlock, - KcpErr::ExpectingFragment => ErrorKind::WouldBlock, - KcpErr::UnsupportedCmd(..) => ErrorKind::Other, - KcpErr::UserBufTooBig => ErrorKind::Other, - KcpErr::UserBufTooSmall => ErrorKind::Other, - KcpErr::NoMoreConv => ErrorKind::Other, - KcpErr::ConnectionReset => ErrorKind::ConnectionReset, - }; - - make_io_error(kind, err) - } -} - -impl From for KcpErr { - fn from(err: io::Error) -> KcpErr { - KcpErr::IoError(err) - } -} diff --git a/src/net/kcp/third_party/mod.rs b/src/net/kcp/third_party/mod.rs deleted file mode 100644 index 0e90559..0000000 --- a/src/net/kcp/third_party/mod.rs +++ /dev/null @@ -1,1293 +0,0 @@ -//! KCP https://github.com/Matrix-Zhang/kcp -#![allow(unused)] - -mod error; - -use log::{debug, trace}; -use std::cmp; -use std::cmp::Ordering; -use std::collections::VecDeque; -use std::fmt::Display; -use std::io::{Cursor, Read}; - -use bytes::{Buf, BufMut, Bytes, BytesMut}; -pub use error::KcpErr; - -use crate::ext::AsyncWriteExt; -use crate::AsyncWrite; - -/// KCP result -pub type KcpResult = crate::Result; - -const KCP_RTO_NDL: u32 = 30; -const KCP_RTO_MIN: u32 = 100; -const KCP_RTO_DEF: u32 = 200; -const KCP_RTO_MAX: u32 = 60000; - -const KCP_CMD_PUSH: u8 = 81; -const KCP_CMD_ACK: u8 = 82; -const KCP_CMD_WASK: u8 = 83; -const KCP_CMD_WINS: u8 = 84; - -const KCP_CMD_CLS: u8 = 85; -const KCP_CMD_CLF: u8 = 86; - -const KCP_CLS_NO: u8 = 0; -const KCP_CLS_YES: u8 = 1; -const KCP_CLS_WSND: u8 = 2; -const KCP_CLS_WRCV: u8 = 3; -const KCP_CLS_WAIT: u8 = 4; - -const KCP_ASK_SEND: u32 = 1; -const KCP_ASK_TELL: u32 = 2; - -pub const KCP_WND_SND: u16 = 32; -pub const KCP_WND_RCV: u16 = 128; - -const KCP_MTU_DEF: usize = 1400; -// const KCP_ACK_FAST: u32 = 3; - -const KCP_INTERVAL: u32 = 100; -pub const KCP_OVERHEAD: usize = 24; -// const KCP_DEADLINK: u32 = 20; - -const KCP_THRESH_INIT: u16 = 2; -const KCP_THRESH_MIN: u16 = 2; - -const KCP_PROBE_INIT: u32 = 7000; -const KCP_PROBE_LIMIT: u32 = 120000; - -/// Read `conv` from raw buffer -pub fn get_conv(mut buf: &[u8]) -> u32 { - assert!(buf.len() >= KCP_OVERHEAD); - buf.get_u32_le() -} - -/// Set `conv` to raw buffer -pub fn set_conv(mut buf: &mut [u8], conv: u32) { - assert!(buf.len() >= KCP_OVERHEAD); - buf.put_u32_le(conv) -} - -pub fn force_close(conv: u32) -> Bytes { - let seg = KcpSegment { - conv, - cmd: KCP_CMD_CLF, - ..Default::default() - }; - - let mut buf = BytesMut::new(); - - seg.encode(&mut buf); - - buf.into() -} - -/// Get `sn` from raw buffer -pub fn get_sn(buf: &[u8]) -> u32 { - assert!(buf.len() >= KCP_OVERHEAD); - (&buf[12..]).get_u32_le() -} - -#[inline] -fn bound(lower: u32, v: u32, upper: u32) -> u32 { - cmp::min(cmp::max(lower, v), upper) -} - -#[inline] -fn timediff(later: u32, earlier: u32) -> i32 { - later as i32 - earlier as i32 -} - -#[derive(Default, Clone, Debug)] -struct KcpSegment { - conv: u32, - cmd: u8, - frg: u8, - wnd: u16, - ts: u32, - sn: u32, - una: u32, - resendts: u32, - rto: u32, - fastack: u32, - xmit: u32, - data: BytesMut, -} - -impl KcpSegment { - fn new_with_data(data: BytesMut) -> Self { - KcpSegment { - conv: 0, - cmd: 0, - frg: 0, - wnd: 0, - ts: 0, - sn: 0, - una: 0, - resendts: 0, - rto: 0, - fastack: 0, - xmit: 0, - data, - } - } - - fn encode(&self, buf: &mut BytesMut) { - if buf.remaining_mut() < self.encoded_len() { - panic!( - "REMAIN {} encoded {} {:?}", - buf.remaining_mut(), - self.encoded_len(), - self - ); - } - - buf.put_u32_le(self.conv); - buf.put_u8(self.cmd); - buf.put_u8(self.frg); - buf.put_u16_le(self.wnd); - buf.put_u32_le(self.ts); - buf.put_u32_le(self.sn); - buf.put_u32_le(self.una); - buf.put_u32_le(self.data.len() as u32); - buf.put_slice(&self.data); - } - - fn encoded_len(&self) -> usize { - KCP_OVERHEAD + self.data.len() - } -} - -/// KCP control - -pub struct Kcp { - /// Conversation ID - conv: u32, - /// Maximun Transmission Unit - mtu: usize, - /// Maximum Segment Size - mss: u32, - /// Connection state - state: i32, - - /// First unacknowledged packet - snd_una: u32, - /// Next packet - snd_nxt: u32, - /// Next packet to be received - rcv_nxt: u32, - - /// Congetion window threshole - ssthresh: u16, - - /// ACK receive variable RTT - rx_rttval: u32, - /// ACK receive static RTT - rx_srtt: u32, - /// Resend time (calculated by ACK delay time) - rx_rto: u32, - /// Minimal resend timeout - rx_minrto: u32, - - /// Send window - snd_wnd: u16, - /// Receive window - rcv_wnd: u16, - /// Remote receive window - rmt_wnd: u16, - /// Congetion window - cwnd: u16, - /// Check window - /// - IKCP_ASK_TELL, telling window size to remote - /// - IKCP_ASK_SEND, ask remote for window size - probe: u32, - - /// Last update time - current: u32, - /// Flush interval - interval: u32, - /// Next flush interval - ts_flush: u32, - xmit: u32, - - /// Enable nodelay - nodelay: bool, - /// Updated has been called or not - updated: bool, - - /// Next check window timestamp - ts_probe: u32, - /// Check window wait time - probe_wait: u32, - - /// Maximum resend time - dead_link: u32, - /// Maximum payload size - incr: u32, - - snd_queue: VecDeque, - rcv_queue: VecDeque, - snd_buf: VecDeque, - rcv_buf: VecDeque, - - /// Pending ACK - acklist: VecDeque<(u32, u32)>, - buf: BytesMut, - - /// ACK number to trigger fast resend - fastresend: u32, - /// Disable congetion control - nocwnd: bool, - /// Enable stream mode - stream: bool, - close_last: std::time::Instant, - close_state: u8, - /// Get conv from the next input call - input_conv: bool, - - output: Output, -} - -impl Kcp -where - Output: AsyncWrite + Unpin + 'static, -{ - /// Creates a KCP control object, `conv` must be equal in both endpoints in one connection. - /// `output` is the callback object for writing. - /// - /// `conv` represents conversation. - pub fn new(conv: u32, output: Output) -> Self { - Kcp::construct(conv, output, false) - } - - /// Creates a KCP control object in stream mode, `conv` must be equal in both endpoints in one connection. - /// `output` is the callback object for writing. - /// - /// `conv` represents conversation. - pub fn new_stream(conv: u32, output: Output) -> Self { - Kcp::construct(conv, output, true) - } - - fn construct(conv: u32, output: Output, stream: bool) -> Self { - Kcp { - conv, - snd_una: 0, - snd_nxt: 0, - rcv_nxt: 0, - rx_rttval: 0, - rx_srtt: 0, - state: 0, - cwnd: 0, - probe: 0, - current: 0, - xmit: 0, - nodelay: false, - updated: false, - ts_probe: 0, - probe_wait: 0, - dead_link: 10, - incr: 0, - fastresend: 0, - nocwnd: false, - stream, - snd_wnd: KCP_WND_SND, - rcv_wnd: KCP_WND_RCV, - rmt_wnd: KCP_WND_RCV, - mtu: KCP_MTU_DEF, - mss: (KCP_MTU_DEF - KCP_OVERHEAD) as u32, - buf: BytesMut::with_capacity((KCP_MTU_DEF + KCP_OVERHEAD) * 3), - snd_queue: VecDeque::new(), - rcv_queue: VecDeque::new(), - snd_buf: VecDeque::new(), - rcv_buf: VecDeque::new(), - acklist: VecDeque::new(), - rx_rto: KCP_RTO_DEF, - rx_minrto: KCP_RTO_MIN, - interval: KCP_INTERVAL, - ts_flush: KCP_INTERVAL, - ssthresh: KCP_THRESH_INIT, - input_conv: false, - close_state: KCP_CLS_NO, - output: output, - close_last: std::time::Instant::now(), - } - } - - /// Check buffer size without actually consuming it - pub fn peeksize(&self) -> KcpResult { - match self.rcv_queue.front() { - Some(segment) => { - if segment.frg == 0 { - return Ok(segment.data.len()); - } - - if self.rcv_queue.len() < (segment.frg + 1) as usize { - return Err(KcpErr::ExpectingFragment.into()); - } - - let mut len = 0; - - for segment in &self.rcv_queue { - len += segment.data.len(); - if segment.frg == 0 { - break; - } - } - - Ok(len) - } - None => Err(KcpErr::RecvQueueEmpty.into()), - } - } - - // move available data from rcv_buf -> rcv_queue - pub fn move_buf(&mut self) { - while !self.rcv_buf.is_empty() { - let nrcv_que = self.rcv_queue.len(); - { - let seg = &self.rcv_buf[0]; - if seg.sn == self.rcv_nxt && nrcv_que < self.rcv_wnd as usize { - self.rcv_nxt += 1; - } else { - break; - } - } - - let seg = self.rcv_buf.pop_front().unwrap(); - self.rcv_queue.push_back(seg); - } - } - - /// Receive data from buffer - pub fn recv(&mut self, buf: &mut [u8]) -> KcpResult { - if self.close_state == KCP_CLS_YES - || self.close_state == KCP_CLS_WAIT - || self.close_state == KCP_CLS_WRCV - || self.close_state == KCP_CLS_WSND - { - return Err(KcpErr::ConnectionReset.into()); - } - - if self.rcv_queue.is_empty() { - return Err(KcpErr::RecvQueueEmpty.into()); - } - - let peeksize = self.peeksize()?; - - if peeksize > buf.len() { - trace!("recv peeksize={} bufsize={} too small", peeksize, buf.len()); - return Err(KcpErr::UserBufTooSmall.into()); - } - - let recover = self.rcv_queue.len() >= self.rcv_wnd as usize; - - // Merge fragment - let mut cur = Cursor::new(buf); - while let Some(seg) = self.rcv_queue.pop_front() { - std::io::Write::write_all(&mut cur, &seg.data)?; - - trace!("recv sn={}", seg.sn); - - if seg.frg == 0 { - break; - } - } - assert_eq!(cur.position() as usize, peeksize); - - self.move_buf(); - - // fast recover - if self.rcv_queue.len() < self.rcv_wnd as usize && recover { - // ready to send back IKCP_CMD_WINS in ikcp_flush - // tell remote my window size - self.probe |= KCP_ASK_TELL; - } - - Ok(cur.position() as usize) - } - - /// Send bytes into buffer - pub fn send(&mut self, mut buf: &[u8]) -> KcpResult { - if self.close_state == KCP_CLS_YES - || self.close_state == KCP_CLS_WAIT - || self.close_state == KCP_CLS_WRCV - || self.close_state == KCP_CLS_WSND - { - return Err(KcpErr::ConnectionReset.into()); - } - - let mut sent_size = 0; - - assert!(self.mss > 0); - - // append to previous segment in streaming mode (if possible) - if self.stream { - if let Some(old) = self.snd_queue.back_mut() { - let l = old.data.len(); - if l < self.mss as usize { - let capacity = self.mss as usize - l; - let extend = cmp::min(buf.len(), capacity); - - trace!( - "send stream mss={} last length={} extend={}", - self.mss, - l, - extend - ); - - let (lf, rt) = buf.split_at(extend); - old.data.extend_from_slice(lf); - buf = rt; - - old.frg = 0; - sent_size += extend; - } - - if buf.is_empty() { - return Ok(sent_size); - } - } - } - - let count = if buf.len() <= self.mss as usize { - 1 - } else { - (buf.len() + self.mss as usize - 1) / self.mss as usize - }; - - if count >= KCP_WND_RCV as usize { - debug!("send bufsize={} mss={} too large", buf.len(), self.mss); - return Err(KcpErr::UserBufTooBig.into()); - } - assert!(count > 0); - - // let count = cmp::max(1, count); - - for i in 0..count { - let size = cmp::min(self.mss as usize, buf.len()); - - let (lf, rt) = buf.split_at(size); - - let mut new_segment = KcpSegment::new_with_data(lf.into()); - buf = rt; - - new_segment.frg = if self.stream { - 0 - } else { - (count - i - 1) as u8 - }; - - self.snd_queue.push_back(new_segment); - sent_size += size; - } - - Ok(sent_size) - } - - fn update_ack(&mut self, rtt: u32) { - if self.rx_srtt == 0 { - self.rx_srtt = rtt; - self.rx_rttval = rtt / 2; - } else { - let delta = if rtt > self.rx_srtt { - rtt - self.rx_srtt - } else { - self.rx_srtt - rtt - }; - self.rx_rttval = (3 * self.rx_rttval + delta) / 4; - self.rx_srtt = (7 * self.rx_srtt + rtt) / 8; - if self.rx_srtt < 1 { - self.rx_srtt = 1; - } - } - let rto = self.rx_srtt + cmp::max(self.interval, 4 * self.rx_rttval); - self.rx_rto = bound(self.rx_minrto, rto, KCP_RTO_MAX); - } - - #[inline] - fn shrink_buf(&mut self) { - self.snd_una = match self.snd_buf.front() { - Some(seg) => seg.sn, - None => self.snd_nxt, - }; - } - - fn parse_ack(&mut self, sn: u32) { - if timediff(sn, self.snd_una) < 0 || timediff(sn, self.snd_nxt) >= 0 { - return; - } - - let mut i = 0 as usize; - while i < self.snd_buf.len() { - match sn.cmp(&self.snd_buf[i].sn) { - Ordering::Equal => { - self.snd_buf.remove(i); - } - Ordering::Less => break, - _ => i = i + 1, - } - } - } - - fn parse_una(&mut self, una: u32) { - while !self.snd_buf.is_empty() { - if timediff(una, self.snd_buf[0].sn) > 0 { - // self.snd_buf.remove(0); - self.snd_buf.pop_front(); - } else { - break; - } - } - } - - fn parse_fastack(&mut self, sn: u32) { - if timediff(sn, self.snd_una) < 0 || timediff(sn, self.snd_nxt) >= 0 { - return; - } - - for seg in &mut self.snd_buf { - if timediff(sn, seg.sn) < 0 { - break; - } else if sn != seg.sn { - seg.fastack += 1; - } - } - } - - #[inline] - fn ack_push(&mut self, sn: u32, ts: u32) { - self.acklist.push_back((sn, ts)); - } - - fn parse_data(&mut self, new_segment: KcpSegment) { - let sn = new_segment.sn; - - if timediff(sn, self.rcv_nxt + self.rcv_wnd as u32) >= 0 || timediff(sn, self.rcv_nxt) < 0 { - return; - } - - let mut repeat = false; - let mut new_index = self.rcv_buf.len(); - - for segment in self.rcv_buf.iter().rev() { - if segment.sn == sn { - repeat = true; - break; - } else if timediff(sn, segment.sn) > 0 { - break; - } - new_index -= 1; - } - - if !repeat { - self.rcv_buf.insert(new_index, new_segment); - } - - // move available data from rcv_buf -> rcv_queue - self.move_buf(); - } - - /// Get `conv` from the next `input` call - #[inline] - pub fn input_conv(&mut self) { - self.input_conv = true; - } - - /// Check if Kcp is waiting for the next input - #[inline] - pub fn waiting_conv(&self) -> bool { - self.input_conv - } - - /// Set `conv` value - #[inline] - pub fn set_conv(&mut self, conv: u32) { - self.conv = conv; - } - - /// Get `conv` - #[inline] - pub fn conv(&self) -> u32 { - self.conv - } - - /// Call this when you received a packet from raw connection - pub fn input(&mut self, buf: &[u8]) -> KcpResult { - let input_size = buf.len(); - - trace!("[RI] {} bytes", buf.len()); - - if buf.len() < KCP_OVERHEAD { - debug!( - "input bufsize={} too small, at least {}", - buf.len(), - KCP_OVERHEAD - ); - return Err(KcpErr::InvalidSegmentSize(buf.len()).into()); - } - - let mut flag = false; - let mut max_ack = 0; - let old_una = self.snd_una; - - let mut buf = Cursor::new(buf); - while buf.remaining() >= KCP_OVERHEAD as usize { - let conv = buf.get_u32_le(); - if conv != self.conv { - // This allows getting conv from this call, which allows us to allocate - // conv from the server side. - if self.input_conv { - debug!("input conv={} updated, original conv={}", conv, self.conv); - self.conv = conv; - self.input_conv = false; - } else { - debug!("input conv={} expected conv={} not match", conv, self.conv); - return Err(KcpErr::ConvInconsistent(self.conv, conv).into()); - } - } - - let cmd = buf.get_u8(); - let frg = buf.get_u8(); - let wnd = buf.get_u16_le(); - let ts = buf.get_u32_le(); - let sn = buf.get_u32_le(); - let una = buf.get_u32_le(); - let len = buf.get_u32_le() as usize; - - if buf.remaining() < len as usize { - debug!( - "input bufsize={} payload length={} remaining={} not match", - input_size, - len, - buf.remaining() - ); - return Err(KcpErr::InvalidSegmentDataSize(len, buf.remaining()).into()); - } - - match cmd { - KCP_CMD_PUSH | KCP_CMD_ACK | KCP_CMD_WASK | KCP_CMD_WINS | KCP_CMD_CLS - | KCP_CMD_CLF => {} - _ => { - debug!("input cmd={} unrecognized", cmd); - return Err(KcpErr::UnsupportedCmd(cmd).into()); - } - } - - self.rmt_wnd = wnd; - - self.parse_una(una); - self.shrink_buf(); - - let mut has_read_data = false; - - match cmd { - KCP_CMD_ACK => { - let rtt = timediff(self.current, ts); - if rtt >= 0 { - self.update_ack(rtt as u32); - } - self.parse_ack(sn); - self.shrink_buf(); - - if !flag { - max_ack = sn; - flag = true; - } else if timediff(sn, max_ack) > 0 { - max_ack = sn; - } - - trace!( - "input ack: sn={} rtt={} rto={}", - sn, - timediff(self.current, ts), - self.rx_rto - ); - } - KCP_CMD_PUSH => { - trace!("input psh: sn={} ts={}", sn, ts); - - if timediff(sn, self.rcv_nxt + self.rcv_wnd as u32) < 0 { - self.ack_push(sn, ts); - if timediff(sn, self.rcv_nxt) >= 0 { - let mut sbuf = BytesMut::with_capacity(len as usize); - unsafe { - sbuf.set_len(len as usize); - } - buf.read_exact(&mut sbuf).unwrap(); - has_read_data = true; - - let mut segment = KcpSegment::new_with_data(sbuf); - - segment.conv = conv; - segment.cmd = cmd; - segment.frg = frg; - segment.wnd = wnd; - segment.ts = ts; - segment.sn = sn; - segment.una = una; - - self.parse_data(segment); - } - } - } - KCP_CMD_WASK => { - trace!("input probe"); - self.probe |= KCP_ASK_TELL; - } - KCP_CMD_WINS => { - // Do nothing - trace!("input wins: {}", wnd); - } - KCP_CMD_CLS => { - debug!("receiver close message cs={}", self.close_state); - - if self.close_state == KCP_CLS_WAIT { - self.close_state = KCP_CLS_YES; - } - - if self.close_state == KCP_CLS_NO { - self.close_state = KCP_CLS_WRCV; - } - } - KCP_CMD_CLF => { - self.close_state = KCP_CLS_YES; - } - _ => unreachable!(), - } - - // Force skip unread data - if !has_read_data { - let next_pos = buf.position() + len as u64; - buf.set_position(next_pos); - } - } - - if flag { - self.parse_fastack(max_ack); - } - - if self.snd_una > old_una && self.cwnd < self.rmt_wnd { - let mss = self.mss; - if self.cwnd < self.ssthresh { - self.cwnd += 1; - self.incr += mss; - } else { - if self.incr < mss { - self.incr = mss; - } - self.incr += (mss * mss) / self.incr + (mss / 16); - if (self.cwnd + 1) as u32 * mss <= self.incr { - self.cwnd += 1; - } - } - if self.cwnd > self.rmt_wnd { - self.cwnd = self.rmt_wnd; - self.incr = self.rmt_wnd as u32 * mss; - } - } - - Ok(buf.position() as usize) - } - - fn wnd_unused(&self) -> u16 { - if self.rcv_queue.len() < self.rcv_wnd as usize { - self.rcv_wnd - self.rcv_queue.len() as u16 - } else { - 0 - } - } - - async fn _flush_ack(&mut self, segment: &mut KcpSegment) -> KcpResult<()> { - // flush acknowledges - // while let Some((sn, ts)) = self.acklist.pop_front() { - for &(sn, ts) in &self.acklist { - if self.buf.len() + KCP_OVERHEAD > self.mtu as usize { - self.output.write_all(&self.buf).await?; - self.buf.clear(); - } - segment.sn = sn; - segment.ts = ts; - segment.encode(&mut self.buf); - } - self.acklist.clear(); - - Ok(()) - } - - async fn probe_wnd_size(&mut self) { - // probe window size (if remote window size equals zero) - if self.rmt_wnd == 0 { - if self.probe_wait == 0 { - self.probe_wait = KCP_PROBE_INIT; - self.ts_probe = self.current + self.probe_wait; - } else { - if timediff(self.current, self.ts_probe) >= 0 && self.probe_wait < KCP_PROBE_INIT { - self.probe_wait = KCP_PROBE_INIT; - } - self.probe_wait += self.probe_wait / 2; - if self.probe_wait > KCP_PROBE_LIMIT { - self.probe_wait = KCP_PROBE_LIMIT; - } - self.ts_probe = self.current + self.probe_wait; - self.probe |= KCP_ASK_SEND; - } - } else { - self.ts_probe = 0; - self.probe_wait = 0; - } - } - - async fn _flush_probe_commands(&mut self, cmd: u8, segment: &mut KcpSegment) -> KcpResult<()> { - segment.cmd = cmd; - if self.buf.len() + KCP_OVERHEAD > self.mtu as usize { - self.output.write_all(&self.buf).await?; - self.buf.clear(); - } - segment.encode(&mut self.buf); - Ok(()) - } - - async fn flush_probe_commands(&mut self, segment: &mut KcpSegment) -> KcpResult<()> { - // flush window probing commands - if (self.probe & KCP_ASK_SEND) != 0 { - self._flush_probe_commands(KCP_CMD_WASK, segment).await?; - } - - // flush window probing commands - if (self.probe & KCP_ASK_TELL) != 0 { - self._flush_probe_commands(KCP_CMD_WINS, segment).await?; - } - self.probe = 0; - Ok(()) - } - - /// Flush pending ACKs - pub async fn flush_ack(&mut self) -> KcpResult<()> { - if !self.updated { - debug!("flush updated() must be called at least once"); - return Err(KcpErr::NeedUpdate.into()); - } - - let mut segment = KcpSegment { - conv: self.conv, - cmd: KCP_CMD_ACK, - wnd: self.wnd_unused(), - una: self.rcv_nxt, - ..Default::default() - }; - - self._flush_ack(&mut segment).await - } - - /// Flush pending data in buffer. - pub async fn flush(&mut self) -> KcpResult<()> { - if !self.updated { - debug!("flush updated() must be called at least once"); - return Err(KcpErr::NeedUpdate.into()); - } - - let mut segment = KcpSegment { - conv: self.conv, - cmd: KCP_CMD_ACK, - wnd: self.wnd_unused(), - una: self.rcv_nxt, - ..Default::default() - }; - - self._flush_ack(&mut segment).await?; - self.probe_wnd_size().await; - self.flush_probe_commands(&mut segment).await?; - - // println!("SNDBUF size {}", self.snd_buf.len()); - - // calculate window size - let mut cwnd = cmp::min(self.snd_wnd, self.rmt_wnd); - if !self.nocwnd { - cwnd = cmp::min(self.cwnd, cwnd); - } - - // move data from snd_queue to snd_buf - while timediff(self.snd_nxt, self.snd_una + cwnd as u32) < 0 { - match self.snd_queue.pop_front() { - Some(mut new_segment) => { - new_segment.conv = self.conv; - new_segment.cmd = KCP_CMD_PUSH; - new_segment.wnd = segment.wnd; - new_segment.ts = self.current; - new_segment.sn = self.snd_nxt; - self.snd_nxt += 1; - new_segment.una = self.rcv_nxt; - new_segment.resendts = self.current; - new_segment.rto = self.rx_rto; - new_segment.fastack = 0; - new_segment.xmit = 0; - self.snd_buf.push_back(new_segment); - } - None => break, - } - } - - // calculate resent - let resent = if self.fastresend > 0 { - self.fastresend - } else { - u32::max_value() - }; - - let rtomin = if !self.nodelay { self.rx_rto >> 3 } else { 0 }; - - let mut lost = false; - let mut change = 0; - - for snd_segment in &mut self.snd_buf { - let mut need_send = false; - - if snd_segment.xmit == 0 { - need_send = true; - snd_segment.xmit += 1; - snd_segment.rto = self.rx_rto; - snd_segment.resendts = self.current + snd_segment.rto + rtomin; - } else if timediff(self.current, snd_segment.resendts) >= 0 { - need_send = true; - snd_segment.xmit += 1; - self.xmit += 1; - if !self.nodelay { - snd_segment.rto += self.rx_rto; - } else { - snd_segment.rto += self.rx_rto / 2; - } - snd_segment.resendts = self.current + snd_segment.rto; - lost = true; - } else if snd_segment.fastack >= resent { - need_send = true; - snd_segment.xmit += 1; - snd_segment.fastack = 0; - snd_segment.resendts = self.current + snd_segment.rto; - change += 1; - } - - if need_send { - snd_segment.ts = self.current; - snd_segment.wnd = segment.wnd; - snd_segment.una = self.rcv_nxt; - - let need = KCP_OVERHEAD + snd_segment.data.len(); - - if self.buf.len() + need > self.mtu as usize { - self.output.write_all(&self.buf).await?; - self.buf.clear(); - } - - snd_segment.encode(&mut self.buf); - - if snd_segment.xmit >= self.dead_link { - self.state = -1; - } - } - } - - // Flush all data in buffer - if !self.buf.is_empty() { - self.output.write_all(&self.buf).await?; - self.buf.clear(); - } - - // update ssthresh - if change > 0 { - let inflight = self.snd_nxt - self.snd_una; - self.ssthresh = inflight as u16 / 2; - if self.ssthresh < KCP_THRESH_MIN { - self.ssthresh = KCP_THRESH_MIN; - } - self.cwnd = self.ssthresh + resent as u16; - self.incr = self.cwnd as u32 * self.mss; - } - - if lost { - self.ssthresh = cwnd / 2; - if self.ssthresh < KCP_THRESH_MIN { - self.ssthresh = KCP_THRESH_MIN; - } - self.cwnd = 1; - self.incr = self.mss; - } - - if self.cwnd < 1 { - self.cwnd = 1; - self.incr = self.mss; - } - - Ok(()) - } - - pub fn force_close(&mut self) -> KcpResult<()> { - log::debug!("force close {}", self.conv); - self.close_state = KCP_CLS_YES; - self.close_last = std::time::Instant::now(); - Ok(()) - } - - pub fn close(&mut self) -> KcpResult<()> { - log::debug!("safe close {}", self.conv); - - if self.close_state == KCP_CLS_NO { - self.close_state = KCP_CLS_WSND; - self.close_last = std::time::Instant::now(); - Ok(()) - } else { - Err(KcpErr::ConnectionReset.into()) - } - } - - pub fn closed(&self) -> bool { - self.close_state == KCP_CLS_YES - } - - pub fn close_state(&self) -> u8 { - self.close_state - } - - /// Update state every 10ms ~ 100ms. - /// - /// Or you can ask `check` when to call this again. - pub async fn update(&mut self, current: u32) -> KcpResult<()> { - if self.close_state == KCP_CLS_YES { - return Err(KcpErr::ConnectionReset.into()); - } - - self.current = current; - - if !self.updated { - self.updated = true; - self.ts_flush = self.current; - } - - let mut slap = timediff(self.current, self.ts_flush); - - if slap >= 10000 || slap < -10000 { - self.ts_flush = self.current; - slap = 0; - } - - if slap >= 0 { - self.ts_flush += self.interval; - if timediff(self.current, self.ts_flush) >= 0 { - self.ts_flush = self.current + self.interval; - } - self.flush().await?; - } - - if self.close_state == KCP_CLS_WSND { - let _ = self.flush_ack().await; - } - - if self.close_state == KCP_CLS_WSND && self.wait_snd() == 0 { - log::debug!("wait close KCP_CLS_WSND"); - let mut seg = KcpSegment::new_with_data(Default::default()); - - seg.conv = self.conv; - seg.cmd = KCP_CMD_CLS; - - let mut buf = BytesMut::new(); - - seg.encode(&mut buf); - self.output.write_all(&buf[..seg.encoded_len()]).await?; - self.close_last = std::time::Instant::now(); - - self.snd_queue.push_back(seg); - - self.close_state = KCP_CLS_WAIT; - } - - if self.close_state == KCP_CLS_WRCV { - log::debug!("wait close KCP_CLS_WRCV"); - let mut seg = KcpSegment { - conv: self.conv, - cmd: KCP_CMD_CLS, - ..Default::default() - }; - - let mut buf = BytesMut::new(); - - seg.encode(&mut buf); - self.output.write_all(&buf[..seg.encoded_len()]).await?; - - self.close_state = KCP_CLS_YES; - } - - if self.close_state == KCP_CLS_WAIT && self.close_last.elapsed().as_secs() > 10 { - return Err(std::io::ErrorKind::TimedOut.into()); - } - - Ok(()) - } - - /// Determine when you should call `update`. - /// Return when you should invoke `update` in millisec, if there is no `input`/`send` calling. - /// You can call `update` in that time without calling it repeatly. - pub fn check(&self, current: u32) -> u32 { - if !self.updated { - return 0; - } - - let mut ts_flush = self.ts_flush; - let mut tm_packet = u32::max_value(); - - if timediff(current, ts_flush) >= 10000 || timediff(current, ts_flush) < -10000 { - ts_flush = current; - } - - if timediff(current, ts_flush) >= 0 { - // return self.interval; - return 0; - } - - let tm_flush = timediff(ts_flush, current) as u32; - for seg in &self.snd_buf { - let diff = timediff(seg.resendts, current); - if diff <= 0 { - // return self.interval; - return 0; - } - if (diff as u32) < tm_packet { - tm_packet = diff as u32; - } - } - - cmp::min(cmp::min(tm_packet, tm_flush), self.interval) - } - - /// Change MTU size, default is 1400 - /// - /// MTU = Maximum Transmission Unit - pub fn set_mtu(&mut self, mtu: usize) -> KcpResult<()> { - if mtu < 50 || mtu < KCP_OVERHEAD { - debug!("set_mtu mtu={} invalid", mtu); - return Err(KcpErr::InvalidMtu(mtu).into()); - } - - self.mtu = mtu; - self.mss = (self.mtu - KCP_OVERHEAD) as u32; - - let additional = ((mtu + KCP_OVERHEAD) * 3) as isize - self.buf.capacity() as isize; - if additional > 0 { - self.buf.reserve(additional as usize); - } - - Ok(()) - } - - /// Get MTU - pub fn mtu(&self) -> usize { - self.mtu - } - - /// Set check interval - pub fn set_interval(&mut self, mut interval: u32) { - if interval > 5000 { - interval = 5000; - } else if interval < 10 { - interval = 10; - } - self.interval = interval; - } - - /// Set nodelay - /// - /// fastest config: nodelay(true, 20, 2, true) - /// - /// `nodelay`: default is disable (false) - /// `interval`: internal update timer interval in millisec, default is 100ms - /// `resend`: 0:disable fast resend(default), 1:enable fast resend - /// `nc`: `false`: normal congestion control(default), `true`: disable congestion control - pub fn set_nodelay(&mut self, nodelay: bool, interval: i32, resend: i32, nc: bool) { - if nodelay { - self.nodelay = true; - self.rx_minrto = KCP_RTO_NDL; - } else { - self.nodelay = false; - self.rx_minrto = KCP_RTO_MIN; - } - - match interval { - interval if interval < 10 => self.interval = 10, - interval if interval > 5000 => self.interval = 5000, - _ => self.interval = interval as u32, - } - - if resend >= 0 { - self.fastresend = resend as u32; - } - - self.nocwnd = nc; - } - - /// Set `wndsize` - /// set maximum window size: `sndwnd=32`, `rcvwnd=32` by default - pub fn set_wndsize(&mut self, sndwnd: u16, rcvwnd: u16) { - if sndwnd > 0 { - self.snd_wnd = sndwnd as u16; - } - - if rcvwnd > 0 { - self.rcv_wnd = cmp::max(rcvwnd, KCP_WND_RCV) as u16; - } - } - - /// `snd_wnd` Send window - pub fn snd_wnd(&self) -> u16 { - self.snd_wnd - } - - /// `rcv_wnd` Receive window - pub fn rcv_wnd(&self) -> u16 { - self.rcv_wnd - } - - /// Get `waitsnd`, how many packet is waiting to be sent - pub fn wait_snd(&self) -> usize { - self.snd_buf.len() + self.snd_queue.len() - } - - /// Set `rx_minrto` - pub fn set_rx_minrto(&mut self, rto: u32) { - self.rx_minrto = rto; - } - - /// Set `fastresend` - pub fn set_fast_resend(&mut self, fr: u32) { - self.fastresend = fr; - } - - /// KCP header size - pub fn header_len() -> usize { - KCP_OVERHEAD as usize - } - - /// Enabled stream or not - pub fn is_stream(&self) -> bool { - self.stream - } - - /// Maximum Segment Size - pub fn mss(&self) -> u32 { - self.mss - } - - /// Set maximum resend times - pub fn set_maximum_resend_times(&mut self, dead_link: u32) { - self.dead_link = dead_link; - } - - /// Check if KCP connection is dead (resend times excceeded) - pub fn is_dead_link(&self) -> bool { - self.state != 0 - } -} - -impl Display for Kcp { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "conv={}", self.conv) - } -} diff --git a/src/net/mod.rs b/src/net/mod.rs deleted file mode 100644 index fffef7d..0000000 --- a/src/net/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -pub mod udp; - -#[cfg(feature = "fuso-kcp")] -pub mod kcp; - -#[cfg(feature = "fuso-quic")] -pub mod quic; - -#[cfg(feature = "fuso-socks5")] -pub mod socks; - -pub mod tun; - -#[cfg(feature = "fuso-proxy")] -pub mod proxy; - -pub mod penetrate; diff --git a/src/net/penetrate/accepter.rs b/src/net/penetrate/accepter.rs deleted file mode 100644 index 413861c..0000000 --- a/src/net/penetrate/accepter.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::{pin::Pin, task::Poll}; - -use crate::{Accepter, Address, NetSocket, Stream}; - -pub enum Pen { - Visit(S), - Client(S), -} - -pub struct PenetrateAccepter { - visit: SA, - client: CA, -} - -impl PenetrateAccepter -where - CA: Accepter + Unpin + 'static, - SA: Accepter + Unpin + 'static, -{ - pub fn new(visit: SA, client: CA) -> Self { - Self { visit, client } - } -} - -impl NetSocket for PenetrateAccepter -where - CA: NetSocket, - SA: NetSocket, -{ - fn local_addr(&self) -> crate::Result { - Ok(self.visit.local_addr()? + self.client.local_addr()?) - } - - fn peer_addr(&self) -> crate::Result

{ - Ok(self.visit.peer_addr()? + self.client.peer_addr()?) - } -} - -impl Accepter for PenetrateAccepter -where - CA: Accepter + Unpin, - SA: Accepter + Unpin, - S: Stream, -{ - type Stream = Pen; - - fn poll_accept( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - let mut poll_next = true; - - while poll_next { - match Pin::new(&mut self.visit).poll_accept(cx)? { - std::task::Poll::Ready(visit) => return Poll::Ready(Ok(Pen::Visit(visit))), - std::task::Poll::Pending => {} - } - - match Pin::new(&mut self.client).poll_accept(cx)? { - std::task::Poll::Ready(client) => return Poll::Ready(Ok(Pen::Client(client))), - std::task::Poll::Pending => { - poll_next = false; - } - } - } - - return Poll::Pending; - } -} diff --git a/src/net/penetrate/bridge/mod.rs b/src/net/penetrate/bridge/mod.rs deleted file mode 100644 index 7d523fc..0000000 --- a/src/net/penetrate/bridge/mod.rs +++ /dev/null @@ -1,234 +0,0 @@ -use std::{net::IpAddr, pin::Pin, sync::Arc, time::Duration}; - -use crate::{ - client::Client, - generator::Generator, - io, - protocol::{AsyncRecvPacket, AsyncSendPacket, Bind, Poto, ToBytes, TryToPoto}, - select::Select, - server::Handshake, - time, Accepter, AccepterExt, Address, ClientProvider, DecorateProvider, Executor, Fuso, Kind, - Processor, Provider, Serve, Socket, Stream, WrappedProvider, -}; - -type BoxedFuture = Pin> + Send + 'static>>; - -pub struct Bridge { - socket: Socket, - client: Client, - bridge_handshake: Option>>, - accepter_provider: SP, -} - -impl Fuso> { - pub fn using_bridge, SP, HF>( - self, - socket: A, - provider: SP, - handshake: HF, - ) -> Bridge - where - HF: Provider>)>> - + Send - + Sync - + 'static, - { - Bridge { - socket: socket.into(), - client: self.0, - bridge_handshake: Some(Arc::new(WrappedProvider::wrap(handshake))), - accepter_provider: provider, - } - } -} - -impl Bridge -where - E: Executor + Send + Sync + 'static, - P: Provider> + Send + Sync + 'static, - SP: Provider> + Send + Sync + 'static, - A: Accepter + Send + Sync + Unpin + 'static, - S: Stream + Send + Sync + 'static, - G: Generator>> + Unpin + Send + 'static, - H: Provider<(S, Processor, S, ()>), Output = BoxedFuture> - + Send - + Sync - + 'static, -{ - async fn run_bridge( - client: S, - accepter: Arc, - handshake: Option>)>>, - client_provider: ClientProvider

, - upstream: Socket, - executor: Arc, - bridge_handshake: Option>>, - ) -> crate::Result<()> { - let (mut client, client_decorate) = match bridge_handshake.as_ref() { - Some(handshake) => handshake.call(client).await?, - None => (client, None), - }; - - let poto = client.recv_packet().await?.try_poto()?; - - let (upstream, client_addr, proxy_accepter, upstream_decorate) = match poto { - Poto::Bind(Bind::Setup(client_addr, visitor_addr)) => { - let upstream_addr = upstream; - let upstream = client_provider.call(upstream_addr.clone()).await?; - - let (mut upstream, decorate) = match handshake { - Some(handshake) => handshake.call(upstream).await?, - None => (upstream, None), - }; - - let poto = - Poto::Bind(Bind::Setup(client_addr.clone(), visitor_addr.clone())).bytes(); - - upstream.send_packet(&poto).await?; - - let poto = upstream.recv_packet().await?.try_poto()?; - - match poto { - Poto::Bind(Bind::Success(mut client_accepter, visitor_accepter)) => { - let proxy_accepter = accepter.call(client_addr).await?; - let poto = Poto::Bind(Bind::Success( - proxy_accepter.local_addr()?, - visitor_accepter, - )) - .bytes(); - - client.send_packet(&poto).await?; - - if client_accepter.is_ip_unspecified() { - client_accepter.from_set_host(upstream_addr.addr()); - } - - if client_accepter.is_ip_unspecified() { - client_accepter.set_ip([127, 0, 0, 1]); - } - - log::info!( - "bridge from {} to {} -> {}", - client.peer_addr()?, - proxy_accepter.local_addr()?, - client_accepter - ); - - (upstream, client_accepter, proxy_accepter, decorate) - } - _ => { - let poto = poto.bytes(); - return client.send_packet(&poto).await; - } - } - } - poto => { - let poto = poto.bytes(); - return client.send_packet(&poto).await; - } - }; - - let f1 = io::forward(client, upstream); - - let f2 = async move { - let mut proxy_accepter = proxy_accepter; - let client_decorate = client_decorate; - let upstream_decorate = upstream_decorate; - - loop { - let stream = proxy_accepter.accept().await?; - - let socket = match stream.peer_addr()? { - Address::One(socket) => socket, - Address::Many(_) => unsafe { std::hint::unreachable_unchecked() }, - }; - - let upstream = match client_addr.select(&socket) { - Ok(socket) => client_provider.call(socket), - Err(e) => { - log::warn!("failed to bridge: {}", e); - continue; - } - }; - - let client_decorate = client_decorate.clone(); - let upstream_decorate = upstream_decorate.clone(); - - let forward_fn = move || async move { - let stream = client_decorate.call(stream).await?; - - let upstream = upstream_decorate.call(upstream.await?).await?; - - log::debug!( - "bridge {} -> {}", - stream.peer_addr()?, - upstream.peer_addr()? - ); - - io::forward(stream, upstream).await - }; - - executor.spawn(async move { - let result = match time::wait_for(Duration::from_secs(10), forward_fn()).await { - Ok(Err(e)) => Err(e), - Err(e) => Err(e), - _ => Ok(()), - }; - - if let Err(e) = result { - log::debug!("failed to bridge {}", e); - } - }); - } - }; - - Select::select(f1, f2).await - } - - async fn run_async(self) -> crate::Result<()> { - let bridge_socket = self.socket; - let accepter_provider = Arc::new(self.accepter_provider); - let executor = self.client.executor.clone(); - let server_socket = self.client.socket.clone(); - let handshake = self.client.handshake.clone(); - let client_provider = self.client.client_provider.clone(); - let bridge_handshake = self.bridge_handshake.clone(); - - if bridge_socket.eq(&server_socket) - || (bridge_socket.is_ip_unspecified() - && server_socket.ip().eq(&Some(IpAddr::from([127, 0, 0, 1])))) - { - return Err(Kind::AddressLoop(bridge_socket).into()); - } - - let bridge = async move { - let mut accepter = accepter_provider.call(bridge_socket).await?; - - log::info!("bridge listening on {}", accepter.local_addr()?); - - loop { - let client = accepter.accept().await?; - let handshake = handshake.clone(); - executor.spawn(Self::run_bridge( - client, - accepter_provider.clone(), - handshake, - client_provider.clone(), - server_socket.clone(), - executor.clone(), - bridge_handshake.clone(), - )); - } - }; - - let client = Fuso(self.client); - - Select::select(client.run(), bridge).await - } - - pub fn run(self) -> Fuso { - Fuso(Serve { - fut: Box::pin(self.run_async()), - }) - } -} diff --git a/src/net/penetrate/builder.rs b/src/net/penetrate/builder.rs deleted file mode 100644 index 6a35c15..0000000 --- a/src/net/penetrate/builder.rs +++ /dev/null @@ -1,262 +0,0 @@ -use std::{pin::Pin, sync::Arc, time::Duration}; - -use crate::{ - client::{Client, ClientBuilder, Route}, - guard::Fallback, - server::{Server, ServerBuilder}, - Accepter, Executor, Fuso, Platform, Provider, Socket, Stream, WrappedProvider, -}; - -use super::{ - client::PenetrateClientProvider, - server::{Config, Peer, PenetrateProvider}, - PenetrateObserver, -}; - -type BoxedFuture = Pin> + Send + 'static>>; - -pub struct PenetrateServerBuilder { - is_mixed: bool, - max_wait_time: Duration, - heartbeat_timeout: Duration, - read_timeout: Option, - write_timeout: Option, - fallback_strict_mode: bool, - server_builder: ServerBuilder, -} - -pub struct PenetrateClientBuilder { - /// 服务名 - name: String, - /// 上游地址,也就是服务端地址 - upstream: Socket, - /// 下游地址, 也就是本地需要映射的地址 - downstream: Socket, - channel_port: u16, - /// 创建连接等待时间, 超过视为超时 - maximum_wait: Option, - /// 重连延时 - reconnect_delay: Option, - /// 重连尝试次数,如果为None那么永不停止 - maximum_retries: Option, - /// 心跳延时 - heartbeat_delay: Option, - /// 是否启用 kcp - enable_kcp: bool, - /// 是否启用socks5 - enable_socks5: bool, - /// socks5用户名 - socks_username: Option, - /// socks5密码 - socks_password: Option, - /// 是否启用socks5 udp转发 - enable_socks5_udp: bool, - /// builder ... - client_builder: ClientBuilder, -} - -impl ServerBuilder { - pub fn using_penetrate(self) -> PenetrateServerBuilder { - PenetrateServerBuilder { - is_mixed: self.is_mixed, - write_timeout: None, - read_timeout: None, - max_wait_time: Duration::from_secs(10), - heartbeat_timeout: Duration::from_secs(60), - fallback_strict_mode: true, - server_builder: self, - } - } -} - -impl PenetrateServerBuilder -where - E: Executor + 'static, - A: Accepter + Unpin + Send + 'static, - S: Stream + Send + Sync + 'static, - P: Provider> + Send + Sync + 'static, - O: PenetrateObserver + Send + Sync + 'static, -{ - pub fn read_timeout(mut self, time: Option) -> Self { - self.read_timeout = time; - self - } - - pub fn write_timeout(mut self, time: Option) -> Self { - self.write_timeout = time; - self - } - - pub fn max_wait_time(mut self, time: Duration) -> Self { - self.max_wait_time = time.min(Duration::from_secs(10)); - self - } - - pub fn heartbeat_timeout(mut self, time: Duration) -> Self { - self.heartbeat_timeout = time.min(Duration::from_secs(60)); - self - } - - pub fn enable_fallback_strict_mode(mut self) -> Self { - self.fallback_strict_mode = true; - self - } - - pub fn disable_fallback_strict_mode(mut self) -> Self { - self.fallback_strict_mode = false; - self - } - - pub fn build(self, mock: F) -> Fuso, P, S, O>> - where - F: Provider< - (Fallback, Arc), - Output = BoxedFuture>>, - > + Send - + Sync - + 'static, - { - self.server_builder.build(PenetrateProvider { - mock: Arc::new(WrappedProvider::wrap(mock)), - config: Config { - whoami: String::from("anonymous"), - is_mixed: self.is_mixed, - maximum_wait: self.max_wait_time, - heartbeat_delay: self.heartbeat_timeout, - read_timeout: self.read_timeout, - write_timeout: self.write_timeout, - fallback_strict_mode: self.fallback_strict_mode, - enable_socks: false, - enable_socks_udp: false, - real_ip: false, - socks5_password: None, - socks5_username: None, - platform: Default::default(), - }, - }) - } -} - -impl ClientBuilder { - pub fn using_penetrate>( - self, - upstream: U, - downstream: U, - ) -> PenetrateClientBuilder { - PenetrateClientBuilder { - name: String::from("anonymous"), - upstream: upstream.into(), - downstream: downstream.into(), - channel_port: 0, - client_builder: self, - maximum_wait: None, - maximum_retries: None, - reconnect_delay: None, - heartbeat_delay: None, - enable_kcp: false, - enable_socks5: false, - socks_username: None, - socks_password: None, - enable_socks5_udp: false, - } - } -} - -impl PenetrateClientBuilder -where - E: Executor + 'static, - CF: Provider> + Send + Sync + 'static, - S: Stream + Send + 'static, -{ - pub fn reconnect_delay(mut self, delay: Duration) -> Self { - self.reconnect_delay = Some(delay.min(Duration::from_secs(2))); - self - } - - pub fn set_name(mut self, name: String) -> Self { - self.name = name; - self - } - - pub fn enable_kcp(mut self, enable: bool) -> Self { - self.enable_kcp = enable; - self - } - - pub fn enable_socks5(mut self, enable: bool) -> Self { - self.enable_socks5 = enable; - self - } - - pub fn enable_socks5_udp(mut self, enable: bool) -> Self { - self.enable_socks5_udp = enable; - self - } - - pub fn set_socks5_username(mut self, username: Option) -> Self { - self.socks_username = username; - self - } - - pub fn set_socks5_password(mut self, password: Option) -> Self { - self.socks_password = password; - self - } - - pub fn maximum_retries(mut self, maximum_retries: Option) -> Self { - self.maximum_retries = maximum_retries; - self - } - - pub fn heartbeat_delay(mut self, delay: Duration) -> Self { - self.heartbeat_delay = Some(delay.min(Duration::from_secs(30))); - self - } - - pub fn maximum_wait(mut self, time: Duration) -> Self { - self.maximum_wait = Some(time.min(Duration::from_secs(10))); - self - } - - pub fn channel_port(mut self, port: u16) -> Self { - self.channel_port = port; - self - } - - pub fn build, C>( - self, - server_socket: A, - connector: C, - ) -> Fuso, CF, S>> - where - C: Provider>> + Unpin + Send + Sync + 'static, - { - ClientBuilder { - executor: self.client_builder.executor, - retry_delay: self.reconnect_delay, - maximum_retries: self.maximum_retries, - handshake: self.client_builder.handshake, - client_provider: self.client_builder.client_provider, - } - .build( - server_socket, - PenetrateClientProvider { - forward: (self.upstream, self.downstream), - connector_provider: Arc::new(connector), - config: super::client::Config { - name: self.name, - channel_port: self.channel_port, - maximum_wait: self.maximum_wait.unwrap_or(Duration::from_secs(10)), - heartbeat_delay: self.heartbeat_delay.unwrap_or(Duration::from_secs(30)), - enable_kcp: self.enable_kcp, - enable_socks5: self.enable_socks5, - socks_username: self.socks_username, - socks_password: self.socks_password, - enable_socks5_udp: self.enable_socks5_udp, - version: String::from(env!("CARGO_PKG_VERSION")), - platform: Platform::default(), - }, - }, - ) - } -} diff --git a/src/net/penetrate/client.rs b/src/net/penetrate/client.rs deleted file mode 100644 index 7fb3618..0000000 --- a/src/net/penetrate/client.rs +++ /dev/null @@ -1,397 +0,0 @@ -use std::pin::Pin; -use std::sync::Arc; -use std::time::Duration; -use std::{future::Future, task::Poll}; - -use serde::{Deserialize, Serialize}; - -use crate::io::{ReadHalf, WriteHalf}; -use crate::protocol::IntoPacket; -use crate::{ - client::Route, - generator::Generator, - protocol::{AsyncRecvPacket, AsyncSendPacket, Bind, Poto, ToBytes, TryToPoto}, - Kind, Socket, Stream, {ClientProvider, Provider}, -}; - -use crate::{io, join, time, Address, Processor, Platform}; - -type BoxedFuture = Pin> + Send + 'static>>; - -macro_rules! async_connect { - ($writer: expr, $connector: expr, $id: expr, $socket: expr) => {{ - let socket = $socket.clone(); - let mut writer = $writer.clone(); - async move { - log::debug!("try connect to {}", socket); - match $connector.call(socket).await { - Ok(ok) => Ok(ok), - Err(err) => { - let poto = Poto::MapError($id, err.to_string()).bytes(); - return match writer.send_packet(&poto).await { - Ok(_) => Err(err), - Err(err) => Err(err), - }; - } - } - } - }}; -} - -macro_rules! default_socket { - ($addr: expr, $default: expr) => {{ - if $addr.is_ip_unspecified() { - $addr.from_set_host($default); - } - - if $addr.is_ip_unspecified() { - $addr.set_ip([127, 0, 0, 1]); - } - }}; -} - - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Config { - /// 服务名 - pub(super) name: String, - /// 创建连接等待时间, 超过视为超时 - pub(super) maximum_wait: Duration, - /// 心跳延时 - pub(super) heartbeat_delay: Duration, - /// 是否启用 kcp - pub(super) enable_kcp: bool, - /// 是否启用socks5 - pub(super) enable_socks5: bool, - /// socks5用户名 - pub(super) socks_username: Option, - /// socks5密码 - pub(super) socks_password: Option, - /// 客户端与服务端通信端口 - pub(super) channel_port: u16, - /// 是否启用socks5 udp转发 - pub(super) enable_socks5_udp: bool, - pub(super) version: String, - pub(super) platform: Platform -} - -pub struct PenetrateClientProvider { - pub config: Config, - pub forward: (Socket, Socket), - pub connector_provider: Arc, -} - -enum State { - Leave(Socket), - Ready(BoxedFuture<()>), - Map(u32, Socket), - Error(crate::Error), -} - -pub struct PenetrateClient { - reader: ReadHalf, - config: Config, - forward: (Address, Socket), - writer: WriteHalf, - futures: Vec>, - processor: Processor, S, ()>, - connector_provider: Arc, -} - -impl Provider<(S, Processor, S, ()>)> for PenetrateClientProvider -where - P: Provider> + Send + Sync + 'static, - C: Provider>> + Send + Sync + 'static, - S: Stream + Send + 'static, -{ - type Output = BoxedFuture>; - - fn call(&self, (stream, processor): (S, Processor, S, ()>)) -> Self::Output { - let socket = self.forward.clone(); - let config = self.config.clone(); - - let connector_provider = self.connector_provider.clone(); - - Box::pin(async move { - let mut stream = stream; - let (visit_addr, route_addr) = socket; - let bind = Poto::Bind(Bind::Setup( - Socket::tcp(config.channel_port).if_stream_mixed(config.enable_kcp || config.enable_socks5_udp), - visit_addr.clone(), - )) - .bytes(); - - if let Err(e) = stream.send_packet(&bind).await { - log::error!("failed to send listen message to server err={}", e); - return Err(e); - } - - let message = match stream.recv_packet().await { - Ok(packet) => packet.try_poto(), - Err(e) => { - log::error!("the listen message was sent successfully, but the server seems to have an error err={}", e); - return Err(e); - } - }; - - if let Err(e) = message.as_ref() { - log::error!("received a response from the server, but the message format is incorrect err={}", e); - return Err(unsafe { message.unwrap_err_unchecked() }); - } - - let message = unsafe { message.unwrap_unchecked() }; - - match message { - Poto::Bind(Bind::Success(mut server_addr, mut visit_addr)) => { - let copy_cfg = config.clone(); - let config_packet = config.into_packet(); - let config_bytes = config_packet.encode(); - - if let Err(e) = stream.send_packet(&config_bytes).await { - log::warn!("server error {}", e); - return Err(e); - }; - - let configured = stream.recv_packet().await; - - if let Err(e) = configured.as_ref() { - log::warn!("server error {}", e); - return Err(unsafe { configured.unwrap_err_unchecked() }); - } - - let configured = unsafe { configured.unwrap_unchecked() }; - let configured = bincode::deserialize::(&configured.payload); - if let Err(e) = configured.as_ref() { - log::warn!("server configuration error {}", e); - return Err(e.to_string().into()); - }; - - let configured = unsafe { configured.unwrap_unchecked() }; - - if configured.ne("YES") { - log::warn!("server configuration error {}", configured); - return Err(configured.into()); - }; - - default_socket!(visit_addr, processor.default_socket()); - default_socket!(server_addr, processor.default_socket()); - - log::info!("the server is bound to {}", server_addr); - log::info!("please visit {}", visit_addr); - - Ok(PenetrateClient::new( - (server_addr, route_addr), - stream, - copy_cfg, - processor, - connector_provider, - )) - } - Poto::Bind(Bind::Failed(fail)) => { - log::error!( - "an error occurred while creating the listener on the server {}", - fail, - ); - Err(Kind::Message(fail).into()) - } - message => { - log::error!( - "The message returned by the server cannot be accepted msg={}", - message - ); - Err(Kind::Unexpected(format!("{}", message)).into()) - } - } - }) - } -} - -impl PenetrateClient -where - P: Provider> + Send + Sync + 'static, - C: Provider>> + Send + Sync + 'static, - S: Stream + Send + 'static, -{ - pub fn new( - socket: (Address, Socket), - conn: S, - config: Config, - processor: Processor, S, ()>, - connector_provider: Arc, - ) -> Self { - let (reader, writer) = io::split(conn); - - let fut1 = Box::pin(Self::register_server_handle(reader.clone())); - let fut2 = Box::pin(Self::guard_server_heartbeat( - writer.clone(), - config.maximum_wait, - )); - - Self { - forward: socket, - processor, - config, - connector_provider, - reader: reader.clone(), - writer: writer.clone(), - futures: vec![fut1, fut2], - } - } - - async fn guard_server_heartbeat( - mut writer: WriteHalf, - timeout: Duration, - ) -> crate::Result { - let ping = Poto::Ping.bytes(); - - loop { - if let Err(e) = writer.send_packet(&ping).await { - log::error!("failed to send heartbeat to server err={}", e); - return Ok(State::Error(e)); - } - - time::sleep(timeout).await; - } - } - - async fn register_server_handle(mut reader: ReadHalf) -> crate::Result { - loop { - let message = match reader.recv_packet().await { - Ok(packet) => packet.try_poto(), - Err(e) => return Ok(State::Error(e)), - }; - - if let Err(e) = message.as_ref() { - log::warn!("server error {}", e); - return Ok(State::Error(unsafe { message.unwrap_err_unchecked() })); - } - - let message = unsafe { message.unwrap_unchecked() }; - - match message { - Poto::Map(id, socket) => { - break Ok(State::Map(id, socket)); - } - message => { - log::trace!("received server message {:?}", message); - } - } - } - } - - fn start_async_forward( - &self, - id: u32, - server_socket: Socket, - target_socket: Socket, - ) -> BoxedFuture { - let s1_connector = self.processor.clone(); - let s2_connector = self.connector_provider.clone(); - let maximum_wait = self.config.maximum_wait.clone(); - - let server_fut = async_connect!(self.writer, s1_connector, id, server_socket); - let client_fut = async_connect!(self.writer, s2_connector, id, target_socket); - let server_writer = self.writer.clone(); - let processor = self.processor.clone(); - - let future = async move { - let mut server_writer = server_writer; - let result = - time::wait_for(maximum_wait, join::join_output(server_fut, client_fut)).await; - - let result = match result { - Err(e) => Err(e), - Ok(r) => r, - }; - - let (s1, s2) = result?; - - let mut s1 = processor.decorate(s1).await?; - - let poto = Poto::Map(id, target_socket).bytes(); - - if let Err(e) = s1.send_packet(&poto).await { - let message = Poto::MapError(id, e.to_string()).bytes(); - if let Err(e) = server_writer.send_packet(&message).await { - Ok(State::Error(e)) - } else { - Err(e) - } - } else { - Ok(State::Ready({ - match s2 { - Route::Forward(s2) => Box::pin(io::forward(s1, s2)), - Route::Provider(s2) => s2.call(s1), - } - })) - } - }; - - Box::pin(future) - } -} - -impl Generator for PenetrateClient -where - CF: Provider> + Send + Sync + 'static, - C: Provider>> + Send + Sync + 'static, - S: Stream + Send + 'static, -{ - type Output = Option>; - fn poll_generate( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context, - ) -> std::task::Poll> { - let mut futures = std::mem::replace(&mut self.futures, Default::default()); - - while let Some(mut future) = futures.pop() { - match Pin::new(&mut future).poll(cx) { - Poll::Pending => self.futures.push(future), - Poll::Ready(Ok(State::Error(e))) => { - log::warn!("server stops talking"); - return Poll::Ready(Err(e)); - } - Poll::Ready(Ok(State::Leave(socket))) => { - log::warn!("leave {}", socket); - } - Poll::Ready(Ok(State::Map(id, target_socket))) => { - log::debug!("{}", target_socket); - - let (server, local) = self.forward.clone(); - let target_socket = target_socket.default_or(local); - let server_writer = self.writer.clone(); - - let future = match server.select(&target_socket) { - Ok(server_socket) => { - self.start_async_forward(id, server_socket, target_socket) - } - Err(e) => Box::pin(async move { - let mut server_writer = server_writer; - let poto = Poto::MapError(id, e.to_string()).bytes(); - match server_writer.send_packet(&poto).await { - Ok(()) => Ok(State::Leave(target_socket)), - Err(e) => Ok(State::Error(e)), - } - }), - }; - - let fut2 = Box::pin(Self::register_server_handle(self.reader.clone())); - - futures.push(future); - futures.push(fut2); - } - Poll::Ready(Ok(State::Ready(fut))) => { - self.futures.extend(futures); - return Poll::Ready(Ok(Some(fut))); - } - Poll::Ready(Err(e)) => { - log::trace!("{:?}", e); - } - } - } - - log::debug!("{} futures remaining", self.futures.len()); - - Poll::Pending - } -} diff --git a/src/net/penetrate/handshake/mod.rs b/src/net/penetrate/handshake/mod.rs deleted file mode 100644 index e4dfd43..0000000 --- a/src/net/penetrate/handshake/mod.rs +++ /dev/null @@ -1,162 +0,0 @@ -pub mod real_ip; - -use std::pin::Pin; - -use rsa::pkcs8::{DecodePublicKey, EncodePublicKey}; - -use crate::{ - compress::Lz4Compress, - encryption::{AESEncryptor, RSAEncryptor}, - ext::{AsyncReadExt, AsyncWriteExt}, - DecorateProvider, FusoStream, Provider, Stream, ToBoxStream, -}; - -type BoxedFuture = Pin> + Send + 'static>>; - -pub enum PenetrateRsaAndAesHandshake { - Server, - Client, -} - -pub struct PenetrateAesAndLz4Decorator { - iv: [u8; 16], - key: [u8; 16], -} - -impl PenetrateRsaAndAesHandshake { - pub fn server_handshake( - client: S, - ) -> BoxedFuture<(FusoStream, Option>)> - where - S: Stream + Unpin + Send + 'static, - { - Box::pin(async move { - let mut client = Lz4Compress::new(client); - let mut buf = [0u8; 4]; - client.read_exact(&mut buf).await?; - let len = u32::from_be_bytes(buf) as usize; - - let mut buf = Vec::with_capacity(len); - - unsafe { buf.set_len(len) } - - client.read_exact(&mut buf).await?; - - let priv_key = rsa::RsaPrivateKey::new(&mut rand::thread_rng(), 1024)?; - let publ_key = rsa::RsaPublicKey::from(&priv_key); - let client_publ_key = rsa::RsaPublicKey::from_public_key_der(&buf)?; - - let pem = publ_key.to_public_key_der()?; - let pem = pem.as_ref(); - let len = pem.len() as u32; - - client.write_all(&len.to_be_bytes()).await?; - client.write_all(pem).await?; - - let mut fuso_stream = RSAEncryptor::new(client, client_publ_key, priv_key); - - let mut iv = [0u8; 16]; - let mut key = [0u8; 16]; - - fuso_stream.read_exact(&mut iv).await?; - - fuso_stream.read_exact(&mut key).await?; - - log::trace!("iv: {:?}, key: {:?}", iv, key); - - Ok(( - fuso_stream.into_boxed_stream(), - Some(DecorateProvider::wrap(PenetrateAesAndLz4Decorator { - iv, - key, - })), - )) - }) - } - - pub fn client_handshake( - stream: S, - ) -> BoxedFuture<(FusoStream, Option>)> - where - S: Stream + Unpin + Send + 'static, - { - Box::pin(async move { - let mut stream = Lz4Compress::new(stream); - let priv_key = rsa::RsaPrivateKey::new(&mut rand::thread_rng(), 1024)?; - let publ_key = rsa::RsaPublicKey::from(&priv_key); - - let pem = publ_key.to_public_key_der()?; - let pem = pem.as_ref(); - - let len = pem.len() as u32; - - stream.write_all(&len.to_be_bytes()).await?; - stream.write_all(pem).await?; - - let mut buf = [0u8; 4]; - stream.read_exact(&mut buf).await?; - let len = u32::from_be_bytes(buf) as usize; - - let mut buf = Vec::with_capacity(len); - - unsafe { - buf.set_len(len); - } - - stream.read_exact(&mut buf).await?; - - let server_publ_key = rsa::RsaPublicKey::from_public_key_der(&buf)?; - - let mut fuso_stream = RSAEncryptor::new(stream, server_publ_key, priv_key); - - let mut iv = [0u8; 16]; - let mut key = [0u8; 16]; - - iv.fill_with(rand::random); - key.fill_with(rand::random); - - log::trace!("iv: {:?}, key: {:?}", iv, key); - - fuso_stream.write_all(&iv).await?; - fuso_stream.write_all(&key).await?; - - Ok(( - fuso_stream.into_boxed_stream(), - Some(DecorateProvider::wrap(PenetrateAesAndLz4Decorator { - iv, - key, - })), - )) - }) - } -} - -impl Provider for PenetrateRsaAndAesHandshake -where - S: Stream + Unpin + Send + 'static, -{ - type Output = BoxedFuture<(FusoStream, Option>)>; - - fn call(&self, client: S) -> Self::Output { - match self { - PenetrateRsaAndAesHandshake::Server => Self::server_handshake(client), - PenetrateRsaAndAesHandshake::Client => Self::client_handshake(client), - } - } -} - -impl Provider for PenetrateAesAndLz4Decorator -where - S: Stream + Unpin + Send + 'static, -{ - type Output = BoxedFuture; - fn call(&self, stream: S) -> Self::Output { - let iv = self.iv.clone(); - let key = self.key.clone(); - Box::pin(async move { - let lz4 = Lz4Compress::new(stream); - let aes = AESEncryptor::new(lz4, iv, key); - Ok(aes.into_boxed_stream()) - }) - } -} diff --git a/src/net/penetrate/handshake/real_ip.rs b/src/net/penetrate/handshake/real_ip.rs deleted file mode 100644 index 5646649..0000000 --- a/src/net/penetrate/handshake/real_ip.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::pin::Pin; - -use crate::{FusoStream, Provider, ext::AsyncWriteExt}; - -type BoxedFuture = Pin> + Send + 'static>>; - -pub struct ProxyProtocol; - - -impl Provider for ProxyProtocol { - type Output = BoxedFuture; - - fn call(&self, mut client: FusoStream) -> Self::Output { - Box::pin(async move { - // ..... - client.write_all(b"\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A\x02").await?; - Ok(client) - }) - } -} diff --git a/src/net/penetrate/mock/direct.rs b/src/net/penetrate/mock/direct.rs deleted file mode 100644 index 6403bbe..0000000 --- a/src/net/penetrate/mock/direct.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::{pin::Pin, sync::Arc}; - -use crate::{ - guard::Fallback, - penetrate::{ - server::{Peer, Visitor}, - Selector, - }, - NetSocket, Provider, Socket, Stream, -}; - -type BoxedFuture = Pin> + Send + 'static>>; - -pub struct DirectMock; - -impl Provider<(Fallback, Arc)> for DirectMock -where - S: Stream + Send + 'static, -{ - type Output = BoxedFuture>; - - fn call( - &self, - (stream, config): (Fallback, Arc), - ) -> Self::Output { - Box::pin(async move { - let mut socket = Socket::default(); - - if config.real_ip { - let addr = stream.peer_addr()?; - socket.set_origin(addr.first_addr()); - } - - Ok(Selector::Checked(Peer::Route( - Visitor::Route(stream), - Socket::default(), - ))) - }) - } -} diff --git a/src/net/penetrate/mock/mod.rs b/src/net/penetrate/mock/mod.rs deleted file mode 100644 index 492f41d..0000000 --- a/src/net/penetrate/mock/mod.rs +++ /dev/null @@ -1,36 +0,0 @@ -mod direct; - -mod socks; - -use std::{pin::Pin, sync::Arc}; - -use self::socks::PenetrateSocksBuilder; - -pub use socks::SocksUdpForwardMock; - -use super::{server::Peer, PenetrateSelectorBuilder}; -use crate::{guard::Fallback, Accepter, Executor, Provider, Socket, Stream, WrappedProvider}; - -type BoxedFuture = Pin> + Send + 'static>>; - -pub type Mock = WrappedProvider<(Fallback, Arc), Peer>>; - -impl PenetrateSelectorBuilder -where - E: Executor + 'static, - A: Accepter + Unpin + Send + 'static, - S: Stream + Send + Sync + 'static, - P: Provider> + Send + Sync + 'static, -{ - pub fn using_direct(mut self) -> Self { - self.adapters - .push(WrappedProvider::wrap(direct::DirectMock)); - self - } - - pub fn using_socks(self) -> PenetrateSocksBuilder { - PenetrateSocksBuilder { - adapter_builder: self, - } - } -} diff --git a/src/net/penetrate/mock/socks.rs b/src/net/penetrate/mock/socks.rs deleted file mode 100644 index da4a6e2..0000000 --- a/src/net/penetrate/mock/socks.rs +++ /dev/null @@ -1,330 +0,0 @@ -use std::{net::SocketAddr, pin::Pin, sync::Arc}; - -use crate::{ - ext::AsyncReadExt, - guard::Fallback, - io, - penetrate::{ - server::{Peer, Visitor}, - PenetrateSelectorBuilder, Selector, - }, - protocol::{make_packet, AsyncRecvPacket, AsyncSendPacket, Poto, ToBytes, TryToPoto}, - select::Select, - socks::{self, S5Authenticate, Socks}, - Addr, Kind, Provider, Socket, SocketKind, Stream, UdpReceiverExt, UdpSocket, WrappedProvider, -}; - -type BoxedFuture = Pin> + Send + 'static>>; - -macro_rules! get_auth { - ($config: expr) => {{ - match (&$config.socks5_password, &$config.socks5_username) { - (Some(pwd), Some(username)) => S5Authenticate::standard(username, pwd), - (Some(pwd), None) => S5Authenticate::standard(&$config.whoami, pwd), - _ => S5Authenticate::default(), - } - }}; -} - -pub struct PenetrateSocksBuilder { - pub(crate) adapter_builder: PenetrateSelectorBuilder, -} - -pub struct SimpleSocksMock; - -pub struct SocksMock { - pub(crate) udp_provider: Arc>, -} - -pub struct SocksUdpForwardMock(pub(crate) WrappedProvider); - -pub struct SocksUdpForward { - stream: std::sync::Mutex>, - udp_provider: Arc>, -} - -impl PenetrateSocksBuilder -where - S: Stream + Send + Sync + 'static, -{ - pub fn simple(mut self) -> PenetrateSelectorBuilder { - self.adapter_builder - .adapters - .insert(0, WrappedProvider::wrap(SimpleSocksMock)); - - self.adapter_builder - } - - pub fn using_udp_forward( - mut self, - udp_forward: UF, - ) -> PenetrateSelectorBuilder - where - UF: Provider<(), Output = BoxedFuture<(SocketAddr, U)>> + Send + Sync + 'static, - U: UdpSocket + Unpin + Send + Sync + 'static, - { - let udp_forward = WrappedProvider::wrap(udp_forward); - - self.adapter_builder.adapters.insert( - 0, - WrappedProvider::wrap(SocksMock { - udp_provider: Arc::new(WrappedProvider::wrap(udp_forward)), - }), - ); - self.adapter_builder - } -} - -impl Provider<(Fallback, Arc)> for SimpleSocksMock -where - S: Stream + Send + Sync + 'static, -{ - type Output = BoxedFuture>; - - fn call( - &self, - (stream, config): (Fallback, Arc), - ) -> Self::Output { - Box::pin(async move { - let mut stream = stream; - - if !config.enable_socks { - log::debug!("skip socks mock"); - return Ok(Selector::Unselected(stream)); - } - - let mut socks_auth = get_auth!(config); - - let socket = match stream.socks5_handshake(&mut socks_auth).await { - Err(e) if !e.is_socks_error() => return Err(e), - Err(_) => return Ok(Selector::Unselected(stream)), - Ok(socket) => socket, - }; - - stream.consume_back_data(); - - match socket.kind() { - SocketKind::Tcp => Ok(Selector::Checked(Peer::Route( - Visitor::Route(stream), - socket, - ))), - SocketKind::Udp => Ok({ - socks::finish_udp_forward(&mut stream).await?; - Selector::Checked(Peer::Finished(stream)) - }), - _ => unsafe { std::hint::unreachable_unchecked() }, - } - }) - } -} - -impl Provider<(Fallback, Arc)> for SocksMock -where - S: Stream + Send + Sync + 'static, - U: UdpSocket + Unpin + Send + Sync + 'static, -{ - type Output = BoxedFuture>; - - fn call( - &self, - (stream, config): (Fallback, Arc), - ) -> Self::Output { - let udp_provider = self.udp_provider.clone(); - Box::pin(async move { - let mut stream = stream; - - if !config.enable_socks { - log::debug!("skip socks mock"); - return Ok(Selector::Unselected(stream)); - } - - let mut socks_auth = get_auth!(config); - - let socket = match stream.socks5_handshake(&mut socks_auth).await { - Err(e) if !e.is_socks_error() => return Err(e), - Err(_) => return Ok(Selector::Unselected(stream)), - Ok(socket) => socket, - }; - - stream.consume_back_data(); - - match socket.kind() { - SocketKind::Tcp => Ok(Selector::Checked(Peer::Route( - Visitor::Route(stream), - socket, - ))), - SocketKind::Udp => Ok({ - if !config.enable_socks_udp { - log::debug!("skip udp forwarding"); - socks::finish_udp_forward(&mut stream).await?; - Selector::Checked(Peer::Finished(stream)) - } else { - let stream = stream.into_inner(); - let udp_forward = SocksUdpForward { - udp_provider, - stream: std::sync::Mutex::new(Some(stream)), - }; - Selector::Checked(Peer::Route( - Visitor::Provider(WrappedProvider::wrap(udp_forward)), - Socket::ufd(socket.into_addr()), - )) - } - }), - _ => unsafe { std::hint::unreachable_unchecked() }, - } - }) - } -} - -impl Provider> for SocksUdpForward -where - S: Stream + Send + 'static, - U: UdpSocket + Unpin + Send + Sync + 'static, -{ - type Output = BoxedFuture<()>; - - fn call(&self, s2: Fallback) -> Self::Output { - let s2 = s2.into_inner(); - let s1 = match self.stream.lock() { - Err(_) => return Box::pin(async move { Err(Kind::Once.into()) }), - Ok(mut lock) => match lock.take() { - None => return Box::pin(async move { Err(Kind::Once.into()) }), - Some(s) => s, - }, - }; - - let provider = self.udp_provider.clone(); - - let fut = async move { - let mut s1 = s1; - let peer_addr = s2.peer_addr()?; - let (mut reader, mut writer) = io::split(s2); - - let (addr, mut udp) = provider.call(()).await?; - - log::debug!("udp forwarding service listening on {}", addr); - - socks::send_udp_forward_message(&mut s1, addr).await?; - - let fut1 = { - let mut writer = writer.clone(); - async move { - let mut buf = [1]; - - loop { - let r = s1.read(&mut buf).await; - - if r.is_err() { - break; - } - - let n = unsafe { r.unwrap_unchecked() }; - - if n == 0 { - break; - } - } - - let close = Poto::Close.bytes(); - - writer.send_packet(&close).await - } - }; - - let fut2 = async move { - let mut buf = Vec::with_capacity(1500); - - unsafe { - buf.set_len(1500); - } - - loop { - let (n, addr) = udp.recv_from(&mut buf).await?; - let origin = socks::parse_and_forward_data(&mut writer, &buf[..n]).await?; - log::info!("connect from {} to {}", peer_addr, origin); - - let packet = reader.recv_packet().await?; - - socks::send_packed_udp_forward_message( - &mut udp, - &addr, - origin, - &packet.payload, - ) - .await?; - } - }; - - Select::select(fut1, fut2).await - }; - - Box::pin(async move { - match fut.await { - Ok(()) => { - log::debug!("forward packet success"); - Ok(()) - } - Err(e) => { - log::warn!("failed to forward {}", e); - Err(e) - } - } - }) - } -} - -impl Provider for SocksUdpForwardMock -where - S: Stream + Send + 'static, - U: UdpSocket + Send + Unpin + Sync + 'static, -{ - type Output = BoxedFuture<()>; - - fn call(&self, mut stream: S) -> Self::Output { - let provider = self.0.clone(); - Box::pin(async move { - let mut buf = Vec::with_capacity(1500); - - unsafe { - buf.set_len(1500); - } - - loop { - let message = stream.recv_packet().await?.try_poto()?; - - let addr = match message { - Poto::Forward(addr) => addr, - Poto::Close => { - log::debug!("close udp forward"); - break Ok(()); - } - message => { - log::warn!("wrong message {}", message); - break Ok(()); - } - }; - - let (_, udp) = provider.call(addr).await?; - - let data = stream.recv_packet().await?; - - let _ = udp.send(&data.payload).await?; - - log::info!( - "connect from {} to {} forward {}bytes", - stream.local_addr()?, - udp.peer_addr()?, - data.payload.len() - ); - - let n = udp.recv(&mut buf).await?; - - let packet = make_packet(buf[..n].to_vec()).encode(); - - stream.send_packet(&packet).await?; - - log::debug!("forward success {}bytes", n); - } - }) - } -} diff --git a/src/net/penetrate/mod.rs b/src/net/penetrate/mod.rs deleted file mode 100644 index afac05f..0000000 --- a/src/net/penetrate/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -mod accepter; -mod selector; -mod builder; -mod handshake; -mod observer; -mod bridge; - -pub use handshake::*; -pub use observer::*; - -mod mock; - -pub use mock::*; - -pub mod client; -pub mod server; - -pub use selector::*; -pub use builder::*; diff --git a/src/net/penetrate/observer.rs b/src/net/penetrate/observer.rs deleted file mode 100644 index 1842165..0000000 --- a/src/net/penetrate/observer.rs +++ /dev/null @@ -1,152 +0,0 @@ -use std::sync::Arc; - -use crate::{Address, Error}; - -use super::server; - -pub trait PenetrateObserver { - fn on_pen_start( - &self, - client: &Address, - visit: &Address, - server: &Address, - config: &server::Config, - ) where - Self: Sized, - { - log::debug!( - "on_pen_start client: {}, visit: {}, server: {}, config: {:#?}", - client, - visit, - server, - config - ); - } - - fn on_pen_stop( - &self, - client: &Address, - visit: &Address, - server: &Address, - _: &server::Config, - ) where - Self: Sized, - { - log::debug!( - "on_pen_stop client: {}, visit: {}, server: {}", - client, - visit, - server - ) - } - - fn on_pen_route(&self, client: &Address, from: &Address, to: &Address) - where - Self: Sized, - { - log::debug!( - "on_pen_route client: {}, from: {}, to: {}", - client, - from, - to - ); - } - - fn on_pen_error(&self, client: &Address, _: &server::Config, error: &Error) - where - Self: Sized, - { - log::debug!("on_pen_error {} {}", client, error); - } -} - -impl PenetrateObserver for () {} - -impl PenetrateObserver for Arc -where - T: PenetrateObserver, -{ - fn on_pen_error(&self, client: &Address, config: &server::Config, error: &Error) - where - Self: Sized, - { - (**self).on_pen_error(client, config, error) - } - - fn on_pen_route(&self, client: &Address, from: &Address, to: &Address) - where - Self: Sized, - { - (**self).on_pen_route(client, from, to) - } - - fn on_pen_start( - &self, - client: &Address, - visit: &Address, - server: &Address, - config: &server::Config, - ) where - Self: Sized, - { - (**self).on_pen_start(client, visit, server, config) - } - - fn on_pen_stop( - &self, - client: &Address, - visit: &Address, - server: &Address, - config: &server::Config, - ) where - Self: Sized, - { - (**self).on_pen_stop(client, visit, server, config) - } -} - -impl PenetrateObserver for Option -where - T: PenetrateObserver, -{ - fn on_pen_error(&self, client: &Address, config: &server::Config, error: &Error) - where - Self: Sized, - { - self.as_ref() - .map(|obs| obs.on_pen_error(client, config, error)); - } - - fn on_pen_route(&self, client: &Address, from: &Address, to: &Address) - where - Self: Sized, - { - self.as_ref().map(|obs| obs.on_pen_route(client, from, to)); - } - - fn on_pen_start( - &self, - client: &Address, - visit: &Address, - server: &Address, - config: &server::Config, - ) where - Self: Sized, - { - self.as_ref() - .map(|obs| obs.on_pen_start(client, visit, server, config)); - } - - fn on_pen_stop( - &self, - client: &Address, - visit: &Address, - server: &Address, - config: &server::Config, - ) where - Self: Sized, - { - self.as_ref() - .map(|obs| obs.on_pen_stop(client, visit, server, config)); - } -} diff --git a/src/net/penetrate/selector.rs b/src/net/penetrate/selector.rs deleted file mode 100644 index b98c40b..0000000 --- a/src/net/penetrate/selector.rs +++ /dev/null @@ -1,79 +0,0 @@ -use std::{pin::Pin, sync::Arc}; - -use crate::{ - guard::Fallback, server::Server, Accepter, Executor, Fuso, Provider, WrappedProvider, Socket, - Stream, -}; - -use super::{ - server::{Peer, PenetrateProvider}, - PenetrateServerBuilder, PenetrateObserver, -}; - -type BoxedFuture = Pin> + Send + 'static>>; - -pub enum Selector { - Checked(Peer>), - Unselected(Fallback), -} - -pub struct PenetrateSelector(Arc>); - -pub struct PenetrateSelectorBuilder { - pub(crate) adapters: Vec, Arc), Selector>>, - pub(crate) penetrate_builder: PenetrateServerBuilder, -} - -impl PenetrateServerBuilder { - pub fn using_adapter(self) -> PenetrateSelectorBuilder { - PenetrateSelectorBuilder { - adapters: Default::default(), - penetrate_builder: self, - } - } -} - -impl PenetrateSelectorBuilder -where - E: Executor + 'static, - A: Accepter + Unpin + Send + 'static, - S: Stream + Send + Sync + 'static, - P: Provider> + Send + Sync + 'static, - O: PenetrateObserver + Send + Sync + 'static -{ - pub fn build(self) -> Fuso, P, S, O>> { - self.penetrate_builder - .disable_fallback_strict_mode() - .build(PenetrateSelector(Arc::new(self.adapters))) - } -} - -impl Provider<(Fallback, Arc)> for PenetrateSelector -where - S: Stream + Send + Unpin + 'static, - M: Provider<(Fallback, Arc), Output = BoxedFuture>> + Sync + Send + 'static, -{ - type Output = BoxedFuture>>; - - fn call(&self, (fallback, config): (Fallback, Arc)) -> Self::Output { - let adapters = self.0.clone(); - Box::pin(async move { - let mut fallback = fallback; - - for adapter in adapters.iter() { - fallback.mark().await?; - - fallback = match adapter.call((fallback, config.clone())).await? { - Selector::Unselected(fallback) => fallback, - Selector::Checked(peer) => { - return Ok(peer); - } - }; - - fallback.backward().await?; - } - - Ok(Peer::Unknown(fallback)) - }) - } -} diff --git a/src/net/penetrate/server.rs b/src/net/penetrate/server.rs deleted file mode 100644 index e7aba07..0000000 --- a/src/net/penetrate/server.rs +++ /dev/null @@ -1,627 +0,0 @@ -use std::{collections::HashMap, fmt::Display, pin::Pin, sync::Arc, task::Poll, time::Duration}; - -use serde::Serialize; - -use crate::penetrate::accepter::PenetrateAccepter; -use crate::penetrate::client; -use crate::protocol::IntoPacket; -use crate::server::Environ; -use crate::sync::Mutex; -use std::future::Future; - -use crate::io::{ReadHalf, WriteHalf}; - -use crate::{ - ext::AsyncWriteExt, - generator::Generator, - guard::Fallback, - io, - protocol::{AsyncRecvPacket, AsyncSendPacket, Bind, Poto, ToBytes, TryToPoto}, - ready, Accepter, Provider, Socket, Stream, WrappedProvider, -}; - -use super::accepter::Pen; -use super::mock::Mock; -use super::PenetrateObserver; -use crate::{join, time, Address, Error, Kind, NetSocket, Platform, Processor}; - -type BoxedFuture = Pin> + Send + 'static>>; - -macro_rules! throw_client_error { - ($result: expr) => { - match $result { - Ok(t) => t, - Err(e) => { - log::warn!("client error {}", e); - return Ok(State::Error(e)); - } - } - }; -} - -macro_rules! read_client_config { - ($client: expr) => {{ - let config = $client.recv_packet().await; - - if let Err(e) = config.as_ref() { - log::warn!("client error {}", e); - return Err(unsafe { config.unwrap_err_unchecked() }); - } - - let config = unsafe { config.unwrap_unchecked() }; - - let config = bincode::deserialize::(&config.payload); - - if let Err(e) = config.as_ref() { - log::warn!("configuration error {}", e); - let err_msg = e.to_string().into_packet(); - let err_msg = err_msg.encode(); - if let Err(e) = $client.send_packet(&err_msg).await { - log::warn!("client error {}", e); - } - return Err(e.to_string().into()); - } else { - log::debug!("recv client config "); - let ok_msg = "YES".into_packet(); - let ok_msg = ok_msg.encode(); - if let Err(e) = $client.send_packet(&ok_msg).await { - log::warn!("client error {}", e); - return Err(e); - } - } - - unsafe { config.unwrap_unchecked() } - }}; -} - -pub enum Outcome { - Route(T, T), - Future(BoxedFuture<()>), -} - -pub enum State { - Stop, - Close(T), - Finish, - Route(T, T), - Provider(BoxedFuture<()>), - Error(crate::Error), -} - -pub enum Visitor { - Route(T), - Provider(WrappedProvider), -} - -pub struct PenetrateGenerator(Penetrate, O>); - -pub enum Peer { - Route(Visitor, Socket), - Finished(T), - Unknown(T), -} - -#[derive(Default, Clone)] -pub struct MQueue { - identify: Arc>, - wait_list: Arc>>, -} - -#[derive(Debug, Clone, Serialize)] -pub struct Config { - pub(super) whoami: String, - pub(super) is_mixed: bool, - pub(super) maximum_wait: Duration, - pub(super) heartbeat_delay: Duration, - pub(super) read_timeout: Option, - pub(super) write_timeout: Option, - pub(super) fallback_strict_mode: bool, - pub(super) enable_socks: bool, - pub(super) enable_socks_udp: bool, - pub(super) socks5_password: Option, - pub(super) socks5_username: Option, - pub(super) platform: Platform, - pub(super) real_ip: bool, -} - -pub struct PenetrateProvider { - pub(crate) mock: Arc>, - pub(crate) config: Config, -} - -#[derive(Debug, Clone, Serialize)] -pub struct PenetrateEnviron { - conn: Address, - client: Address, - config: Config, - visitor: Address, -} - -pub struct Penetrate { - mock: Arc>, - config: Arc, - accepter: A, - address: Address, - writer: WriteHalf, - processor: Processor, - futures: Vec>>, - mqueue: MQueue>, - client_addr: Address, -} - -impl MQueue { - pub async fn push(&self, item: T) -> u32 { - // FIXME cur === next may lead to an infinite loop - let mut ident = self.identify.lock().await; - let mut wait_list = self.wait_list.lock().await; - while wait_list.contains_key(&ident) { - let (next, overflowing) = ident.overflowing_add(1); - - if overflowing { - *ident = 0; - } else { - *ident = next; - } - } - - wait_list.insert(*ident, item); - - *ident - } - - pub async fn remove(&self, id: u32) -> Option { - self.wait_list.lock().await.remove(&id) - } -} - -impl Config { - fn update(&mut self, config: client::Config) { - self.whoami = config.name; - self.enable_socks = config.enable_socks5 || config.enable_socks5_udp; - self.enable_socks_udp = config.enable_socks5_udp; - self.socks5_username = config.socks_username; - self.socks5_password = config.socks_password; - self.heartbeat_delay = config.heartbeat_delay; - self.maximum_wait = config.maximum_wait; - self.is_mixed = config.enable_kcp; - self.platform = config.platform; - } -} - -impl Penetrate -where - T: Stream + Sync + Send + 'static, - A: Accepter> + Unpin + Send + 'static, - O: PenetrateObserver + Sync + Send + 'static, - P: Sync + Send + 'static, -{ - pub fn new( - config: Config, - converter: Arc>, - processor: Processor, - address: Address, - client: T, - accepter: A, - ) -> Self { - let client_addr = unsafe { client.peer_addr().unwrap_unchecked() }; - let (reader, writer) = crate::io::split(client); - - let mqueue = MQueue { - identify: Default::default(), - wait_list: Default::default(), - }; - - let recv_fut = Self::poll_handle_recv(mqueue.clone(), reader.clone()); - let write_fut = Self::poll_heartbeat_future(writer.clone(), config.heartbeat_delay); - - Self { - writer, - config: Arc::new(config), - mock: converter, - accepter, - mqueue, - client_addr, - processor, - address, - futures: vec![Box::pin(recv_fut), Box::pin(write_fut)], - } - } - - async fn poll_handle_recv( - mqueue: MQueue>, - mut stream: ReadHalf, - ) -> crate::Result> { - loop { - let packet = stream.recv_packet().await; - - if packet.is_err() { - let err = unsafe { packet.unwrap_err_unchecked() }; - log::warn!("client error {}", err); - return Ok(State::Error(err)); - } - - let packet = unsafe { packet.unwrap_unchecked() }.try_poto(); - - if packet.is_err() { - log::warn!("The client sent an invalid packet"); - return Ok(State::Error(unsafe { packet.unwrap_err_unchecked() })); - } - - let message = unsafe { packet.unwrap_unchecked() }; - - match message { - Poto::Ping => { - log::trace!("client ping received"); - } - Poto::MapError(id, err) => { - log::warn!("client mapping failed, msg = {}", err); - mqueue.remove(id).await.map(|r| r.close()); - } - message => { - log::warn!("ignore client message {:?}", message); - } - } - } - } - - async fn poll_heartbeat_future( - mut stream: WriteHalf, - timeout: Duration, - ) -> crate::Result> { - let ping = Poto::Ping.bytes(); - - loop { - log::trace!("send heartbeat packet to client"); - - if let Err(e) = stream.send_packet(&ping).await { - log::warn!("failed to send heartbeat packet to client"); - break Ok(State::Error(e)); - } - - time::sleep(timeout).await; - } - } - - fn async_penetrate_handle(self: &mut Pin<&mut Self>, pen: Pen) -> BoxedFuture> { - let mut writer = self.writer.clone(); - let mock = self.mock.clone(); - let timeout = self.config.maximum_wait; - let mqueue = self.mqueue.clone(); - let fallback_strict_mode = self.config.fallback_strict_mode; - let processor = self.processor.clone(); - let config = self.config.clone(); - - let fut = async move { - match pen { - Pen::Visit(visitor) => { - let mut fallback = Fallback::new(visitor, fallback_strict_mode); - let visit_addr = fallback.peer_addr()?; - let _ = fallback.mark().await?; - let peer = mock.call((fallback, config)).await?; - let (accept_tx, accept_ax) = async_channel::bounded(1); - let id = mqueue.push(accept_tx).await; - - let (visitor, dst) = match peer { - Peer::Finished(visitor) => return Ok(State::Close(visitor.into_inner())), - Peer::Unknown(visitor) => return Ok(State::Close(visitor.into_inner())), - Peer::Route(visitor, dst) => (visitor, dst), - }; - - let route = Poto::Map(id, dst).bytes(); - - throw_client_error!(writer.send_packet(&route).await); - - log::trace!("client notified, waiting for mapping"); - - match visitor { - Visitor::Route(src) => { - let mut src = src; - let mut dst = accept_ax.recv().await?; - - src.backward().await?; - - if let Some(data) = src.back_data() { - log::debug!("copy data to peer {}bytes", data.len()); - - if let Err(e) = dst.write_all(&data).await { - log::warn!( - "mapping failed, the client has closed the connection" - ); - return Err(e.into()); - } - } - - processor.observer().on_pen_route( - &writer.peer_addr()?, - &visit_addr, - &dst.peer_addr()?, - ); - - Ok::<_, crate::Error>(State::Route(src.into_inner(), dst)) - } - Visitor::Provider(provider) => { - let fallback = - Fallback::new(accept_ax.recv().await?, fallback_strict_mode); - - processor.observer().on_pen_route( - &writer.peer_addr()?, - &visit_addr, - &fallback.peer_addr()?, - ); - - let dst = provider.call(fallback); - - Ok(State::Provider(dst)) - } - } - } - Pen::Client(client) => { - let mut client = processor.decorate(client).await?; - - let poto = client.recv_packet().await?.try_poto()?; - - match poto { - Poto::Map(id, _) => { - if let Some(tx) = mqueue.remove(id).await { - if let Err(_) = tx.send(client).await { - log::warn!("the client established a mapping request, but the peer was closed"); - } - } - } - poto => { - log::warn!("bad message {}", poto) - } - } - - Ok(State::Finish) - } - } - }; - - let wait_fut = crate::time::wait_for(timeout, fut); - - Box::pin(async move { - match wait_fut.await { - Ok(ready) => ready, - Err(e) => Err(e.into()), - } - }) - } -} - -impl NetSocket for Penetrate -where - T: Stream, - A: Accepter>, -{ - fn peer_addr(&self) -> crate::Result

{ - self.accepter.peer_addr() - } - - fn local_addr(&self) -> crate::Result
{ - self.accepter.local_addr() - } -} - -impl Accepter for Penetrate -where - T: Stream + Send + Sync + 'static, - A: Accepter> + Unpin + Send + 'static, - O: PenetrateObserver + Sync + Send + 'static, - P: Send + Sync + 'static, -{ - type Stream = Outcome; - - fn poll_accept( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - let mut futures = std::mem::replace(&mut self.futures, Default::default()); - - // 至少进行一次轮询, - // accept如果就绪后没有再次poll,将会导致对端连接卡顿,甚至最坏的情况下可能无法建立连接 - let mut poll_accepter = true; - - while poll_accepter { - poll_accepter = match Pin::new(&mut self.accepter).poll_accept(cx)? { - Poll::Pending => false, - Poll::Ready(pen) => { - futures.push(self.async_penetrate_handle(pen)); - true - } - }; - - while let Some(mut future) = futures.pop() { - match Pin::new(&mut future).poll(cx) { - Poll::Pending => { - self.futures.push(future); - } - Poll::Ready(Ok(State::Route(s1, s2))) => { - self.futures.extend(futures); - - return Poll::Ready(Ok::<_, crate::Error>(Outcome::Route(s1, s2))); - } - Poll::Ready(Ok(State::Provider(fut))) => { - self.futures.extend(futures); - return Poll::Ready(Ok::<_, crate::Error>(Outcome::Future(fut))); - } - Poll::Ready(Ok(State::Stop)) => { - log::warn!("client aborted {}", self.client_addr); - return Poll::Ready(Err(crate::error::Kind::Channel.into())); - } - Poll::Ready(Ok(State::Error(e))) => { - log::warn!("client error {}, err: {}", self.client_addr, e); - let cfg = (*self.config).clone(); - self.processor - .observer() - .on_pen_error(&self.address, &cfg, &e); - - return Poll::Ready(Err(e)); - } - Poll::Ready(Ok(State::Close(mut s))) => { - futures.push(Box::pin(async move { - let _ = s.close().await; - Ok(State::Finish) - })); - } - _ => {} - } - } - } - - log::debug!("{} futures remaining", self.futures.len()); - - Poll::Pending - } -} - -impl Provider<(S, Processor)> for PenetrateProvider -where - A: Accepter + Send + Unpin + 'static, - S: Stream + Sync + Send + 'static, - P: Provider> + Send + Sync + 'static, - O: PenetrateObserver + Send + Sync + 'static, -{ - type Output = BoxedFuture<(PenetrateGenerator, Environ)>; - - fn call(&self, (mut client, processor): (S, Processor)) -> Self::Output { - let peer_provider = self.mock.clone(); - let mut config = self.config.clone(); - Box::pin(async move { - let poto = client.recv_packet().await?.try_poto()?; - let penetrate = match poto { - Poto::Bind(Bind::Setup(client_addr, visit_addr)) => { - log::debug!("try to bind the server to {}", visit_addr); - let visit_fut = processor.bind(visit_addr); - let client_fut = processor.bind(client_addr); - join::join_output(client_fut, visit_fut).await - } - message => { - log::debug!("received an invalid message {}", message); - - let err: Error = Kind::Unexpected(format!("{}", message)).into(); - - processor - .observer() - .on_pen_error(&client.peer_addr()?, &config, &err); - - return Err(err); - } - }; - - match penetrate { - Err(e) => { - let message = Poto::Bind(Bind::Failed(e.to_string())).bytes(); - - log::warn!("failed to create listener err={}", e); - - if let Err(e) = client.send_packet(&message).await { - log::warn!("failed to send failure message to client err={}", e); - processor - .observer() - .on_pen_error(&client.peer_addr()?, &config, &e); - } - - Err(e) - } - Ok((aclient, avisit)) => { - let visit_addr = avisit.local_addr()?; - let client_addr = aclient.local_addr()?; - - let poto = Poto::Bind(Bind::Success(client_addr, visit_addr)); - - client.send_packet(&poto.bytes()).await.map_err(|e| { - log::warn!("failed to send message to client err={}", e); - e - })?; - - let client_config = read_client_config!(client); - - config.update(client_config); - - processor.observer().on_pen_start( - &client.peer_addr()?, - &avisit.local_addr()?, - &aclient.local_addr()?, - &config, - ); - - log::info!( - "client is {} and the server is {}", - client.peer_addr()?, - aclient.local_addr()? - ); - - log::info!("please visit {} for port mapping", avisit.local_addr()?); - - let environ: Environ = Arc::new(PenetrateEnviron { - conn: client.peer_addr()?, - config: config.clone(), - client: aclient.local_addr()?, - visitor: avisit.local_addr()?, - }); - - let generator = PenetrateGenerator(Penetrate::new( - config, - peer_provider, - processor, - client.peer_addr()?, - client, - PenetrateAccepter::new(avisit, aclient), - )); - - Ok((generator, environ)) - } - } - }) - } -} - -impl Generator for PenetrateGenerator -where - A: Accepter + Send + Unpin + 'static, - T: Stream + Send + Sync + 'static, - O: PenetrateObserver + Sync + Send + 'static, - P: Send + Sync + 'static, -{ - type Output = Option>; - - fn poll_generate( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context, - ) -> Poll> { - match ready!(Pin::new(&mut self.0).poll_accept(cx)?) { - Outcome::Future(fut) => { - log::debug!("start a future"); - Poll::Ready(Ok(Some(fut))) - } - Outcome::Route(s1, s2) => Poll::Ready(Ok(Some(Box::pin(async move { - log::debug!("start forwarding"); - if let Err(e) = io::forward(s1, s2).await { - log::trace!("forward error {}", e); - }; - Ok(()) - })))), - } - } -} - -impl crate::Environ for PenetrateEnviron { - fn conn(&self) -> Address { - self.conn.clone() - } - - fn info(&self) -> crate::Result { - Ok(serde_json::to_value(self).unwrap()) - } -} - -impl Display for Config { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "read_timeout = {:?}, write_timeout = {:?}, max_wait_time={:?}, heartbeat_time={:?}", - self.read_timeout, self.write_timeout, self.maximum_wait, self.heartbeat_delay - ) - } -} diff --git a/src/net/proxy/mod.rs b/src/net/proxy/mod.rs deleted file mode 100644 index 8b13789..0000000 --- a/src/net/proxy/mod.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/net/quic/mod.rs b/src/net/quic/mod.rs deleted file mode 100644 index 8b13789..0000000 --- a/src/net/quic/mod.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/net/socks/auth.rs b/src/net/socks/auth.rs deleted file mode 100644 index 8ae9302..0000000 --- a/src/net/socks/auth.rs +++ /dev/null @@ -1,232 +0,0 @@ -use std::{pin::Pin, task::Poll}; - -use crate::{ready, ReadBuf, SocksErr, Stream}; - -use super::Socks5Auth; - -#[derive(Clone)] -pub enum S5Authenticate { - Skip { - reply: [u8; 2], - offset: usize, - }, - Standard { - rpos: usize, - rbuf: Option>, - wpos: usize, - wbuf: Option<[u8; 2]>, - init: bool, - good: bool, - cmp_user: Option>, - cmp_pass: Option>, - username: Vec, - password: Vec, - }, -} - -impl S5Authenticate { - pub fn skip() -> Self { - Self::Skip { - reply: [0x05, 0x00], - offset: 0, - } - } - - pub fn standard, P: AsRef<[u8]>>(username: U, password: P) -> Self { - Self::Standard { - rpos: 0, - wpos: 0, - rbuf: None, - wbuf: Some([0x05, 0x02]), - good: false, - init: false, - cmp_user: None, - cmp_pass: None, - username: username.as_ref().to_vec(), - password: password.as_ref().to_vec(), - } - } -} - -impl Default for S5Authenticate { - fn default() -> Self { - Self::skip() - } -} - -// The server selects from one of the methods given in METHODS, and -// sends a METHOD selection message: - -// +----+--------+ -// |VER | METHOD | -// +----+--------+ -// | 1 | 1 | -// +----+--------+ - -// If the selected METHOD is X'FF', none of the methods listed by the -// client are acceptable, and the client MUST close the connection. - -// The values currently defined for METHOD are: - -// o X'00' NO AUTHENTICATION REQUIRED -// o X'01' GSSAPI -// o X'02' USERNAME/PASSWORD -// o X'03' to X'7F' IANA ASSIGNED -// o X'80' to X'FE' RESERVED FOR PRIVATE METHODS -// o X'FF' NO ACCEPTABLE METHODS -impl Socks5Auth for S5Authenticate -where - S: Stream + Unpin, -{ - fn poll_auth( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context, - mut stream: std::pin::Pin<&mut S>, - _: &[u8], - ) -> std::task::Poll> { - match &mut *self { - S5Authenticate::Skip { reply, offset } => loop { - let n = ready!(Pin::new(&mut *stream).poll_write(cx, &reply[*offset..]))?; - - if n == 0 { - return Poll::Ready(Err(std::io::Error::from( - std::io::ErrorKind::UnexpectedEof, - ) - .into())); - } - - *offset += n; - if *offset == reply.len() { - break Poll::Ready(Ok(())); - } - }, - S5Authenticate::Standard { - rpos, - rbuf, - wpos, - wbuf, - init, - good, - cmp_user, - cmp_pass, - username, - password, - } => { - let mut do_next = true; - while do_next { - do_next = false; - while let Some(buf) = wbuf.as_ref() { - let n = ready!(Pin::new(&mut *stream).poll_write(cx, &buf[*wpos..]))?; - - if n == 0 { - return Poll::Ready(Err(std::io::Error::from( - std::io::ErrorKind::UnexpectedEof, - ) - .into())); - } - - *wpos += n; - - if *wpos == buf.len() && cmp_pass.is_none() && cmp_user.is_none() { - let mut buf = Vec::new(); - buf.resize(2, 0); - drop(std::mem::replace(rbuf, Some(buf))); - drop(std::mem::replace(wbuf, None)); - } else if *wpos == buf.len() - && cmp_pass.is_some() - && cmp_user.is_some() - && !*good - { - log::debug!("socks5 authentication fail"); - return Poll::Ready(Err(SocksErr::Authenticate.into())); - } else if *wpos == buf.len() - && cmp_pass.is_some() - && cmp_user.is_some() - && *good - { - log::debug!("socks5 authentication successful"); - return Poll::Ready(Ok(())); - } - } - - while let Some(buf) = rbuf.as_mut() { - let need = buf.len(); - let n = ready!(Pin::new(&mut *stream) - .poll_read(cx, &mut ReadBuf::new(&mut buf[*rpos..])))?; - - log::trace!("read {}, total: {}", n, buf.len()); - - if n == 0 { - return Poll::Ready(Err(std::io::Error::from( - std::io::ErrorKind::UnexpectedEof, - ) - .into())); - } - - *rpos += n; - - if *rpos == need && cmp_user.is_none() && !*init { - // username + password len - let ul = buf[1] as usize + 1; - - if buf[0] != 0x01 { - log::warn!("ver({}) != 0x01", buf[0]); - return Poll::Ready(Err(SocksErr::Authenticate.into())); - } - - let mut nbuf = Vec::with_capacity(ul); - - log::trace!("username len {}", ul); - - unsafe { - nbuf.set_len(ul); - } - - drop(std::mem::replace(rbuf, Some(nbuf))); - *rpos = 0; - *init = true; - } else if *rpos == need && cmp_user.is_none() && *init { - let pl = buf[buf.len() - 1] as usize; - let mut nbuf = Vec::with_capacity(pl); - - log::trace!("password len {}", pl); - - unsafe { - nbuf.set_len(pl); - } - - *rpos = 0; - - drop(std::mem::replace( - cmp_user, - std::mem::replace(rbuf, Some(nbuf)), - )) - } else if *rpos == need && cmp_user.is_some() { - drop(std::mem::replace(cmp_pass, std::mem::replace(rbuf, None))); - break; - } - } - - match (&cmp_pass, &cmp_user) { - (Some(pass), Some(user)) => { - if password.eq(&pass) && user[..user.len() - 1].eq(username) { - *wpos = 0; - *good = true; - do_next = true; - drop(std::mem::replace(wbuf, Some([0x01, 0x00]))); - } else { - *wpos = 0; - do_next = true; - drop(std::mem::replace(wbuf, Some([0x01, 0x01]))); - } - } - _ => {} - } - } - - // should never get this far !!! - unsafe { std::hint::unreachable_unchecked() } - } - } - } -} diff --git a/src/net/socks/mod.rs b/src/net/socks/mod.rs deleted file mode 100644 index ee128d7..0000000 --- a/src/net/socks/mod.rs +++ /dev/null @@ -1,456 +0,0 @@ -mod auth; -pub use auth::*; -use std::net::SocketAddr; -use std::{pin::Pin, task::Poll}; - -use std::future::Future; - -use crate::ext::AsyncWriteExt; -use crate::protocol::{make_packet, Poto, ToBytes}; -use crate::{ - ready, Addr, AsyncRead, AsyncWrite, Kind, NetSocket, ReadBuf, Socket, SocksErr, Stream, - UdpReceiverExt, UdpSocket, -}; -use std::task::Context; - -macro_rules! reset_connect { - () => { - Into::::into(std::io::ErrorKind::ConnectionReset) - }; -} - -macro_rules! expect { - ($ver: expr) => { - if $ver != 0x05 { - return Poll::Ready(Err(SocksErr::Protocol.into())); - } - }; -} - -macro_rules! atype_len { - ($atype: expr) => { - match $atype { - 0x01 => 6, // ipv4 + port, - 0x03 => 1, // domain - 0x04 => 18, // ipv6, - _ => return Poll::Ready(Err(SocksErr::InvalidAddress.into())), - } - }; -} - -enum State { - Handshake, - // nmethod - Authentication(usize), - // ver, cmd, rsv, atype, size - Request(u8, u8, u8, u8, usize), - Success(Option), -} - -#[repr(C)] -#[derive(Clone, Copy)] -struct Head { - ver: u8, - nmethod: u8, -} - -#[pin_project::pin_project] -pub struct Socks5<'a, S, A> { - state: State, - read_buf: Vec, - write_buf: Vec, - read_offset: usize, - write_offset: usize, - auth: &'a mut A, - #[pin] - stream: &'a mut S, -} - -pub trait Socks: Stream { - fn socks5_handshake<'a, A>(&'a mut self, auth: &'a mut A) -> Socks5<'a, Self, A> - where - Self: Sized + Unpin, - A: Socks5Auth + Unpin, - { - Socks5 { - auth, - state: State::Handshake, - read_buf: Default::default(), - write_buf: Default::default(), - read_offset: 0, - write_offset: 0, - stream: self, - } - } -} - -pub trait Socks5Auth { - fn poll_auth( - self: Pin<&mut Self>, - cx: &mut Context, - stream: Pin<&mut S>, - methods: &[u8], - ) -> Poll>; -} - -impl<'a> TryFrom<&'a [u8]> for Head { - type Error = crate::Error; - - fn try_from(value: &'a [u8]) -> Result { - if value.len() < 2 { - return Err({ - SocksErr::BadLength { - expect: 2, - current: value.len(), - } - .into() - }); - } - - let head = unsafe { (value.as_ptr() as *mut Self).as_ref().unwrap_unchecked() }; - - if head.ver != 0x05 { - return Err(SocksErr::Head { - ver: head.ver, - nmethod: head.nmethod, - } - .into()); - } - - return Ok(head.clone()); - } -} - -impl Socks for T where T: NetSocket + AsyncRead + AsyncWrite + Unpin {} - -fn parse_address(cmd: u8, _: u8, atype: u8, data: &[u8]) -> crate::Result { - if cmd == 0x02 { - return Err(SocksErr::BindNotSupport.into()); - } - - let addr = match atype { - 0x01 => { - #[repr(C)] - struct V4 { - ip: [u8; 4], - port: [u8; 2], - } - - let v4 = unsafe { (data.as_ptr() as *mut V4).as_ref().unwrap_unchecked() }; - - Addr::from((v4.ip, u16::from_be_bytes(v4.port))) - } - 0x04 => { - #[repr(C)] - struct V6 { - ip: [u8; 16], - port: [u8; 2], - } - - let v6 = unsafe { (data.as_ptr() as *mut V6).as_ref().unwrap_unchecked() }; - - Addr::from((v6.ip, u16::from_be_bytes(v6.port))) - } - 0x03 => { - let len = data.len(); - let port = u16::from_be_bytes([data[len - 2], data[len - 1]]); - let domain = String::from_utf8_lossy(&data[..len - 2]); - Addr::from((format!("{}", domain), port)) - } - _ => return Err(SocksErr::InvalidAddress.into()), - }; - - Ok({ - match cmd { - 0x01 => Socket::tcp(addr), - 0x03 => Socket::udp(addr), - _ => return Err(SocksErr::BindNotSupport.into()), - } - }) -} - -impl<'a, S, A> Future for Socks5<'a, S, A> -where - S: Stream + Unpin, - A: Socks5Auth + Unpin, -{ - type Output = crate::Result; - - fn poll( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - let this = self.project(); - let mut stream = this.stream; - let auth = this.auth; - let read_buf = this.read_buf; - let write_buf = this.write_buf; - let state = this.state; - let read_offset = this.read_offset; - let write_offset = this.write_offset; - - loop { - match state { - // +----+----------+----------+ - // |VER | NMETHODS | METHODS | - // +----+----------+----------+ - // | 1 | 1 | 1 to 255 | - // +----+----------+----------+ - State::Handshake if read_buf.is_empty() => { - read_buf.resize(2, 0); - } - - State::Handshake if *read_offset == 2 => { - let head = Head::try_from(&read_buf[..*read_offset])?; - - log::trace!("ver={}, nmethod={}", head.ver, head.nmethod); - - *read_offset = 0; - read_buf.clear(); - read_buf.resize(head.nmethod as usize, 0); - drop(std::mem::replace( - state, - State::Authentication(head.nmethod as usize), - )); - } - // +----+--------+ - // |VER | METHOD | - // +----+--------+ - // | 1 | 1 | - // +----+--------+ - // o X'00' NO AUTHENTICATION REQUIRED - // o X'01' GSSAPI - // o X'02' USERNAME/PASSWORD - // o X'03' to X'7F' IANA ASSIGNED - // o X'80' to X'FE' RESERVED FOR PRIVATE METHODS - // o X'FF' NO ACCEPTABLE METHODS - State::Authentication(methods) if *methods == *read_offset => { - ready!(Pin::new(&mut **auth).poll_auth( - cx, - Pin::new(&mut **stream), - &read_buf[..*methods] - ))?; - *read_offset = 0; - read_buf.clear(); - read_buf.resize(4, 0); - drop(std::mem::replace(state, State::Request(0x05, 0, 0, 0, 4))); - } - State::Request(0x05, 0, 0, 0, 4) if 4 == *read_offset => { - expect!(read_buf[0]); - - let cmd = read_buf[1]; - let rsv = read_buf[2]; - let atype = read_buf[3]; - let len = atype_len!(atype); - - log::trace!("ver=0x05, cmd={}, rsv={}, atype={}", cmd, rsv, atype); - - *read_offset = 0; - read_buf.clear(); - read_buf.resize(len, 0); - - drop(std::mem::replace( - state, - State::Request(0x05, cmd, rsv, atype, len), - )); - } - State::Request(0x05, cmd, rsv, 0x03, 1) if 1 == *read_offset => { - *read_offset = 0; - let len = (read_buf[0] + 2) as usize; - read_buf.clear(); - read_buf.resize(len, 0); - let new_state = State::Request(0x05, *cmd, *rsv, 0x03, len); - drop(std::mem::replace(state, new_state)) - } - State::Request(0x05, cmd, rsv, atype, n) if n == read_offset => { - let socket = parse_address(*cmd, *rsv, *atype, &read_buf)?; - - log::debug!("target = {}", socket); - - *read_offset = 0; - read_buf.clear(); - - if socket.is_udp() { - write_buf.clear(); - } else { - *write_offset = 0; - write_buf.clear(); - write_buf - .extend([0x05, 0x00, *rsv, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); - } - - let new_state = State::Success(Some(socket)); - drop(std::mem::replace(state, new_state)) - } - State::Success(socket) if write_buf.is_empty() => { - break Poll::Ready(Ok(unsafe { socket.take().unwrap_unchecked() })); - } - _ => {} - } - - if !write_buf.is_empty() { - loop { - let n = ready!( - Pin::new(&mut **stream).poll_write(cx, &write_buf[*write_offset..]) - )?; - - if n == 0 { - return Poll::Ready(Err(reset_connect!().into())); - } - - if n == write_buf.len() { - *write_offset = 0; - write_buf.clear(); - break; - } - } - } - - if !read_buf.is_empty() { - loop { - let mut buf = ReadBuf::new(&mut read_buf[*read_offset..]); - let n = ready!(Pin::new(&mut **stream).poll_read(cx, &mut buf))?; - if n == 0 { - return Poll::Ready(Err(reset_connect!().into())); - } - - *read_offset += n; - - if *read_offset == buf.len() { - break; - } - } - } - } - } -} - -pub async fn finish_udp_forward(stream: &mut S) -> crate::Result<()> -where - S: Stream + Send + Unpin, -{ - stream - .write_all(&[0x05, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) - .await -} - -pub async fn send_udp_forward_message(stream: &mut S, addr: SocketAddr) -> crate::Result<()> -where - S: Stream + Send + Unpin, -{ - let mut buf = Vec::new(); - - buf.extend(&[0x05, 0x00, 0x00]); - - match addr { - SocketAddr::V4(v1) => { - buf.extend(&[0x01]); - buf.extend(&v1.ip().octets()); - buf.extend(&v1.port().to_be_bytes()); - } - SocketAddr::V6(v2) => { - buf.extend(&[0x04]); - buf.extend(&v2.ip().octets()); - buf.extend(&v2.port().to_be_bytes()); - } - } - - stream.write_all(&buf).await -} - -// +----+------+------+----------+----------+----------+ -// |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | -// +----+------+------+----------+----------+----------+ -// | 2 | 1 | 1 | Variable | 2 | Variable | -// +----+------+------+----------+----------+----------+ -pub async fn parse_and_forward_data(s1: &mut S, data: &[u8]) -> crate::Result -where - S: AsyncWrite + Unpin, -{ - if data.len() < 6 { - return Err(Kind::BadForward.into()); - } - - let rsv = u16::from_be_bytes([data[0], data[1]]); - let frag = data[2]; - let atype = data[3]; - - log::trace!( - "rsv={}, frag={}, atype={} data={}bytes", - rsv, - frag, - atype, - data.len() - ); - - if frag != 0 { - log::warn!("frag is not supported !"); - return Err(SocksErr::Socks5Frg.into()); - } - - let (size, data) = match atype { - 0x03 => (data[4] as usize, &data[5..]), - 0x01 => (6, &data[4..]), - 0x04 => (18, &data[4..]), - _ => return Err(SocksErr::InvalidAddress.into()), - }; - - let addr = parse_address(0x03, 0, atype, data)?.into_addr(); - - let message = Poto::Forward(addr.clone()).bytes(); - - s1.write_all(&message).await?; - - let data = make_packet(data[size..].to_vec()).encode(); - - s1.write_all(&data).await?; - - log::trace!("send forward data success"); - - Ok(addr) -} - -pub async fn send_packed_udp_forward_message( - udp: &mut U, - to: &SocketAddr, - origin: Addr, - data: &[u8], -) -> crate::Result<()> -where - U: UdpSocket + Unpin, -{ - let mut buf = Vec::new(); - buf.extend(&[0x00, 0x00, 0x00]); - - match origin.into_inner() { - crate::InnerAddr::Socket(origin) => match origin { - SocketAddr::V4(v1) => { - buf.push(0x01); - buf.extend(&v1.ip().octets()); - buf.extend(&v1.port().to_be_bytes()); - } - SocketAddr::V6(v2) => { - buf.push(0x04); - buf.extend(&v2.ip().octets()); - buf.extend(&v2.port().to_be_bytes()); - } - }, - crate::InnerAddr::Domain(domain, port) => { - let domain = domain.as_bytes(); - buf.push(0x03); - buf.push(domain.len() as u8); - buf.extend(domain); - buf.extend(&port.to_be_bytes()); - } - } - - buf.extend(data); - - match udp.send_to(to, &buf).await { - Ok(_) => Ok(()), - Err(e) => { - log::warn!("udp forward fail {} {}", to, e); - Err(e) - } - } -} diff --git a/src/net/tun/mod.rs b/src/net/tun/mod.rs deleted file mode 100644 index 8b13789..0000000 --- a/src/net/tun/mod.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs deleted file mode 100644 index f66bf52..0000000 --- a/src/net/udp/mod.rs +++ /dev/null @@ -1,227 +0,0 @@ -use std::{ - collections::{hash_map::DefaultHasher, HashMap}, - hash::{Hash, Hasher}, - net::SocketAddr, - pin::Pin, - sync::Arc, - task::{Context, Poll, Waker}, -}; - -use async_mutex::Mutex; - -use crate::{ - guard::buffer::Buffer, Address, Executor, NetSocket, ReadBuf, Socket, Task, UdpReceiverExt, - UdpSocket, -}; - -type UCore = Arc>; -type USessions = Arc>>; -type CloseCallback = Option>; - -#[derive(Debug, Default)] -struct Session { - ubuf: Buffer, - uwaker: Option, -} - -pub struct Datagram { - executor: E, - udp_socket: U, - listen_addr: SocketAddr, - udp_sessions: USessions, - udp_receiver: Option>>, -} - -pub struct VirtualUdpSocket { - ucore: UCore, - udp_socket: U, - peer_addr: SocketAddr, - close_callback: CloseCallback, -} - -impl Session { - fn input(&mut self, data: Vec) { - self.ubuf.push_all(data); - if let Some(waker) = self.uwaker.take() { - waker.wake(); - } - } - - fn poll_recv( - mut self: Pin<&mut Self>, - cx: &mut Context, - buf: &mut ReadBuf<'_>, - ) -> Poll> { - if self.ubuf.is_empty() { - drop(std::mem::replace( - &mut self.uwaker, - Some(cx.waker().clone()), - )); - Poll::Pending - } else { - let unfilled = buf.initialize_unfilled(); - let n = self.ubuf.read_to_buffer(unfilled); - buf.advance(n); - Poll::Ready(Ok(())) - } - } -} - -impl Datagram -where - U: UdpSocket + Sync + Unpin + Clone + Send + 'static, - E: Executor + Clone + Send + 'static, -{ - pub fn new(udp: U, listen_addr: SocketAddr, executor: E) -> crate::Result { - let sessions: USessions = Default::default(); - - let task = { - let udp = udp.clone(); - let sessions = sessions.clone(); - executor.spawn(async move { - loop { - let mut buf = Vec::with_capacity(1500); - unsafe { - buf.set_len(1500); - } - - let (n, addr) = udp.recv_from(&mut buf).await?; - buf.truncate(n); - - log::trace!("receiver from {} {}bytes", addr, n); - - let id = { - let mut hasher = DefaultHasher::default(); - addr.hash(&mut hasher); - hasher.finish() - }; - - let mut sessions = sessions.lock().await; - match sessions.get_mut(&id) { - Some(session) => session.lock()?.input(buf), - None => { - log::debug!("bad session {}", id); - } - } - } - }) - }; - - Ok(Self { - executor, - listen_addr, - udp_socket: udp, - udp_sessions: sessions, - udp_receiver: Some(task), - }) - } - - pub async fn connect( - &self, - addr: SocketAddr, - ) -> crate::Result<(SocketAddr, VirtualUdpSocket)> { - let ucore: UCore = Default::default(); - let executor = self.executor.clone(); - let sessions = self.udp_sessions.clone(); - - let id = { - let mut hasher = DefaultHasher::default(); - addr.hash(&mut hasher); - hasher.finish() - }; - - self.udp_sessions.lock().await.insert(id, ucore.clone()); - - Ok(( - self.listen_addr.clone(), - VirtualUdpSocket { - ucore, - peer_addr: addr, - udp_socket: self.udp_socket.clone(), - close_callback: Some(Box::new(move || { - executor.spawn(async move { - if let Some(session) = sessions.lock().await.remove(&id) { - drop(session) - }; - }); - })), - }, - )) - } -} - -impl NetSocket for VirtualUdpSocket -where - U: UdpSocket, -{ - fn local_addr(&self) -> crate::Result { - self.udp_socket.local_addr() - } - - fn peer_addr(&self) -> crate::Result { - Ok(Address::One(Socket::udp(self.peer_addr.clone()))) - } -} - -impl Drop for VirtualUdpSocket { - fn drop(&mut self) { - if let Some(close_callback) = self.close_callback.take() { - close_callback() - } - } -} - -impl Drop for Datagram { - fn drop(&mut self) { - if let Some(mut receiver) = self.udp_receiver.take() { - receiver.abort(); - } - } -} - -unsafe impl Sync for VirtualUdpSocket {} - -unsafe impl Send for VirtualUdpSocket {} - -impl UdpSocket for VirtualUdpSocket -where - U: UdpSocket + Unpin + Send + 'static, -{ - fn poll_recv_from( - self: std::pin::Pin<&Self>, - cx: &mut std::task::Context<'_>, - buf: &mut crate::ReadBuf<'_>, - ) -> std::task::Poll> { - let mut ucore = self.ucore.lock()?; - match Pin::new(&mut *ucore).poll_recv(cx, buf)? { - Poll::Pending => Poll::Pending, - Poll::Ready(()) => Poll::Ready(Ok(self.peer_addr.clone())), - } - } - - fn poll_send( - self: std::pin::Pin<&Self>, - cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> std::task::Poll> { - Pin::new(&self.udp_socket).poll_send_to(cx, &self.peer_addr, buf) - } - - fn poll_recv( - self: std::pin::Pin<&Self>, - cx: &mut std::task::Context<'_>, - buf: &mut crate::ReadBuf<'_>, - ) -> std::task::Poll> { - let mut ucore = self.ucore.lock()?; - Pin::new(&mut *ucore).poll_recv(cx, buf) - } - - fn poll_send_to( - self: std::pin::Pin<&Self>, - cx: &mut std::task::Context<'_>, - _: &std::net::SocketAddr, - buf: &[u8], - ) -> std::task::Poll> { - self.poll_send(cx, buf) - } -} diff --git a/src/observer/mod.rs b/src/observer/mod.rs deleted file mode 100644 index 875b1e1..0000000 --- a/src/observer/mod.rs +++ /dev/null @@ -1,171 +0,0 @@ -use std::{future::Future, pin::Pin}; - -use serde_json::json; - -use crate::{penetrate::PenetrateObserver, Executor, Observer}; - -type BoxedFuture = Pin + Send + 'static>>; - -pub struct Executable { - prog: Option, - executor: E, -} - -impl Executable -where - E: Executor + 'static, -{ - pub fn new(prog: Option, executor: E) -> Self { - Self { prog, executor } - } -} - -impl Executable -where - E: Executor, -{ - fn do_exec(&self, data: Vec) -> BoxedFuture { - let prog = self.prog.clone(); - Box::pin(async move { - match prog { - Some(prog) => { - let arg = data - .into_iter() - .map(|s| s.to_string()) - .collect::>(); - let arg = &arg; - - let proc = prog - .find(":[") - .map(|idx| prog.split_at(idx)) - .map(|(prog, args)| { - let args = args - .trim_end_matches("]") - .trim_start_matches(":[") - .split(","); - let mut proc = std::process::Command::new(prog); - proc.args(args).args(arg); - proc - }) - .unwrap_or_else(|| { - let mut proc = std::process::Command::new(prog); - proc.args(arg); - proc - }) - .stdin(std::process::Stdio::null()) - .output(); - - match proc { - Ok(o) => { - let fmt = if o.status.success() { - String::from_utf8_lossy(&o.stdout) - } else { - String::from_utf8_lossy(&o.stderr) - }; - - log::debug!("{}", fmt); - } - Err(e) => { - log::warn!("notification failed {}", e); - } - } - } - None => {} - } - }) - } -} - -impl Observer for Executable -where - E: Executor, -{ - fn on_error(&self, error: &crate::Error, address: &crate::Address) - where - Self: Sized, - { - self.executor.spawn(self.do_exec(vec![json!({ - "on": "error", - "data": { - "error": error.to_string(), - "address": address, - } - })])); - } - - fn on_stop(&self, time: std::time::Instant, address: &crate::Address) - where - Self: Sized, - { - self.executor.spawn(self.do_exec(vec![json!({ - "on": "stop", - "data": { - "last": time.elapsed(), - "from": address - } - })])); - } -} - -impl PenetrateObserver for Executable -where - E: Executor, -{ - fn on_pen_start( - &self, - client: &crate::Address, - visit: &crate::Address, - server: &crate::Address, - config: &crate::penetrate::server::Config, - ) where - Self: Sized, - { - self.executor.spawn(self.do_exec(vec![json!({ - "on": "pen_start", - "data": { - "client": client, - "visit": visit, - "server": server, - "config": config - }, - })])); - } - - fn on_pen_stop( - &self, - client: &crate::Address, - visit: &crate::Address, - server: &crate::Address, - config: &crate::penetrate::server::Config, - ) where - Self: Sized, - { - self.executor.spawn(self.do_exec(vec![json!({ - "on": "pen_stop", - "data": { - "client": client, - "visit": visit, - "server": server, - "config": config - } - })])); - } - - fn on_pen_error( - &self, - client: &crate::Address, - config: &crate::penetrate::server::Config, - error: &crate::Error, - ) where - Self: Sized, - { - self.executor.spawn(self.do_exec(vec![json!({ - "on": "pen_error", - "data": { - "client": client, - "error": error.to_string(), - "config": config - } - })])); - } -} diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 49fd084..9965980 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1,9 +1,8 @@ -#[cfg(feature = "fuso-rt-tokio")] -mod tokio; -#[cfg(feature = "fuso-rt-tokio")] -pub use crate::runtime::tokio::*; - -#[cfg(feature = "fuso-rt-smol")] -mod smol; -#[cfg(feature = "fuso-rt-smol")] -pub use crate::runtime::smol::*; +pub mod tokio; + +pub trait Runtime { + fn spawn(fut: F) -> () + where + F: std::future::Future + Send + 'static, + O: Send + 'static; +} diff --git a/src/runtime/smol/mod.rs b/src/runtime/smol/mod.rs deleted file mode 100644 index 69339ac..0000000 --- a/src/runtime/smol/mod.rs +++ /dev/null @@ -1,242 +0,0 @@ -mod penetrate; -mod udp; - -use std::{pin::Pin, sync::Arc, task::Poll}; - -use futures::Future; -use std::net::SocketAddr; -pub use udp::*; - -pub use penetrate::*; - -use crate::{ - client, kcp, server, Accepter, Address, ClientProvider, Executor, FusoStream, NetSocket, - Observer, Provider, Socket, SocketErr, Task, ToBoxStream, -}; - -type BoxedFuture = Pin> + Send + 'static>>; - -#[derive(Clone)] -pub struct FusoExecutor; -pub struct FusoAccepter; -pub struct FusoConnector(Arc, FusoExecutor>>); -pub struct FusoUdpServerProvider; -pub struct FusoUdpForwardProvider; - -unsafe impl Sync for SmolUdpSocket {} -unsafe impl Send for SmolUdpSocket {} - -pub struct FusoTcpListener { - smol_tcp: smol::net::TcpListener, - accept_fut: Option>, -} - -unsafe impl Send for FusoTcpListener {} -unsafe impl Sync for FusoTcpListener {} - -fn spawn(fut: F) -> smol::Task -where - F: Future + Send + 'static, - O: Send + 'static, -{ - static GLOBAL: once_cell::sync::Lazy> = once_cell::sync::Lazy::new(|| { - let num_cpus = num_cpus::get().max(2); - log::info!("use {} threads to process tasks", num_cpus); - for n in 0..num_cpus { - drop({ - std::thread::Builder::new() - .name(format!("fuso-{}", n)) - .spawn({ - log::debug!("task thread {} started", n); - move || loop { - if let Err(_) = std::panic::catch_unwind(|| { - smol::block_on(GLOBAL.run(smol::future::pending::<()>())) - }) { - log::error!("smol-{}", n); - }; - } - }) - }); - } - smol::Executor::new() - }); - - GLOBAL.spawn(fut) -} - -impl Executor for FusoExecutor { - fn spawn(&self, fut: F) -> crate::Task - where - F: Future + Send + 'static, - O: Send + 'static, - { - let task = Arc::new(smol::lock::Mutex::new(Some(spawn(fut)))); - Task { - abort_fn: Some(Box::new({ - let task = task.clone(); - move || { - spawn(async move { - if let Some(task) = task.lock().await.take() { - task.cancel().await; - } - }) - .detach() - } - })), - detach_fn: Some(Box::new(move || { - spawn(async move { - if let Some(task) = task.lock().await.take() { - task.detach(); - } - }) - .detach() - })), - _marked: std::marker::PhantomData, - } - } -} - -impl Provider for FusoAccepter { - type Output = BoxedFuture; - fn call(&self, socket: Socket) -> Self::Output { - Box::pin(async move { - Ok(FusoTcpListener { - smol_tcp: smol::net::TcpListener::bind(socket.as_string()).await?, - accept_fut: None, - }) - }) - } -} - -impl NetSocket for FusoTcpListener { - fn peer_addr(&self) -> crate::Result { - Ok(Address::One(Socket::tcp(self.smol_tcp.local_addr()?))) - } - - fn local_addr(&self) -> crate::Result
{ - Ok(Address::One(Socket::tcp(self.smol_tcp.local_addr()?))) - } -} - -impl NetSocket for smol::net::TcpStream { - fn peer_addr(&self) -> crate::Result
{ - Ok(Address::One(Socket::tcp((*self).peer_addr()?))) - } - - fn local_addr(&self) -> crate::Result
{ - Ok(Address::One(Socket::tcp((*self).local_addr()?))) - } -} - -impl Accepter for FusoTcpListener { - type Stream = FusoStream; - fn poll_accept( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> Poll> { - let mut fut = match self.accept_fut.take() { - Some(fut) => fut, - None => { - let smol_tcp = self.smol_tcp.clone(); - Box::pin(async move { - let (stream, addr) = smol_tcp.accept().await?; - log::debug!("accept connection from {}", addr); - Ok(stream.into_boxed_stream()) - }) - } - }; - - match Pin::new(&mut fut).poll(cx) { - Poll::Ready(ready) => Poll::Ready(ready), - Poll::Pending => { - drop(std::mem::replace(&mut self.accept_fut, Some(fut))); - Poll::Pending - } - } - } -} - -impl Provider for FusoConnector { - type Output = BoxedFuture; - fn call(&self, socket: Socket) -> Self::Output { - let kcp = self.0.clone(); - Box::pin(async move { - if socket.is_tcp() { - smol::net::TcpStream::connect(socket.as_string()) - .await - .map(ToBoxStream::into_boxed_stream) - .map_err(Into::into) - } else if socket.is_kcp() { - kcp.core().connect(socket.as_string()).await?; - kcp.connect() - .await - .map(ToBoxStream::into_boxed_stream) - .map_err(Into::into) - } else { - Err(SocketErr::NotSupport(socket).into()) - } - }) - } -} - -impl Provider for FusoUdpServerProvider { - type Output = BoxedFuture>; - fn call(&self, socket: Socket) -> Self::Output { - Box::pin(async move { - SmolUdpSocket::bind(socket.as_string()) - .await - .map(|(_, udp_server)| Arc::new(udp_server)) - .map_err(Into::into) - }) - } -} - -impl Provider<()> for FusoUdpForwardProvider { - type Output = BoxedFuture<(SocketAddr, SmolUdpSocket)>; - fn call(&self, _: ()) -> Self::Output { - Box::pin(async move { SmolUdpSocket::bind("0.0.0.0:0").await.map_err(Into::into) }) - } -} - -#[inline] -pub fn block_on(fut: F) -> crate::Result<()> -where - F: Future> + Send, -{ - smol::block_on(fut) -} - -pub fn builder_server( - observer: O, -) -> server::ServerBuilder -where - O: Observer + Send + Sync + 'static, -{ - server::ServerBuilder { - is_mixed: false, - executor: FusoExecutor, - handshake: None, - observer: Some(Arc::new(observer)), - server_provider: Arc::new(FusoAccepter), - } -} - -pub async fn builder_client( -) -> crate::Result> { - Ok(client::ClientBuilder { - executor: FusoExecutor, - handshake: None, - retry_delay: None, - maximum_retries: None, - client_provider: ClientProvider { - server_address: Default::default(), - connect_provider: Arc::new({ - let (_, udp_server) = SmolUdpSocket::bind("0.0.0.0:0").await?; - FusoConnector(Arc::new(kcp::KcpConnector::new( - Arc::new(udp_server), - FusoExecutor, - ))) - }), - }, - }) -} diff --git a/src/runtime/smol/penetrate.rs b/src/runtime/smol/penetrate.rs deleted file mode 100644 index 36cba78..0000000 --- a/src/runtime/smol/penetrate.rs +++ /dev/null @@ -1,67 +0,0 @@ -use std::{net::ToSocketAddrs, pin::Pin, sync::Arc}; - -use futures::Future; - -use crate::{ - client::Route, - penetrate::SocksUdpForwardMock, - udp::{Datagram, VirtualUdpSocket}, - Addr, FusoExecutor, FusoStream, InvalidAddr, Provider, SmolUdpSocket, Socket, SocketErr, - SocketKind, ToBoxStream, WrappedProvider, -}; - -type BoxedFuture = Pin> + Send + 'static>>; - -pub struct FusoPenetrateConnector { - udp_server: Arc, FusoExecutor>>, -} - -pub struct FusoUdpForwardProvider(Arc, FusoExecutor>>); - -impl FusoPenetrateConnector { - pub async fn new() -> crate::Result { - let (addr, udp_server) = SmolUdpSocket::bind("0.0.0.0:0").await?; - Ok(Self { - udp_server: Arc::new(Datagram::new(Arc::new(udp_server), addr, FusoExecutor)?), - }) - } -} - -impl Provider for FusoPenetrateConnector { - type Output = BoxedFuture>; - - fn call(&self, socket: Socket) -> Self::Output { - let udp_server = self.udp_server.clone(); - Box::pin(async move { - match socket.kind() { - SocketKind::Tcp => Ok(Route::Forward( - smol::net::TcpStream::connect(socket.as_string()) - .await? - .into_boxed_stream(), - )), - SocketKind::Ufd => Ok(Route::Provider(WrappedProvider::wrap(SocksUdpForwardMock( - WrappedProvider::wrap(FusoUdpForwardProvider(udp_server)), - )))), - _ => Err(SocketErr::NotSupport(socket).into()), - } - }) - } -} - -impl Provider for FusoUdpForwardProvider { - type Output = BoxedFuture<(std::net::SocketAddr, VirtualUdpSocket>)>; - fn call(&self, addr: Addr) -> Self::Output { - let udp_server = self.0.clone(); - Box::pin(async move { - log::debug!("try connect to udp {}", addr); - - let addr = addr - .as_string() - .to_socket_addrs()? - .next() - .ok_or(InvalidAddr::Domain(addr.as_string()))?; - - udp_server.connect(addr).await.map_err(Into::into) - }) - } -} diff --git a/src/runtime/smol/udp.rs b/src/runtime/smol/udp.rs deleted file mode 100644 index e643927..0000000 --- a/src/runtime/smol/udp.rs +++ /dev/null @@ -1,195 +0,0 @@ -use std::{pin::Pin, net::SocketAddr, task::Poll}; - -use futures::Future; -use smol::net::AsyncToSocketAddrs; - -use crate::{NetSocket, Address, Socket, UdpSocket}; - -type UFuture = Pin> + 'static>>; - -pub struct SmolUdpSocket { - smol_udp: smol::net::UdpSocket, - recv_fut: std::sync::Mutex>>, - recv_from_fut: std::sync::Mutex>>, - send_fut: std::sync::Mutex>>, - send_to_fut: std::sync::Mutex>>, -} - -impl NetSocket for SmolUdpSocket { - fn local_addr(&self) -> crate::Result
{ - Ok(Address::One(Socket::udp(self.smol_udp.local_addr()?))) - } - - fn peer_addr(&self) -> crate::Result
{ - Ok(Address::One(Socket::udp(self.smol_udp.peer_addr()?))) - } -} - -impl SmolUdpSocket { - pub async fn bind(addr: A) -> crate::Result<(SocketAddr, Self)> { - let udp_server = smol::net::UdpSocket::bind(addr).await?; - let listen_addr = udp_server.local_addr()?; - Ok(( - listen_addr, - Self { - smol_udp: udp_server, - recv_fut: Default::default(), - recv_from_fut: Default::default(), - send_fut: Default::default(), - send_to_fut: Default::default(), - }, - )) - } - - pub async fn connect(&self, addr: A) -> crate::Result<()> { - self.smol_udp.connect(addr).await.map_err(Into::into) - } -} - -impl UdpSocket for SmolUdpSocket -where - Self: Unpin, -{ - fn poll_recv( - self: Pin<&Self>, - cx: &mut std::task::Context<'_>, - buf: &mut crate::ReadBuf<'_>, - ) -> Poll> { - let mut fut = match self.recv_fut.lock()?.take() { - Some(fut) => fut, - None => { - let smol_udp = self.smol_udp.clone(); - let unfilled = buf.initialize_unfilled(); - let unfilled_len = unfilled.len(); - let unfilled_ptr = unfilled.as_mut_ptr(); - Box::pin(async move { - let buf = unsafe { - std::ptr::slice_from_raw_parts_mut(unfilled_ptr, unfilled_len) - .as_mut() - .unwrap_unchecked() - }; - - Ok(smol_udp.recv(buf).await?) - }) - } - }; - - match Pin::new(&mut fut).poll(cx)? { - Poll::Ready(n) => { - buf.advance(n); - Poll::Ready(Ok(())) - } - Poll::Pending => { - let mut locked = self.recv_fut.lock()?; - drop(std::mem::replace(&mut *locked, Some(fut))); - Poll::Pending - } - } - } - - fn poll_recv_from( - self: Pin<&Self>, - cx: &mut std::task::Context<'_>, - buf: &mut crate::ReadBuf<'_>, - ) -> Poll> { - let mut fut = match self.recv_from_fut.lock()?.take() { - Some(fut) => fut, - None => { - let smol_udp = self.smol_udp.clone(); - let unfilled_buf = buf.initialize_unfilled(); - let unfilled_len = unfilled_buf.len(); - let unfilled_ptr = unfilled_buf.as_mut_ptr(); - Box::pin(async move { - let buf = unsafe { - std::ptr::slice_from_raw_parts_mut(unfilled_ptr, unfilled_len) - .as_mut() - .unwrap_unchecked() - }; - smol_udp.recv_from(buf).await.map_err(Into::into) - }) - } - }; - - match Pin::new(&mut fut).poll(cx) { - Poll::Ready(Ok((n, addr))) => { - buf.advance(n); - Poll::Ready(Ok(addr)) - } - Poll::Ready(Err(e)) => Poll::Ready(Err(e)), - Poll::Pending => { - let mut locked = self.recv_from_fut.lock()?; - drop(std::mem::replace(&mut *locked, Some(fut))); - Poll::Pending - } - } - } - - fn poll_send( - self: Pin<&Self>, - cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> Poll> { - let mut fut = match self.send_fut.lock()?.take() { - Some(fut) => fut, - None => { - let smol_udp = self.smol_udp.clone(); - let buf_ptr = buf.as_ptr(); - let buf_len = buf.len(); - Box::pin(async move { - let buf = unsafe { - std::ptr::slice_from_raw_parts(buf_ptr, buf_len) - .as_ref() - .unwrap_unchecked() - }; - smol_udp.send(buf).await.map_err(Into::into) - }) - } - }; - - match Pin::new(&mut fut).poll(cx) { - Poll::Ready(ready) => Poll::Ready(ready), - Poll::Pending => { - let mut locked = self.send_fut.lock()?; - drop(std::mem::replace(&mut *locked, Some(fut))); - Poll::Pending - } - } - } - - fn poll_send_to( - self: Pin<&Self>, - cx: &mut std::task::Context<'_>, - addr: &SocketAddr, - buf: &[u8], - ) -> Poll> { - let mut fut = match self.send_to_fut.lock()?.take() { - Some(fut) => fut, - None => { - let smol_udp = self.smol_udp.clone(); - let addr = addr as *const SocketAddr; - let buf_ptr = buf.as_ptr(); - let buf_len = buf.len(); - Box::pin(async move { - let (buf, addr) = unsafe { - ( - std::ptr::slice_from_raw_parts(buf_ptr, buf_len) - .as_ref() - .unwrap_unchecked(), - addr.as_ref().unwrap_unchecked(), - ) - }; - smol_udp.send_to(buf, addr).await.map_err(Into::into) - }) - } - }; - - match Pin::new(&mut fut).poll(cx) { - Poll::Ready(ready) => Poll::Ready(ready), - Poll::Pending => { - let mut locked = self.send_to_fut.lock()?; - drop(std::mem::replace(&mut *locked, Some(fut))); - Poll::Pending - } - } - } -} diff --git a/src/runtime/tokio/kcp.rs b/src/runtime/tokio/kcp.rs new file mode 100644 index 0000000..5d5d8bf --- /dev/null +++ b/src/runtime/tokio/kcp.rs @@ -0,0 +1,144 @@ +use std::{net::SocketAddr, sync::Arc, task::Poll}; + +use tokio::io::ReadBuf; + +use crate::{ + core::{ + future::StoredFuture, + net::{BoxedDatagram, KcpListener}, + BoxedFuture, + }, + error, + runtime::Runtime, +}; + +pub struct KcpWithTokioRuntime; + +pub struct TokioUdpSocket(Arc); + +impl KcpListener { + pub async fn bind_with_tokio(conf: kcp_rust::Config, addr: A) -> error::Result + where + A: Into, + { + let udp = crate::core::net::UdpSocket::bind::(addr).await?; + let listener = kcp_rust::KcpListener::new::(udp, conf)?; + Ok(Self { + inner: Arc::new(listener), + stored: StoredFuture::new(), + }) + } +} + +impl crate::core::net::UdpProvider for KcpWithTokioRuntime { + type Binder = Self; + + type Connect = Self; +} + +impl crate::core::Provider>>> + for KcpWithTokioRuntime +{ + type Arg = SocketAddr; + + fn call(addr: Self::Arg) -> BoxedFuture<'static, error::Result>> { + Box::pin(async move { + let boxed: BoxedDatagram<'_> = Box::new(TokioUdpSocket(Arc::new({ + tokio::net::UdpSocket::bind(addr).await? + }))); + Ok(boxed) + }) + } +} + +impl kcp_rust::KcpRuntime for KcpWithTokioRuntime { + type Err = error::FusoError; + + type Runner = Self; + + type Timer = Self; + + fn timer() -> Self::Timer { + Self + } +} + +impl kcp_rust::Runner for KcpWithTokioRuntime { + type Err = error::FusoError; + + fn start(process: kcp_rust::Background) -> std::result::Result<(), Self::Err> { + log::debug!("starting {:?}", process.kind()); + crate::runtime::tokio::TokioRuntime::spawn(async move{ + process.await.unwrap(); + log::debug!("kcp finished ....") + }); + Ok(()) + } +} + +impl kcp_rust::Timer for KcpWithTokioRuntime { + type Ret = (); + + type Output = BoxedFuture<'static, Self::Ret>; + + fn sleep(&self, time: std::time::Duration) -> Self::Output { + Box::pin(async move { + let _ = tokio::time::sleep(time).await; + }) + } +} + +impl crate::core::net::Datagram for TokioUdpSocket { + fn boxed_clone(&self) -> Box { + Box::new(TokioUdpSocket(self.0.clone())) + } +} + +impl crate::core::net::AsyncRecvfrom for TokioUdpSocket { + fn poll_recvfrom( + &mut self, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + let mut buf = ReadBuf::new(buf); + match self.0.poll_recv_from(cx, &mut buf)? { + Poll::Pending => Poll::Pending, + Poll::Ready(addr) => Poll::Ready(Ok((addr, buf.filled().len()))), + } + } +} + +impl crate::core::net::AsyncSendTo for TokioUdpSocket { + fn poll_sendto( + &mut self, + cx: &mut std::task::Context<'_>, + addr: &std::net::SocketAddr, + buf: &[u8], + ) -> std::task::Poll> { + self.0.poll_send_to(cx, buf, *addr) + } +} + +impl crate::core::net::AsyncRecv for TokioUdpSocket { + fn poll_recv( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + let mut buf = ReadBuf::new(buf); + match self.0.poll_recv(cx, &mut buf)? { + Poll::Pending => Poll::Pending, + Poll::Ready(()) => Poll::Ready(Ok(buf.filled().len())), + } + } +} + +impl crate::core::net::AsyncSend for TokioUdpSocket { + fn poll_send( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + self.0.poll_send(cx, buf) + } +} diff --git a/src/runtime/tokio/mod.rs b/src/runtime/tokio/mod.rs index aae7658..a8cb550 100644 --- a/src/runtime/tokio/mod.rs +++ b/src/runtime/tokio/mod.rs @@ -1,266 +1,28 @@ -mod penetrate; -pub use penetrate::*; - -use std::{future::Future, net::SocketAddr, pin::Pin, sync::Arc, task::Poll}; - -use tokio::net::TcpListener; - -use crate::{ - client::{self}, - kcp::{self}, - ready, server, Accepter, Address, ClientProvider, Executor, FusoStream, Kind, NetSocket, - Observer, Provider, Socket, SocketErr, Task, ToBoxStream, UdpSocket, -}; - -type BoxedFuture = Pin> + Send + 'static>>; - -#[derive(Clone, Copy)] -pub struct FusoExecutor; -pub struct FusoTcpListener(tokio::net::TcpListener); -pub struct FusoAccepter; -pub struct FusoConnector(Arc, FusoExecutor>>); - -pub struct FusoUdpSocket; - -pub struct FusoUdpServerProvider; -pub struct FusoUdpForwardProvider; - -impl Executor for FusoExecutor { - fn spawn(&self, fut: F) -> Task - where - F: std::future::Future + Send + 'static, - O: Send + 'static, - { - let task = tokio::spawn(fut); - - Task { - detach_fn: None, - abort_fn: Some(Box::new(move || { - task.abort(); - })), - _marked: std::marker::PhantomData, - } - } -} - -impl Provider for FusoAccepter { - type Output = BoxedFuture; - - fn call(&self, socket: Socket) -> Self::Output { - if socket.is_tcp() || socket.is_mixed() { - Box::pin(async move { - Ok({ - TcpListener::bind(socket.as_string()) - .await - .map(|tcp| FusoTcpListener(tcp))? - }) - }) - } else { - Box::pin(async move { Err(Kind::Unsupported(socket).into()) }) - } - } -} - -impl NetSocket for tokio::net::TcpStream { - fn peer_addr(&self) -> crate::Result
{ - Ok(Address::One(Socket::tcp(self.peer_addr()?))) - } - - fn local_addr(&self) -> crate::Result
{ - Ok(Address::One(Socket::tcp(self.local_addr()?))) - } -} - -impl NetSocket for FusoTcpListener { - fn local_addr(&self) -> crate::Result { - Ok(Address::One(Socket::tcp(self.0.local_addr()?))) - } - - fn peer_addr(&self) -> crate::Result
{ - Ok(Address::One(Socket::tcp(self.0.local_addr()?))) - } -} - -impl Accepter for FusoTcpListener { - type Stream = FusoStream; - fn poll_accept( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> Poll> { - match self.0.poll_accept(cx) { - Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())), - Poll::Pending => Poll::Pending, - Poll::Ready(Ok((tcp, addr))) => { - log::debug!("accept connection from {}", addr); - - Poll::Ready(Ok(tcp.into_boxed_stream())) - } - } - } -} - -impl Provider for FusoConnector { - type Output = BoxedFuture; - - fn call(&self, socket: Socket) -> Self::Output { - let kcp = self.0.clone(); - Box::pin(async move { - Ok({ - if socket.is_tcp() { - tokio::net::TcpStream::connect(socket.as_string()) - .await? - .into_boxed_stream() - } else if socket.is_kcp() { - kcp.core().connect(socket.as_string()).await?; - kcp.connect().await?.into_boxed_stream() - } else { - return Err(SocketErr::NotSupport(socket).into()); - } - }) - }) - } -} - -impl ClientProvider { - pub async fn with_tokio() -> crate::Result { - Ok(ClientProvider { - server_address: Default::default(), - connect_provider: Arc::new({ - let udp = tokio::net::UdpSocket::bind("0.0.0.0:0").await?; - FusoConnector(Arc::new(kcp::KcpConnector::new( - Arc::new(udp), - FusoExecutor, - ))) - }), - }) - } -} - -impl NetSocket for tokio::net::UdpSocket { - fn peer_addr(&self) -> crate::Result
{ - Ok(Address::One(Socket::udp((*self).peer_addr()?))) - } - - fn local_addr(&self) -> crate::Result
{ - Ok(Address::One(Socket::udp((*self).local_addr()?))) - } -} - -impl UdpSocket for tokio::net::UdpSocket { - fn poll_recv_from( - self: Pin<&Self>, - cx: &mut std::task::Context<'_>, - buf: &mut crate::ReadBuf<'_>, - ) -> Poll> { - match ready!(tokio::net::UdpSocket::poll_recv_from(&self, cx, buf)) { - Err(e) => Poll::Ready(Err(e.into())), - Ok(addr) => Poll::Ready(Ok(addr)), - } - } - - fn poll_recv( - self: Pin<&Self>, - cx: &mut std::task::Context<'_>, - buf: &mut crate::ReadBuf<'_>, - ) -> Poll> { - match ready!(tokio::net::UdpSocket::poll_recv(&self, cx, buf)) { - Err(e) => Poll::Ready(Err(e.into())), - Ok(()) => Poll::Ready(Ok(())), - } - } - - fn poll_send( - self: Pin<&Self>, - cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> Poll> { - Poll::Ready({ - ready!(tokio::net::UdpSocket::poll_send(&self, cx, buf)).map_err(Into::into) - }) - } - - fn poll_send_to( - self: Pin<&Self>, - cx: &mut std::task::Context<'_>, - addr: &std::net::SocketAddr, - buf: &[u8], - ) -> Poll> { - Poll::Ready({ - ready!(tokio::net::UdpSocket::poll_send_to( - &self, - cx, - buf, - addr.clone() - )) - .map_err(Into::into) - }) - } -} - -impl Provider<()> for FusoUdpForwardProvider { - type Output = BoxedFuture<(SocketAddr, tokio::net::UdpSocket)>; - - fn call(&self, _: ()) -> Self::Output { - Box::pin(async move { - let udp = tokio::net::UdpSocket::bind(SocketAddr::from(([0, 0, 0, 0], 0))).await?; - let addr = udp.local_addr()?; - - log::debug!("udp listening on {}", addr); - - Ok((addr, udp)) - }) - } -} - -impl Provider for FusoUdpServerProvider { - type Output = BoxedFuture>; - - fn call(&self, socket: Socket) -> Self::Output { - Box::pin(async move { - Ok(Arc::new({ - tokio::net::UdpSocket::bind(socket.as_string()) - .await - .map_err(|e| { - log::warn!("udp bind failed addr={}, err={}", socket.addr(), e); - e - })? - })) - }) - } -} - -pub fn block_on(fut: F) -> crate::Result<()> -where - F: Future> + Send, -{ - tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build()? - .block_on(fut) -} - -pub fn builder_server( - observer: O, -) -> server::ServerBuilder -where - O: Observer + Send + Sync + 'static, -{ - server::ServerBuilder { - is_mixed: false, - executor: FusoExecutor, - handshake: None, - observer: Some(Arc::new(observer)), - server_provider: Arc::new(FusoAccepter), - } -} - -pub async fn builder_client( -) -> crate::Result> { - Ok(client::ClientBuilder { - executor: FusoExecutor, - handshake: None, - client_provider: ClientProvider::with_tokio().await?, - retry_delay: None, - maximum_retries: None, - }) -} +mod kcp; +mod tcp; + +pub use kcp::*; +pub use tcp::*; + +pub struct TokioRuntime; + +pub fn block_on(f: F) -> T +where + F: std::future::Future, +{ + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + .block_on(f) +} + +impl super::Runtime for TokioRuntime { + fn spawn(fut: F) -> () + where + F: std::future::Future + Send + 'static, + O: Send + 'static, + { + tokio::spawn(fut); + } +} diff --git a/src/runtime/tokio/penetrate.rs b/src/runtime/tokio/penetrate.rs deleted file mode 100644 index 4ebb9f2..0000000 --- a/src/runtime/tokio/penetrate.rs +++ /dev/null @@ -1,124 +0,0 @@ -use std::{ - net::{SocketAddr, ToSocketAddrs}, - pin::Pin, - sync::Arc, -}; - -use tokio::net::TcpStream; - -use crate::{ - client::Route, - kcp::KcpConnector, - penetrate::SocksUdpForwardMock, - udp::{Datagram, VirtualUdpSocket}, - Addr, FusoExecutor, FusoStream, InvalidAddr, Provider, Socket, SocketErr, SocketKind, - ToBoxStream, WrappedProvider, -}; - -type BoxedFuture = Pin> + Send + 'static>>; - -pub struct TokioTcpConnector; - -pub struct FusoTcpAndKcpConnector { - kconnector: Arc, FusoExecutor>>, -} - -pub struct FusoPenetrateConnector { - udp: Arc, FusoExecutor>>, -} - -pub struct UdpForwardClientProvider(Arc, FusoExecutor>>); - -impl FusoPenetrateConnector { - pub async fn new() -> crate::Result { - let udp_server = tokio::net::UdpSocket::bind("0.0.0.0:0").await?; - let listen_addr = udp_server.local_addr()?; - Ok(Self { - udp: Arc::new(Datagram::new( - Arc::new(udp_server), - listen_addr, - FusoExecutor, - )?), - }) - } -} - -impl Provider for TokioTcpConnector { - type Output = BoxedFuture; - - fn call(&self, socket: Socket) -> Self::Output { - Box::pin(async move { - tokio::net::TcpStream::connect(socket.as_string()) - .await - .map(ToBoxStream::into_boxed_stream) - .map_err(Into::into) - }) - } -} - -impl Provider for FusoTcpAndKcpConnector { - type Output = BoxedFuture; - - fn call(&self, socket: Socket) -> Self::Output { - let kconnector = self.kconnector.clone(); - Box::pin(async move { - if socket.is_tcp() { - tokio::net::TcpStream::connect(socket.as_string()) - .await - .map(ToBoxStream::into_boxed_stream) - .map_err(Into::into) - } else { - kconnector - .connect() - .await - .map(ToBoxStream::into_boxed_stream) - .map_err(Into::into) - } - }) - } -} - -impl Provider for FusoPenetrateConnector { - type Output = BoxedFuture>; - - fn call(&self, socket: Socket) -> Self::Output { - let udp = self.udp.clone(); - Box::pin(async move { - match socket.kind() { - SocketKind::Tcp => Ok(Route::Forward( - TcpStream::connect(socket.as_string()) - .await? - .into_boxed_stream(), - )), - SocketKind::Ufd => { - let provider = WrappedProvider::wrap(UdpForwardClientProvider(udp)); - - Ok(Route::Provider(WrappedProvider::wrap(SocksUdpForwardMock( - provider, - )))) - } - _ => Err(SocketErr::NotSupport(socket).into()), - } - }) - } -} - -impl Provider for UdpForwardClientProvider { - type Output = BoxedFuture<(SocketAddr, VirtualUdpSocket>)>; - - fn call(&self, addr: Addr) -> Self::Output { - let udp = self.0.clone(); - - Box::pin(async move { - log::debug!("try connect to udp {}", addr); - - let addr = addr - .as_string() - .to_socket_addrs()? - .next() - .ok_or(InvalidAddr::Domain(addr.as_string()))?; - - udp.connect(addr).await - }) - } -} diff --git a/src/runtime/tokio/tcp.rs b/src/runtime/tokio/tcp.rs new file mode 100644 index 0000000..0e7870a --- /dev/null +++ b/src/runtime/tokio/tcp.rs @@ -0,0 +1,127 @@ +use std::{net::SocketAddr, task::Poll}; + +use tokio::io::ReadBuf; + +use crate::{ + core::{ + accepter::{Accepter, BoxedAccepter}, + io, + net::{TcpListener, TcpProvider, TcpStream}, + BoxedFuture, BoxedStream, Provider, + }, + error, +}; + +impl io::AsyncRead for tokio::net::TcpStream { + fn poll_read( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + let mut buf = ReadBuf::new(buf); + match tokio::io::AsyncRead::poll_read(self, cx, &mut buf)? { + Poll::Pending => Poll::Pending, + Poll::Ready(()) => Poll::Ready(Ok(buf.filled().len())), + } + } +} + +impl io::AsyncWrite for tokio::net::TcpStream { + fn poll_flush( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + tokio::io::AsyncWrite::poll_flush(self, cx).map_err(Into::into) + } + + fn poll_write( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + tokio::io::AsyncWrite::poll_write(self, cx, buf).map_err(Into::into) + } +} + +impl TcpStream { + pub async fn connect_with_tokio(addr: A) -> error::Result + where + A: tokio::net::ToSocketAddrs, + { + let stream = tokio::net::TcpStream::connect(addr).await?; + Ok(TcpStream { + stream: BoxedStream::new(stream), + }) + } +} + +impl TcpListener { + pub async fn bind_with_tokio(addr: A) -> error::Result + where + A: tokio::net::ToSocketAddrs, + { + let listener = tokio::net::TcpListener::bind(addr).await?; + Ok(TcpListener { + accepter: BoxedAccepter::new(listener), + }) + } +} + +pub struct TokioTcpProver; + +impl TcpProvider for TokioTcpProver { + type Listener = Self; + + type Connector = Self; +} + +impl Provider>>> for TokioTcpProver { + type Arg = SocketAddr; + + fn call(addr: Self::Arg) -> BoxedFuture<'static, error::Result>> { + Box::pin(async move { + Ok(BoxedStream::new( + tokio::net::TcpStream::connect(addr).await?, + )) + }) + } +} + +impl + Provider< + BoxedFuture< + 'static, + error::Result)>>, + >, + > for TokioTcpProver +{ + type Arg = SocketAddr; + + fn call( + addr: Self::Arg, + ) -> BoxedFuture< + 'static, + error::Result)>>, + > { + Box::pin(async move { + Ok(BoxedAccepter::new( + tokio::net::TcpListener::bind(addr).await?, + )) + }) + } +} + +impl Accepter for tokio::net::TcpListener { + type Output = (std::net::SocketAddr, BoxedStream<'static>); + fn poll_accept( + self: std::pin::Pin<&mut Self>, + ctx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match tokio::net::TcpListener::poll_accept(&*self, ctx)? { + std::task::Poll::Pending => Poll::Pending, + std::task::Poll::Ready((stream, addr)) => { + Poll::Ready(Ok((addr, BoxedStream::new(stream)))) + } + } + } +} diff --git a/src/server/builder.rs b/src/server/builder.rs deleted file mode 100644 index da28a8c..0000000 --- a/src/server/builder.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::{pin::Pin, sync::Arc}; - -use crate::{ - generator::Generator, DecorateProvider, Environ, Fuso, Provider, Socket, Stream, - WrappedProvider, -}; - -use super::{Handshake, Processor, Server}; - -type BoxedFuture = Pin> + Send + 'static>>; - -pub struct ServerBuilder { - pub(crate) executor: E, - pub(crate) is_mixed: bool, - pub(crate) observer: Option>, - pub(crate) handshake: Option>, - pub(crate) server_provider: Arc

, -} - -impl ServerBuilder -where - S: Stream + Send + 'static, -{ - pub fn using_handshake(mut self, handshake: F) -> Self - where - F: Provider>)>> - + Send - + Sync - + 'static, - { - self.handshake = Some(WrappedProvider::wrap(handshake)); - self - } - - pub fn build(self, handler: H) -> Fuso> - where - G: Generator>> + Send + 'static, - H: Provider< - (S, Processor), - Output = BoxedFuture<(G, Arc)>, - > + Send - + Sync - + 'static, - { - Fuso(Server { - handler: Arc::new(handler), - bind: Socket::tcp(([0, 0, 0, 0], 0)), - executor: self.executor, - provider: self.server_provider, - observer: self.observer, - handshake: self.handshake.map(Arc::new), - controller: None, - }) - } -} diff --git a/src/server/mod.rs b/src/server/mod.rs index e4b0c82..e845c2b 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,170 +1,3 @@ -mod builder; - -pub use builder::*; - -use crate::{ - generator::GeneratorEx, Controller, DecorateProvider, Observer, Processor, Serve, Socket, - WrappedProvider, -}; -use std::{pin::Pin, sync::Arc}; - -use crate::{generator::Generator, Accepter, AccepterExt, Executor, Fuso, Provider, Stream}; - -pub type Environ = Arc; -pub type Handshake = WrappedProvider>)>; - -type BoxedFuture = Pin> + Send + 'static>>; - -pub struct Server { - pub(crate) bind: Socket, - pub(crate) executor: E, - pub(crate) handler: Arc, - pub(crate) provider: Arc

, - pub(crate) observer: Option>, - pub(crate) handshake: Option>>, - pub(crate) controller: Option>, -} - -impl Server -where - E: Executor + Send + Clone + 'static, - A: Accepter + Unpin + Send + 'static, - H: Provider<(S, Processor), Output = BoxedFuture<(G, Environ)>> - + Send - + Sync - + 'static, - G: Generator>> + Unpin + Send + 'static, - S: Stream + Send + 'static, - O: Observer + Send + Sync + 'static, - P: Provider> + Send + Sync + 'static, -{ - pub async fn run(self) -> crate::Result<()> { - let mut accepter = self.provider.call(self.bind.clone()).await?; - let controller = self.controller.clone(); - - log::info!("the server listens on {}", accepter.local_addr()?); - - loop { - let client = accepter.accept().await?; - - let executor = self.executor.clone(); - let handshake = self.handshake.clone(); - let provider = self.provider.clone(); - let handler = self.handler.clone(); - let observer = self.observer.clone(); - - let client_addr = match client.peer_addr() { - Ok(addr) => addr, - Err(e) => { - log::error!("{}", e); - continue; - } - }; - - observer.on_connect(&client_addr); - - let controller = controller.clone(); - - self.executor.spawn(async move { - let now = std::time::Instant::now(); - - let client = match handshake.as_ref() { - None => Ok((client, None)), - Some(provider) => { - log::debug!("start shaking hands"); - provider.call(client).await - } - }; - - observer.on_handshake(&client_addr); - - let generator = match client { - Err(e) => { - log::warn!("handshake failed {}", e); - Err(e) - } - Ok((client, decorator)) => { - log::debug!("start processing the connection"); - handler - .call(( - client, - Processor::new(provider, observer.clone(), decorator), - )) - .await - } - }; - - if generator.is_err() { - log::warn!("failed to handle connection {}", unsafe { - generator.unwrap_err_unchecked() - }); - return; - } - - let (generator, env) = unsafe { generator.unwrap_unchecked() }; - - let run_fut = async move { - let mut generator = generator; - loop { - match generator.next().await { - Ok(None) => { - log::warn!("stop processing"); - observer.on_stop(now, &client_addr); - break Ok(()); - } - Err(e) => { - log::warn!("An error occurred {}", e); - observer.on_error(&e, &client_addr); - break Err(e); - } - Ok(Some(fut)) => { - executor.spawn(fut); - } - } - } - }; - - let err = match controller { - None => run_fut.await, - Some(controller) => controller.register(env, Box::pin(run_fut)), - }; - - if let Err(e) = err { - log::error!("{}", e); - } - }); - } - } -} - -impl Fuso> -where - E: Executor + Send + Clone + 'static, - A: Accepter + Unpin + Send + 'static, - O: Observer + Sync + Send + 'static, - G: Generator>> + Unpin + Send + 'static, - P: Provider> + Send + Sync + 'static, - S: Stream + Send + 'static, - H: Provider<(S, Processor), Output = BoxedFuture<(G, Environ)>> - + Send - + Sync - + 'static, -{ - pub fn bind>(self, bind: T) -> Self { - Fuso(Server { - bind: bind.into(), - provider: self.0.provider, - executor: self.0.executor, - handshake: self.0.handshake, - handler: self.0.handler, - observer: self.0.observer, - controller: self.0.controller, - }) - } - - pub fn run(self) -> Fuso { - Fuso(Serve { - fut: Box::pin(self.0.run()), - }) - } -} +pub trait Handshake { + +} \ No newline at end of file diff --git a/src/service/mod.rs b/src/service/mod.rs new file mode 100644 index 0000000..fbee5bc --- /dev/null +++ b/src/service/mod.rs @@ -0,0 +1,8 @@ +use std::net::IpAddr; + +pub struct PortForward{ + bind: IpAddr, + ports: Vec, +} + + From e3ec5e7d0c7b215d3f5e4a10ac64c526013179c8 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Tue, 5 Mar 2024 15:16:56 +0000 Subject: [PATCH 02/29] update code --- src/core/future.rs | 32 +++++++++++++++++ src/core/io/mod.rs | 87 ++++++++++++++++++++++++++++++++++++++++++++-- src/core/mod.rs | 1 + src/core/split.rs | 3 +- 4 files changed, 120 insertions(+), 3 deletions(-) diff --git a/src/core/future.rs b/src/core/future.rs index 7f8df2e..7d0575f 100644 --- a/src/core/future.rs +++ b/src/core/future.rs @@ -5,8 +5,26 @@ use std::{ use std::future::Future; +use super::BoxedFuture; + +#[macro_export] +macro_rules! select { + ($($fut: expr),*) => { + $crate::core::future::Select(vec![ + $( + { + let fut: BoxedFuture<'a, _> = Box::pin($fut); + fut + } + ),* + ]).await + }; +} + pub struct StoredFuture<'a, O>(Option + 'a>>>); +pub struct Select<'a, O>(pub Vec>); + unsafe impl<'a, O> Send for StoredFuture<'a, O> {} unsafe impl<'a, O> Sync for StoredFuture<'a, O> {} @@ -30,3 +48,17 @@ impl<'a, O> StoredFuture<'a, O> { Self(Default::default()) } } + +impl std::future::Future for Select<'_, O> { + type Output = O; + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + for fut in self.0.iter_mut() { + match Pin::new(fut).poll(cx) { + Poll::Pending => continue, + Poll::Ready(o) => return Poll::Ready(o), + } + } + + Poll::Pending + } +} diff --git a/src/core/io/mod.rs b/src/core/io/mod.rs index 41b6b26..2afc52f 100644 --- a/src/core/io/mod.rs +++ b/src/core/io/mod.rs @@ -1,9 +1,14 @@ use std::{ + io, pin::Pin, task::{Context, Poll}, }; -use crate::error; +use crate::{error, select}; + +use super::BoxedFuture; + +use crate::core::split::SplitStream; pub trait AsyncRead { fn poll_read( @@ -46,8 +51,55 @@ pub trait AsyncWriteExt: AsyncWrite + Unpin { { Flush { writer: self } } + + fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAll<'a, Self> + where + Self: Sized, + { + WriteAll { + writer: self, + buf, + pos: 0, + } + } +} + +pub trait StreamExt { + fn transfer<'a, T>(self, to: T) -> BoxedFuture<'a, error::Result<()>> + where + T: AsyncRead + AsyncWrite + Unpin + Send + 'a, + Self: Sized + AsyncRead + AsyncWrite + Unpin + Send + 'a, + { + Box::pin(async move { + let (reader_2, writer_2) = to.split(); + let (reader_1, writer_1) = self.split(); + select![reader_1.copy(writer_2), reader_2.copy(writer_1)] + }) + } + + fn copy<'a, T>(mut self, mut to: T) -> BoxedFuture<'a, error::Result<()>> + where + T: AsyncWrite + Unpin + Send + 'a, + Self: AsyncRead + Unpin + Send + Sized + 'a, + { + Box::pin(async move { + let mut buf = [0u8; 2048]; + + loop { + let n = self.read(&mut buf).await?; + + if n == 0 { + break Err(io::Error::from(io::ErrorKind::UnexpectedEof).into()); + } + + to.write_all(&buf).await?; + } + }) + } } +impl StreamExt for T {} + #[pin_project::pin_project] pub struct Read<'a, R> { reader: &'a mut R, @@ -62,6 +114,14 @@ pub struct Write<'a, W> { buf: &'a [u8], } +#[pin_project::pin_project] +pub struct WriteAll<'a, W> { + writer: &'a mut W, + #[pin] + buf: &'a [u8], + pos: usize, +} + #[pin_project::pin_project] pub struct Flush<'a, W> { writer: &'a mut W, @@ -101,6 +161,30 @@ where } } +impl<'a, W> std::future::Future for WriteAll<'a, W> +where + W: AsyncWrite + Unpin, +{ + type Output = error::Result<()>; + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + + while *this.pos < this.buf.len() { + match Pin::new(&mut **this.writer).poll_write(cx, &this.buf[*this.pos..])? { + Poll::Pending => return Poll::Pending, + Poll::Ready(0) => { + return Poll::Ready(Err(io::Error::from(io::ErrorKind::BrokenPipe).into())) + } + Poll::Ready(n) => { + *this.pos += n; + } + }; + } + + Poll::Ready(Ok(())) + } +} + impl AsyncWriteExt for T where T: AsyncWrite + Unpin {} impl AsyncReadExt for T where T: AsyncRead + Unpin {} @@ -134,4 +218,3 @@ where Pin::new(&mut **self).poll_read(cx, buf) } } - \ No newline at end of file diff --git a/src/core/mod.rs b/src/core/mod.rs index 78867e5..9253074 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -6,6 +6,7 @@ pub mod rpc; pub mod task; pub mod split; pub mod future; +pub mod filter; pub mod stream; pub mod accepter; pub mod handshake; diff --git a/src/core/split.rs b/src/core/split.rs index 0987db1..bdba123 100644 --- a/src/core/split.rs +++ b/src/core/split.rs @@ -2,7 +2,7 @@ use std::{pin::Pin, sync::Arc}; use parking_lot::Mutex; -use super::io::{AsyncRead, AsyncWrite}; +use super::io::{AsyncRead, AsyncWrite, StreamExt}; pub struct ReadHalf { reader: Arc>, @@ -30,6 +30,7 @@ pub trait SplitStream: AsyncWrite + AsyncRead { impl SplitStream for T where T: AsyncWrite + AsyncRead + Unpin {} + impl AsyncRead for ReadHalf where S: AsyncRead + Unpin, From 8d5b74f6d60695a027ed6b82b0dc649f990324e5 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Tue, 12 Mar 2024 16:33:15 +0000 Subject: [PATCH 03/29] =?UTF-8?q?feat:=20=E7=BC=96=E5=86=99=E7=AB=AF?= =?UTF-8?q?=E5=8F=A3=E8=BD=AC=E5=8F=91=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 61 +++++++ Cargo.toml | 1 + src/bin/server.rs | 40 +++-- src/channel/mod.rs | 1 - src/channel/remote.rs | 8 - src/core/accepter.rs | 2 + src/core/handshake.rs | 45 +---- src/core/io/mod.rs | 28 +++- src/core/mod.rs | 28 +++- src/core/processor.rs | 101 +++++++++++ src/core/protocol.rs | 239 +++++++++++++++++++++++++++ src/core/rpc/mod.rs | 13 +- src/core/rpc/structs.rs | 25 +++ src/core/split.rs | 2 +- src/error/mod.rs | 1 + src/lib.rs | 1 - src/server/handshake.rs | 25 +++ src/server/mod.rs | 5 + src/server/port_forward/handshake.rs | 50 ++++++ src/server/port_forward/mod.rs | 117 +++++++++++++ src/server/port_forward/transport.rs | 127 ++++++++++++++ src/service/mod.rs | 8 - 22 files changed, 838 insertions(+), 90 deletions(-) delete mode 100644 src/channel/mod.rs delete mode 100644 src/channel/remote.rs create mode 100644 src/core/processor.rs create mode 100644 src/core/protocol.rs create mode 100644 src/core/rpc/structs.rs create mode 100644 src/server/handshake.rs create mode 100644 src/server/port_forward/handshake.rs create mode 100644 src/server/port_forward/mod.rs create mode 100644 src/server/port_forward/transport.rs diff --git a/Cargo.lock b/Cargo.lock index e2bc7c5..c3ddf18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,6 +130,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[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 = "clap" version = "4.5.1" @@ -176,6 +186,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[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 = "env_filter" version = "0.1.0" @@ -217,6 +237,7 @@ dependencies = [ "log", "parking_lot", "pin-project", + "rc4", "serde", "tokio", "toml", @@ -322,6 +343,16 @@ dependencies = [ "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 = "gimli" version = "0.28.1" @@ -362,6 +393,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "kcp-rust" version = "0.1.2" @@ -512,6 +552,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rc4" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f1256e23efe6097f27aa82d6ca6889361c001586ae0f6917cbad072f05eb275" +dependencies = [ + "cipher", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -706,6 +755,12 @@ dependencies = [ "winnow", ] +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -718,6 +773,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 9ef5850..84b0d43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ cc = "1.0" [dependencies] +rc4 = "0.1.0" env_logger = "0.11.2" log = "0.4.16" pin-project = "1.1.4" diff --git a/src/bin/server.rs b/src/bin/server.rs index 04956e0..8c00df8 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -1,16 +1,18 @@ -use std::net::SocketAddr; +use std::{net::SocketAddr, str::FromStr}; use fuso::{ config::server::Config, core::{ accepter::{AccepterExt, MultiAccepter, StreamAccepter, TaggedAccepter}, - handshake::Handshake, - io::AsyncReadExt, - rpc::AsyncCaller, + handshake::Handshaker, + io::{AsyncReadExt, AsyncWriteExt, StreamExt}, + processor::{IProcessor, Processor, StreamProcessor}, + protocol::{AsyncPacketRead, AsyncPacketSend}, split::SplitStream, stream::fallback::Fallback, }, error, + server::port_forward::{PortForwarder, ShareAccepter}, }; #[derive(Debug, Clone)] @@ -81,27 +83,37 @@ async fn enter_fuso_main(conf: Config) -> error::Result<()> { } loop { - let (tag, (addr, s)) = accepter.accept().await?; + let (tag, (addr, transport)) = accepter.accept().await?; + + let a = StreamAccepter::new({ + fuso::core::net::TcpListener::bind_with_tokio(SocketAddr::from_str("0.0.0.0:0").unwrap()).await? + }); + + let mut forwarder = PortForwarder::new( + transport, + ShareAccepter::new(a, 1110, Vec::new())); + + let (c1, c2) = forwarder.accept().await?; - let mut a = s.do_handshake(&()).await?; - let mut buf = [0u8; 1024]; + // let a = processor.process(a).await; + + // let a = PortForward::new(a, ShareAccepter::new( + // accepter, + // // Rc4Recognizer(prefix, a) + // )); - let (mut reader, writer) = a.split(); + // let (from, to) = a.accept().await?; - let n = reader.read(&mut buf).await?; + // let n = reader.read(&mut buf).await?; // let (k, s) = a.into_inner(); // assert_eq!(k, Some(buf[..n].to_vec())); - println!("{:?}", String::from_utf8_lossy(&buf[..n])); - - - - + // println!("{:?}", String::from_utf8_lossy(&buf[..n])); println!("{:?} => {}", tag, addr); } diff --git a/src/channel/mod.rs b/src/channel/mod.rs deleted file mode 100644 index bdb9d62..0000000 --- a/src/channel/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod remote; \ No newline at end of file diff --git a/src/channel/remote.rs b/src/channel/remote.rs deleted file mode 100644 index bf9ccc7..0000000 --- a/src/channel/remote.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub struct RemoteChannel{ - -} - - -impl RemoteChannel{ - -} \ No newline at end of file diff --git a/src/core/accepter.rs b/src/core/accepter.rs index bb34f8f..fb1d559 100644 --- a/src/core/accepter.rs +++ b/src/core/accepter.rs @@ -168,6 +168,7 @@ where impl AccepterExt for A where A: Accepter {} + #[cfg(test)] mod tests { @@ -180,3 +181,4 @@ mod tests { let (a, b) = accepter.accept().await.unwrap(); } } + \ No newline at end of file diff --git a/src/core/handshake.rs b/src/core/handshake.rs index ea5a1d2..ae45319 100644 --- a/src/core/handshake.rs +++ b/src/core/handshake.rs @@ -1,46 +1,7 @@ -use std::pin::Pin; +use super::BoxedFuture; -use crate::{ - config::Crypto, - core::{ - io::AsyncReadExt, - stream::{compress, crypto}, - }, - error, -}; - -use super::{ - io::{AsyncRead, AsyncWrite}, - BoxedFuture, BoxedStream, -}; - -pub trait Handshake<'l> { - type C; - type Output; - fn do_handshake(self, conf: &'l Self::C) -> Self::Output; +pub trait Handshaker { + fn do_handshake<'a>(&'a mut self, stream: &'a mut T) -> BoxedFuture<'a, crate::error::Result<()>>; } -impl<'l, T> Handshake<'l> for T -where - T: AsyncWrite + AsyncRead + Unpin + Send + 'l, -{ - type C = (); - - type Output = BoxedFuture<'l, error::Result>>; - fn do_handshake(self, conf: &'l Self::C) -> Self::Output { - Box::pin(async move { - let stream = crypto::encrypt_stream( - compress::compress_stream( - self, - vec![crate::config::Compress::Lz4, crate::config::Compress::Lz4], - ) - .await?, - vec![crate::config::Crypto::Aes, crate::config::Crypto::Rsa], - ) - .await?; - - Ok(BoxedStream::new(stream)) - }) - } -} diff --git a/src/core/io/mod.rs b/src/core/io/mod.rs index 2afc52f..a03d366 100644 --- a/src/core/io/mod.rs +++ b/src/core/io/mod.rs @@ -185,10 +185,10 @@ where } } -impl AsyncWriteExt for T where T: AsyncWrite + Unpin {} - impl AsyncReadExt for T where T: AsyncRead + Unpin {} +impl AsyncWriteExt for T where T: AsyncWrite + Unpin {} + impl AsyncWrite for &mut T where T: AsyncWrite + Unpin, @@ -218,3 +218,27 @@ where Pin::new(&mut **self).poll_read(cx, buf) } } + +impl AsyncRead for std::io::Cursor> { + fn poll_read( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + Poll::Ready(Ok(std::io::Read::read(&mut *self, buf)?)) + } +} + +impl AsyncWrite for std::io::Cursor> { + fn poll_write( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Poll::Ready(Ok(std::io::Write::write(&mut *self, buf)?)) + } + + fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} diff --git a/src/core/mod.rs b/src/core/mod.rs index 9253074..094806e 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,16 +1,16 @@ -use std::{future::Future, pin::Pin}; +use std::{future::Future, net::SocketAddr, pin::Pin}; +pub mod accepter; +pub mod future; +pub mod handshake; pub mod io; pub mod net; +pub mod processor; +pub mod protocol; pub mod rpc; -pub mod task; pub mod split; -pub mod future; -pub mod filter; pub mod stream; -pub mod accepter; -pub mod handshake; - +pub mod task; pub type BoxedFuture<'a, O> = Pin + Send + 'a>>; @@ -22,6 +22,11 @@ pub trait Provider { pub trait Stream: io::AsyncRead + io::AsyncWrite {} +pub struct Connection<'a> { + addr: SocketAddr, + stream: BoxedStream<'a>, +} + pub struct BoxedStream<'a>(Box); impl<'a> BoxedStream<'a> { @@ -61,3 +66,12 @@ impl<'a> io::AsyncWrite for BoxedStream<'a> { Pin::new(&mut *self.0).poll_flush(cx) } } + +impl<'a> From<(SocketAddr, BoxedStream<'a>)> for Connection<'a> { + fn from(value: (SocketAddr, BoxedStream<'a>)) -> Self { + Self { + addr: value.0, + stream: value.1, + } + } +} diff --git a/src/core/processor.rs b/src/core/processor.rs new file mode 100644 index 0000000..8dd9f93 --- /dev/null +++ b/src/core/processor.rs @@ -0,0 +1,101 @@ +use std::sync::Arc; + +use super::{ + io::{AsyncRead, AsyncWrite}, + BoxedFuture, +}; + +pub enum Process { + Next(A), + Reject(A), + Resolve(R), +} + +pub trait Preprocessor { + type Output; + + fn prepare<'a>(&'a self, input: In) -> BoxedFuture<'a, Self::Output>; +} + +pub trait IProcessor { + fn process<'a>(&'a mut self, data: A) -> BoxedFuture<'a, Process>; +} + +pub trait StreamProcessor { + fn process<'a, P, R>(self, processor: &'a mut P) -> BoxedFuture<'a, Process> + where + Self: Sized + Send, + P: IProcessor + Send + 'a, + { + processor.process(self) + } +} + +impl<'a, T> StreamProcessor for T where T: AsyncRead + AsyncWrite {} + +pub struct BoxedProcessor<'a, A, R>(Box + Send + Unpin + 'a>); + +pub struct Processor<'a, A, R> { + processors: Vec>, +} + +pub struct BoxedPreprocessor<'a, In, Out>(Arc + Sync + Send + 'a>); + + +impl IProcessor for BoxedProcessor<'_, A, R> +where + A: Send, +{ + fn process<'a>(&'a mut self, data: A) -> BoxedFuture<'a, Process> { + self.0.process(data) + } +} + +impl IProcessor for Processor<'_, A, R> +where + A: Send, +{ + fn process<'a>(&'a mut self, data: A) -> BoxedFuture<'a, Process> { + Box::pin(async move { + let mut data = data; + for processor in self.processors.iter_mut() { + data = match processor.process(data).await { + Process::Next(data) => data, + Process::Reject(data) => data, + Process::Resolve(result) => return Process::Resolve(result), + } + } + + Process::Reject(data) + }) + } +} + +impl<'a, A, R> Processor<'a, A, R> { + pub fn new() -> Self { + Self { + processors: Default::default(), + } + } + + pub fn add

(&mut self, p: P) + where + P: IProcessor + Unpin + Send + 'a, + { + self.processors.push(BoxedProcessor(Box::new(p))) + } +} + + +impl Preprocessor for BoxedPreprocessor<'_, In, Out>{ + type Output = Out; + fn prepare<'a>(&'a self, input: In) -> BoxedFuture<'a, Self::Output> { + unimplemented!() + } +} + +impl Clone for BoxedPreprocessor<'_, In, Out>{ + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} \ No newline at end of file diff --git a/src/core/protocol.rs b/src/core/protocol.rs new file mode 100644 index 0000000..fa0d56f --- /dev/null +++ b/src/core/protocol.rs @@ -0,0 +1,239 @@ +use std::{ + io, + ops::{Deref, DerefMut}, + pin::Pin, + task::{Context, Poll}, +}; + +use crate::error::FusoError; + +use super::io::{AsyncRead, AsyncWrite}; + +const MAGIC: u32 = 0x6675736f; + +const F_MAGIC: u8 = 0x1; +const F_SIZE: u8 = 0x2; + +pub trait AsyncPacketRead: AsyncRead { + fn recv_packet<'a>(&'a mut self) -> RecvPacket<'a, Self> + where + Self: Sized, + { + RecvPacket { + flags: F_MAGIC | F_SIZE, + reader: self, + buffer: Buffer { + off: 0, + data: { + let mut buf = Vec::new(); + buf.resize(std::mem::size_of::(), 0); + buf + }, + }, + } + } +} + +pub trait AsyncPacketSend: AsyncWrite { + fn send_packet<'a>(&'a mut self, pkt: &'a [u8]) -> SendPacket<'a, Self> + where + Self: Sized, + { + SendPacket { + pos: 0, + writer: self, + buffer: pkt, + header: { + let mut h = [0u8; 8]; + (&mut h[..4]).copy_from_slice(&(MAGIC as u32).to_be_bytes()); + (&mut h[4..]).copy_from_slice(&(pkt.len() as u32).to_be_bytes()); + h + }, + } + } +} + +impl AsyncPacketRead for T where T: AsyncRead {} +impl AsyncPacketSend for T where T: AsyncWrite {} + +pub struct Buffer { + off: usize, + data: Vec, +} + +#[pin_project::pin_project] +pub struct RecvPacket<'a, R> { + #[pin] + reader: &'a mut R, + flags: u8, + buffer: Buffer, +} + +#[pin_project::pin_project] +pub struct SendPacket<'a, W> { + pos: usize, + #[pin] + writer: &'a mut W, + header: [u8; 8], + buffer: &'a [u8], +} + +impl Buffer { + fn take(&mut self) -> Vec { + std::mem::replace(&mut self.data, Default::default()) + } + + fn reset(&mut self, new: usize) { + self.off = 0; + self.data = unsafe { + let mut renew = Vec::with_capacity(new); + renew.set_len(new); + renew + } + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + mut reader: Pin<&mut R>, + ) -> Poll> + where + R: AsyncRead + Unpin, + { + while self.off < self.data.len() { + let off = self.off; + match Pin::new(&mut *reader).poll_read(cx, &mut self.data[off..])? { + Poll::Pending => break, + Poll::Ready(0) => { + return Poll::Ready(Err(io::Error::from(io::ErrorKind::UnexpectedEof).into())) + } + Poll::Ready(n) => { + self.off += n; + } + }; + } + + if self.off < self.data.len() { + Poll::Pending + } else { + Poll::Ready(Ok(())) + } + } +} + +impl Deref for Buffer { + type Target = [u8]; + fn deref(&self) -> &Self::Target { + self.data.deref() + } +} + +impl DerefMut for Buffer { + fn deref_mut(&mut self) -> &mut Self::Target { + self.data.deref_mut() + } +} + +impl std::future::Future for RecvPacket<'_, R> +where + R: AsyncRead + Unpin, +{ + type Output = crate::error::Result>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + loop { + match this.buffer.poll(cx, Pin::new(&mut *this.reader))? { + Poll::Pending => break Poll::Pending, + Poll::Ready(()) => { + if (*this.flags & F_MAGIC) == F_MAGIC || (*this.flags & F_SIZE) == F_SIZE { + let raw = [ + this.buffer[0], + this.buffer[1], + this.buffer[2], + this.buffer[3], + ]; + + let val = u32::from_be_bytes(raw); + let renew = { + if (*this.flags & F_MAGIC) == F_MAGIC { + if val != MAGIC { + return Poll::Ready(Err(FusoError::BadMagic)); + } else { + *this.flags &= !F_MAGIC; + 4 + } + } else { + *this.flags &= !F_SIZE; + val as usize + } + }; + + this.buffer.reset(renew); + } else { + break Poll::Ready(Ok(this.buffer.take())); + } + } + } + } + } +} + +impl std::future::Future for SendPacket<'_, W> +where + W: AsyncWrite + Unpin, +{ + type Output = crate::error::Result<()>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + + while *this.pos < this.buffer.len() + 8 { + let data = { + if *this.pos >= std::mem::size_of::() * 2 { + &this.buffer[*this.pos - 8..] + } else { + &this.header[*this.pos..] + } + }; + + match Pin::new(&mut *this.writer).poll_write(cx, data)? { + Poll::Pending => break, + Poll::Ready(0) => { + return Poll::Ready(Err(io::Error::from(io::ErrorKind::BrokenPipe).into())) + } + Poll::Ready(n) => { + *this.pos += n; + } + } + } + + if *this.pos < this.buffer.len() { + Poll::Pending + } else { + Poll::Ready(Ok(())) + } + } +} + +#[cfg(test)] +mod tests { + use crate::core::protocol::{AsyncPacketRead, AsyncPacketSend}; + + #[tokio::test] + async fn test_protocol() -> crate::error::Result<()> { + let mut cur = std::io::Cursor::new(Vec::::new()); + + let data = b"hello world"; + + cur.send_packet(data).await?; + + cur.set_position(0); + + let pkt = cur.recv_packet().await?; + + assert_eq!(&pkt, data); + + Ok(()) + } +} diff --git a/src/core/rpc/mod.rs b/src/core/rpc/mod.rs index ef2edfd..8472251 100644 --- a/src/core/rpc/mod.rs +++ b/src/core/rpc/mod.rs @@ -1,12 +1,13 @@ +pub mod structs; + use std::{pin::Pin, task::Poll}; use crate::error; -pub trait AsyncCaller { - fn poll_call(self: Pin<&mut Self>, raw_arg: &[u8]) -> Poll>>; -} +use super::BoxedFuture; +pub trait AsyncCall { + type Output; -pub struct Remote{ - -} \ No newline at end of file + fn call<'a>(&'a mut self, data: T) -> BoxedFuture<'a, Self::Output>; +} diff --git a/src/core/rpc/structs.rs b/src/core/rpc/structs.rs new file mode 100644 index 0000000..81bae6f --- /dev/null +++ b/src/core/rpc/structs.rs @@ -0,0 +1,25 @@ +pub mod port_forward { + use serde::{Deserialize, Serialize}; + + use crate::{config::client::ServerAddr, core::Connection}; + + #[derive(Debug, Serialize, Deserialize)] + pub enum Request { + New(u64, Option), + } + + #[derive(Debug, Serialize, Deserialize)] + pub enum Response { + Error(), + } + + pub enum WithSocks { + Tcp(ServerAddr), + Udp(), + } + + pub enum VisitorProtocol { + Socks(Connection<'static>, WithSocks), + Other(Connection<'static>, Option), + } +} diff --git a/src/core/split.rs b/src/core/split.rs index bdba123..e1e76dc 100644 --- a/src/core/split.rs +++ b/src/core/split.rs @@ -2,7 +2,7 @@ use std::{pin::Pin, sync::Arc}; use parking_lot::Mutex; -use super::io::{AsyncRead, AsyncWrite, StreamExt}; +use super::io::{AsyncRead, AsyncWrite}; pub struct ReadHalf { reader: Arc>, diff --git a/src/error/mod.rs b/src/error/mod.rs index 9e5153c..52a0111 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -3,6 +3,7 @@ pub type Result = std::result::Result; #[derive(Debug)] pub enum FusoError{ + BadMagic, TomlDeError(toml::de::Error), StdIo(std::io::Error), } diff --git a/src/lib.rs b/src/lib.rs index a3ee7e3..3dda2d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,6 @@ pub mod error; pub mod runtime; -pub mod channel; pub fn enter_async_main(fut: F) -> error::Result<()> where diff --git a/src/server/handshake.rs b/src/server/handshake.rs new file mode 100644 index 0000000..fa0303a --- /dev/null +++ b/src/server/handshake.rs @@ -0,0 +1,25 @@ +use crate::core::{processor::{IProcessor, Process}, BoxedFuture}; + +pub struct Handshake { + +} + + + +impl IProcessor for Handshake { + fn process<'a>( + &'a mut self, + data: A, + ) -> BoxedFuture<'a, Process> { + Box::pin(async move{ + + + + + + + + unimplemented!() + }) + } +} diff --git a/src/server/mod.rs b/src/server/mod.rs index e845c2b..5116479 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,3 +1,8 @@ +mod handshake; +pub mod port_forward; + + + pub trait Handshake { } \ No newline at end of file diff --git a/src/server/port_forward/handshake.rs b/src/server/port_forward/handshake.rs new file mode 100644 index 0000000..2e1db17 --- /dev/null +++ b/src/server/port_forward/handshake.rs @@ -0,0 +1,50 @@ +use crate::core::{ + handshake::Handshaker, + io::AsyncReadExt, + io::{AsyncRead, AsyncWrite}, +}; + +pub struct Rc4MagicHandshake { + pub expect: u32, + pub secret: Vec, +} + +impl Handshaker for Rc4MagicHandshake +where + T: AsyncRead + AsyncWrite + Send + Unpin, +{ + fn do_handshake<'a>( + &'a mut self, + stream: &'a mut T, + ) -> crate::core::BoxedFuture<'a, crate::error::Result<()>> { + Box::pin(async move { + let mut buf = [0u8; 1024]; + + Ok(()) + }) + } +} + +#[cfg(test)] +mod tests { + use rc4::{KeyInit, StreamCipher}; + + #[test] + fn test_rc4_magic_handshake() { + // let s = [u32;16]; + + // let mut a = rc4::Rc4::new_from_slice(&s).unwrap(); + // let mut b = b"11111".to_vec(); + + // println!("{:?}", b); + // let mut c = Vec::new(); + + // a.apply_keystream( &mut c); + + // println!("{:?}", b); + // let mut a = rc4::Rc4::new(b"hello world".into()); + // a.apply_keystream(&mut b); + + // println!("{:?}", b); + } +} diff --git a/src/server/port_forward/mod.rs b/src/server/port_forward/mod.rs new file mode 100644 index 0000000..7f590f1 --- /dev/null +++ b/src/server/port_forward/mod.rs @@ -0,0 +1,117 @@ +mod handshake; +mod transport; + +pub use handshake::*; +pub use transport::*; + +use parking_lot::Mutex; + +use std::{collections::HashMap, net::SocketAddr, pin::Pin, sync::Arc, task::Poll}; + +use crate::{ + core::{ + accepter::Accepter, + io::{AsyncRead, AsyncWrite}, + processor::{BoxedPreprocessor, Preprocessor}, + protocol::AsyncPacketSend, + rpc::{ + structs::port_forward::{Request, VisitorProtocol}, + AsyncCall, + }, + split::WriteHalf, + BoxedStream, + }, + error, +}; + +type Connection = crate::core::Connection<'static>; + +enum Forward { + Ready(u64, Connection), + Pending(u64), +} + +pub enum Whence { + Visitor(Connection), + Transport(Connection), +} + +#[derive(Clone)] +pub struct Visitors { + connections: Arc>>, +} + +pub struct PortForwarder { + accepter: A, + transport: T, + visitors: Visitors, + preprocessor: BoxedPreprocessor<'static, Connection, VisitorProtocol>, +} + + +impl Accepter for PortForwarder +where + A: Accepter + Unpin, + T: Unpin, +{ + type Output = (Connection, Connection); + + fn poll_accept( + mut self: std::pin::Pin<&mut Self>, + ctx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let poll = Pin::new(&mut self.accepter).poll_accept(ctx)?; + + match poll { + std::task::Poll::Pending => return Poll::Pending, + std::task::Poll::Ready(whence) => match whence { + Whence::Visitor(visitor) => { + // let preprocessor = self.preprocessor.clone(); + // let protocol = preprocessor.prepare(visitor).await; + + // match protocol { + // VisitorProtocol::Socks(conn, socks) => { + + // }, + // VisitorProtocol::Other(conn, _) => { + + // }, + // } + + // Self::new_transport(); + unimplemented!() + } + Whence::Transport(transport) => { + // Self::handshake_transport(transport); + unimplemented!() + } + }, + } + } +} + + + +impl PortForwarder +where + A: Accepter, + T: AsyncRead + AsyncWrite, +{ + pub fn new(transport: T, accepter: A) -> Self { + // Self { + // accepter, + // transport, + // } + unimplemented!() + } +} + +impl PortForwarder { + async fn new_transport(transport: Transport, token: u64) -> error::Result { + // let a = transport.call(Request::New(token, None)).await; + + // let a = transport.send_packet(NewTransport()).await; + + unimplemented!() + } +} diff --git a/src/server/port_forward/transport.rs b/src/server/port_forward/transport.rs new file mode 100644 index 0000000..93fa8c8 --- /dev/null +++ b/src/server/port_forward/transport.rs @@ -0,0 +1,127 @@ +use std::future::Future; +use std::net::SocketAddr; +use std::pin::Pin; +use std::sync::Arc; +use std::task::Poll; + +use super::{Connection, Rc4MagicHandshake, Whence}; +use crate::core::rpc::structs::port_forward::{Request, Response}; +use crate::core::rpc::AsyncCall; +use crate::core::BoxedFuture; +use crate::core::{accepter::Accepter, BoxedStream}; +use crate::error; + + + +pub struct Transport{ + +} + + + + +pub struct ShareAccepter { + accepter: A, + accepted: Vec, + handshaker: Arc, + connections: Vec>>, +} + +impl Accepter for ShareAccepter +where + A: Accepter)> + Unpin + 'static, +{ + type Output = Whence; + + fn poll_accept( + mut self: std::pin::Pin<&mut Self>, + ctx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let mut polled = false; + + loop { + polled = match self.accepted.pop() { + Some(whence) => break Poll::Ready(Ok(whence)), + None => match polled { + true => break Poll::Pending, + false => match Pin::new(&mut self.accepter).poll_accept(ctx)? { + std::task::Poll::Pending => true, + std::task::Poll::Ready(conn) => { + let handshaker = self.handshaker.clone(); + self.connections.push(Box::pin(Self::do_discern( + handshaker, + Connection::from(conn), + ))); + + false + } + }, + }, + }; + + let mut accepted = Vec::new(); + + self.connections + .retain_mut(|fut| match Pin::new(fut).poll(ctx) { + Poll::Pending => true, + Poll::Ready(r) => match r { + Ok(whence) => { + accepted.push(whence); + false + } + Err(e) => { + log::error!("{:?}", e); + false + } + }, + }); + + drop(std::mem::replace(&mut self.accepted, accepted)); + } + } +} + + + +impl ShareAccepter { + async fn do_discern( + handshaker: Arc, + connection: Connection, + ) -> error::Result { + + unimplemented!() + } +} + + + + +impl ShareAccepter +where + A: Accepter)> + Unpin + Send, +{ + pub fn new(accepter: A, magic: u32, secret: Vec) -> Self { + Self { + accepter, + accepted: Vec::new(), + connections: Default::default(), + handshaker: Arc::new(Rc4MagicHandshake { + expect: magic, + secret, + }), + } + } +} + + + +impl AsyncCall for Transport{ + type Output = error::Result; + + fn call<'a>(&'a mut self, data: Request) -> BoxedFuture<'a, Self::Output> { + Box::pin(async move{ + unimplemented!() + }) + } + +} \ No newline at end of file diff --git a/src/service/mod.rs b/src/service/mod.rs index fbee5bc..e69de29 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -1,8 +0,0 @@ -use std::net::IpAddr; - -pub struct PortForward{ - bind: IpAddr, - ports: Vec, -} - - From 6361a9f499f46e774fe3a7f22406a0fa9910be3c Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Thu, 14 Mar 2024 15:23:46 +0000 Subject: [PATCH 04/29] =?UTF-8?q?feat:=20=E7=BC=96=E5=86=99=E7=AB=AF?= =?UTF-8?q?=E5=8F=A3=E8=BD=AC=E5=8F=91=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 5 +- src/bin/server.rs | 26 +-- src/core/accepter.rs | 2 +- src/core/future.rs | 30 ++++ src/core/net/kcp.rs | 3 +- src/core/net/tcp.rs | 5 +- src/core/processor.rs | 4 +- src/core/rpc/structs.rs | 1 + src/runtime/mod.rs | 10 ++ src/runtime/tokio/kcp.rs | 5 +- src/runtime/tokio/mod.rs | 21 +++ src/runtime/tokio/port_forward/mod.rs | 36 ++++ src/runtime/tokio/tcp.rs | 4 +- src/server/port_forward/accepter.rs | 87 ++++++++++ src/server/port_forward/mod.rs | 221 ++++++++++++++++++------ src/server/port_forward/preprocessor.rs | 11 ++ src/server/port_forward/transport.rs | 140 +++------------ 17 files changed, 423 insertions(+), 188 deletions(-) create mode 100644 src/runtime/tokio/port_forward/mod.rs create mode 100644 src/server/port_forward/accepter.rs create mode 100644 src/server/port_forward/preprocessor.rs diff --git a/Cargo.toml b/Cargo.toml index 84b0d43..934c518 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,11 +33,13 @@ codegen-units = 1 panic = 'abort' [features] -default = ["fuso-config", "fuso-cli", "fuso-kcp"] +default = ["fuso-config", "fuso-cli", "fuso-kcp", "fuso-rt-tokio"] fuso-kcp = ["kcp-rust"] fuso-cli = ["clap"] fuso-serde = ["serde"] fuso-config = ["toml", "serde"] +fuso-runtime = [] +fuso-rt-tokio = ["dep:tokio", "fuso-runtime"] [build-dependencies] @@ -79,6 +81,7 @@ features = ["derive"] [dependencies.tokio] +optional = true version = "1.36.0" features = ["full"] diff --git a/src/bin/server.rs b/src/bin/server.rs index 8c00df8..9c2b353 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -6,6 +6,7 @@ use fuso::{ accepter::{AccepterExt, MultiAccepter, StreamAccepter, TaggedAccepter}, handshake::Handshaker, io::{AsyncReadExt, AsyncWriteExt, StreamExt}, + net::TcpListener, processor::{IProcessor, Processor, StreamProcessor}, protocol::{AsyncPacketRead, AsyncPacketSend}, split::SplitStream, @@ -45,11 +46,8 @@ async fn enter_fuso_main(conf: Config) -> error::Result<()> { accepter.add(TaggedAccepter::new( Tagged::Kcp, StreamAccepter::new({ - fuso::core::net::KcpListener::bind_with_tokio( - Default::default(), - kcp.as_socket_addr(), - ) - .await? + fuso::core::net::KcpListener::bind(Default::default(), kcp.as_socket_addr()) + .await? }), )); } @@ -57,7 +55,7 @@ async fn enter_fuso_main(conf: Config) -> error::Result<()> { accepter.add(TaggedAccepter::new( Tagged::Tcp, StreamAccepter::new({ - fuso::core::net::TcpListener::bind_with_tokio(tcp.as_socket_addr()).await? + fuso::core::net::TcpListener::bind(tcp.as_socket_addr()).await? }), )); } @@ -65,8 +63,7 @@ async fn enter_fuso_main(conf: Config) -> error::Result<()> { accepter.add(TaggedAccepter::new( Tagged::Tcp, StreamAccepter::new({ - fuso::core::net::TcpListener::bind_with_tokio(proxy.as_socket_addr()) - .await? + fuso::core::net::TcpListener::bind(proxy.as_socket_addr()).await? }), )); } @@ -74,8 +71,7 @@ async fn enter_fuso_main(conf: Config) -> error::Result<()> { accepter.add(TaggedAccepter::new( Tagged::Tcp, StreamAccepter::new({ - fuso::core::net::TcpListener::bind_with_tokio(forward.as_socket_addr()) - .await? + fuso::core::net::TcpListener::bind(forward.as_socket_addr()).await? }), )); } @@ -86,18 +82,14 @@ async fn enter_fuso_main(conf: Config) -> error::Result<()> { let (tag, (addr, transport)) = accepter.accept().await?; let a = StreamAccepter::new({ - fuso::core::net::TcpListener::bind_with_tokio(SocketAddr::from_str("0.0.0.0:0").unwrap()).await? + fuso::core::net::TcpListener::bind(SocketAddr::from_str("0.0.0.0:9999").unwrap()).await? }); - let mut forwarder = PortForwarder::new( - transport, - ShareAccepter::new(a, 1110, Vec::new())); + let mut forwarder = + PortForwarder::new(transport, ShareAccepter::new(a, 1110, Vec::new()), ()); let (c1, c2) = forwarder.accept().await?; - - - // let a = processor.process(a).await; // let a = PortForward::new(a, ShareAccepter::new( diff --git a/src/core/accepter.rs b/src/core/accepter.rs index fb1d559..cfd7ed2 100644 --- a/src/core/accepter.rs +++ b/src/core/accepter.rs @@ -178,7 +178,7 @@ mod tests { async fn test_accepter() { let mut accepter = MultiAccepter::<(i32, i32)>::new(); - let (a, b) = accepter.accept().await.unwrap(); + let _ = accepter.accept().await.unwrap(); } } \ No newline at end of file diff --git a/src/core/future.rs b/src/core/future.rs index 7d0575f..8e4aa9a 100644 --- a/src/core/future.rs +++ b/src/core/future.rs @@ -25,6 +25,8 @@ pub struct StoredFuture<'a, O>(Option + 'a>>>); pub struct Select<'a, O>(pub Vec>); +pub struct Poller<'a, O>(pub Vec>); + unsafe impl<'a, O> Send for StoredFuture<'a, O> {} unsafe impl<'a, O> Sync for StoredFuture<'a, O> {} @@ -49,6 +51,19 @@ impl<'a, O> StoredFuture<'a, O> { } } +impl<'a, O> Poller<'a, O> { + pub fn new() -> Self { + Self(Default::default()) + } + + pub fn add(&mut self, fut: F) + where + F: Future + Send + 'a, + { + self.0.push(Box::pin(fut)) + } +} + impl std::future::Future for Select<'_, O> { type Output = O; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { @@ -62,3 +77,18 @@ impl std::future::Future for Select<'_, O> { Poll::Pending } } + +impl std::future::Future for Poller<'_, O> { + type Output = O; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + for (idx, fut) in self.0.iter_mut().enumerate() { + if let Poll::Ready(o) = Pin::new(fut).poll(cx) { + self.0.remove(idx); + return Poll::Ready(o); + } + } + + Poll::Pending + } +} diff --git a/src/core/net/kcp.rs b/src/core/net/kcp.rs index 43807ea..69b6d2a 100644 --- a/src/core/net/kcp.rs +++ b/src/core/net/kcp.rs @@ -35,8 +35,9 @@ pub enum KcpStream { Server(kcp_rust::KcpStream), } +#[cfg(feature = "fuso-runtime")] impl KcpListener { - pub async fn bind(conf: kcp_rust::Config, addr: A) -> error::Result + pub async fn bind_with_provider(conf: kcp_rust::Config, addr: A) -> error::Result where P: KcpProvider, A: Into, diff --git a/src/core/net/tcp.rs b/src/core/net/tcp.rs index 53d6f7c..ce36e77 100644 --- a/src/core/net/tcp.rs +++ b/src/core/net/tcp.rs @@ -43,8 +43,10 @@ impl TcpStream { } } + +#[cfg(feature = "fuso-runtime")] impl TcpListener { - pub async fn bind(addr: A) -> error::Result + pub async fn bind_with_provider(addr: A) -> error::Result where P: TcpProvider, A: Into, @@ -55,6 +57,7 @@ impl TcpListener { } } + impl AsyncWrite for TcpStream { fn poll_write( mut self: std::pin::Pin<&mut Self>, diff --git a/src/core/processor.rs b/src/core/processor.rs index 8dd9f93..6c24df9 100644 --- a/src/core/processor.rs +++ b/src/core/processor.rs @@ -39,7 +39,7 @@ pub struct Processor<'a, A, R> { processors: Vec>, } -pub struct BoxedPreprocessor<'a, In, Out>(Arc + Sync + Send + 'a>); +pub struct BoxedPreprocessor<'a, In, Out>(pub(crate) Arc + Sync + Send + 'a>); impl IProcessor for BoxedProcessor<'_, A, R> @@ -98,4 +98,4 @@ impl Clone for BoxedPreprocessor<'_, In, Out>{ fn clone(&self) -> Self { Self(self.0.clone()) } -} \ No newline at end of file +} diff --git a/src/core/rpc/structs.rs b/src/core/rpc/structs.rs index 81bae6f..124bcb1 100644 --- a/src/core/rpc/structs.rs +++ b/src/core/rpc/structs.rs @@ -10,6 +10,7 @@ pub mod port_forward { #[derive(Debug, Serialize, Deserialize)] pub enum Response { + Ok, Error(), } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 9965980..4c3157c 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1,8 +1,18 @@ +use crate::{core::BoxedFuture, error}; + +#[cfg(feature = "fuso-rt-tokio")] pub mod tokio; + pub trait Runtime { fn spawn(fut: F) -> () where F: std::future::Future + Send + 'static, O: Send + 'static; + + fn wait_for(timeout: std::time::Duration, fut: F) -> BoxedFuture<'static, error::Result> + where + F: std::future::Future + Send + 'static; + + fn sleep(timeout: std::time::Duration) -> BoxedFuture<'static, ()>; } diff --git a/src/runtime/tokio/kcp.rs b/src/runtime/tokio/kcp.rs index 5d5d8bf..7cbed67 100644 --- a/src/runtime/tokio/kcp.rs +++ b/src/runtime/tokio/kcp.rs @@ -16,8 +16,10 @@ pub struct KcpWithTokioRuntime; pub struct TokioUdpSocket(Arc); + +#[cfg(feature = "fuso-rt-tokio")] impl KcpListener { - pub async fn bind_with_tokio(conf: kcp_rust::Config, addr: A) -> error::Result + pub async fn bind(conf: kcp_rust::Config, addr: A) -> error::Result where A: Into, { @@ -30,6 +32,7 @@ impl KcpListener { } } + impl crate::core::net::UdpProvider for KcpWithTokioRuntime { type Binder = Self; diff --git a/src/runtime/tokio/mod.rs b/src/runtime/tokio/mod.rs index a8cb550..d280158 100644 --- a/src/runtime/tokio/mod.rs +++ b/src/runtime/tokio/mod.rs @@ -1,9 +1,13 @@ mod kcp; mod tcp; +mod port_forward; + pub use kcp::*; pub use tcp::*; +pub use port_forward::*; + pub struct TokioRuntime; pub fn block_on(f: F) -> T @@ -25,4 +29,21 @@ impl super::Runtime for TokioRuntime { { tokio::spawn(fut); } + + fn wait_for( + timeout: std::time::Duration, + future: F, + ) -> crate::core::BoxedFuture<'static, crate::error::Result> + where + F: std::future::Future + Send + 'static, + { + Box::pin(async move { + let a = tokio::time::timeout(timeout, future).await; + unimplemented!() + }) + } + + fn sleep(timeout: std::time::Duration) -> crate::core::BoxedFuture<'static, ()> { + todo!() + } } diff --git a/src/runtime/tokio/port_forward/mod.rs b/src/runtime/tokio/port_forward/mod.rs new file mode 100644 index 0000000..044dcac --- /dev/null +++ b/src/runtime/tokio/port_forward/mod.rs @@ -0,0 +1,36 @@ +use std::net::SocketAddr; + +use crate::{ + core::{ + accepter::Accepter, + io::{AsyncRead, AsyncWrite}, + processor::Preprocessor, + rpc::structs::port_forward::VisitorProtocol, + BoxedStream, Connection, + }, + server::port_forward::{PortForwarder, ShareAccepter, Whence}, +}; + +use super::TokioRuntime; + +impl ShareAccepter +where + A: Accepter)> + Unpin + Send, +{ + pub fn new(accepter: A, magic: u32, secret: Vec) -> Self { + ShareAccepter::::new_runtime(accepter, magic, secret) + } +} + +impl PortForwarder +where + A: Accepter + Unpin + 'static, + T: AsyncRead + AsyncWrite + Unpin + 'static, +{ + pub fn new

(stream: T, accepter: A, preprocessor: P) -> Self + where + P: Preprocessor, Output = VisitorProtocol> + Send + Sync + 'static, + { + PortForwarder::::new_with_runtime(stream, accepter, preprocessor) + } +} diff --git a/src/runtime/tokio/tcp.rs b/src/runtime/tokio/tcp.rs index 0e7870a..1fc55e5 100644 --- a/src/runtime/tokio/tcp.rs +++ b/src/runtime/tokio/tcp.rs @@ -55,8 +55,10 @@ impl TcpStream { } } + +#[cfg(feature = "fuso-rt-tokio")] impl TcpListener { - pub async fn bind_with_tokio(addr: A) -> error::Result + pub async fn bind(addr: A) -> error::Result where A: tokio::net::ToSocketAddrs, { diff --git a/src/server/port_forward/accepter.rs b/src/server/port_forward/accepter.rs new file mode 100644 index 0000000..561528c --- /dev/null +++ b/src/server/port_forward/accepter.rs @@ -0,0 +1,87 @@ +use std::future::Future; +use std::marker::PhantomData; +use std::net::SocketAddr; +use std::pin::Pin; +use std::sync::Arc; +use std::task::Poll; + +use super::{Connection, Rc4MagicHandshake, Whence}; +use crate::core::future::Poller; +use crate::core::{accepter::Accepter, BoxedStream}; +use crate::error; +use crate::runtime::Runtime; + +pub struct ShareAccepter { + accepter: A, + handshaker: Arc, + connections: Poller<'static, error::Result>, + _marked: PhantomData, +} + +impl Accepter for ShareAccepter +where + A: Accepter)> + Unpin + 'static, + R: Runtime + Unpin + 'static, +{ + type Output = Whence; + + fn poll_accept( + mut self: std::pin::Pin<&mut Self>, + ctx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let mut polled = false; + + while !polled { + polled = match Pin::new(&mut self.accepter).poll_accept(ctx)? { + std::task::Poll::Pending => true, + std::task::Poll::Ready(conn) => { + let handshaker = self.handshaker.clone(); + let fut = R::wait_for( + std::time::Duration::from_secs(10), + Self::do_discern(handshaker, Connection::from(conn)), + ); + self.connections.add(async move { fut.await? }); + false + } + }; + + if let Poll::Ready(r) = Pin::new(&mut self.connections).poll(ctx) { + return Poll::Ready(r); + } + } + + Poll::Pending + } +} + + +impl ShareAccepter { + async fn do_discern( + handshaker: Arc, + connection: Connection, + ) -> error::Result { + + + + unimplemented!() + } +} + + +#[cfg(feature = "fuso-runtime")] +impl ShareAccepter +where + A: Accepter)> + Unpin + Send, +{ + pub fn new_runtime(accepter: A, magic: u32, secret: Vec) -> Self { + Self { + accepter, + connections: Poller::new(), + handshaker: Arc::new(Rc4MagicHandshake { + expect: magic, + secret, + }), + _marked: PhantomData, + } + } +} diff --git a/src/server/port_forward/mod.rs b/src/server/port_forward/mod.rs index 7f590f1..a018966 100644 --- a/src/server/port_forward/mod.rs +++ b/src/server/port_forward/mod.rs @@ -1,34 +1,42 @@ +mod accepter; mod handshake; +mod preprocessor; mod transport; +pub use accepter::*; pub use handshake::*; +pub use preprocessor::*; pub use transport::*; use parking_lot::Mutex; +use std::future::Future; -use std::{collections::HashMap, net::SocketAddr, pin::Pin, sync::Arc, task::Poll}; +use std::marker::PhantomData; +use std::{collections::HashMap, pin::Pin, sync::Arc, task::Poll}; +use crate::core::future::Poller; +use crate::core::rpc::structs::port_forward; +use crate::runtime::Runtime; use crate::{ core::{ accepter::Accepter, io::{AsyncRead, AsyncWrite}, processor::{BoxedPreprocessor, Preprocessor}, - protocol::AsyncPacketSend, rpc::{ structs::port_forward::{Request, VisitorProtocol}, AsyncCall, }, - split::WriteHalf, - BoxedStream, + split::SplitStream, }, error, }; type Connection = crate::core::Connection<'static>; -enum Forward { +enum Outcome { Ready(u64, Connection), Pending(u64), + Timeout(u64), } pub enum Whence { @@ -38,21 +46,24 @@ pub enum Whence { #[derive(Clone)] pub struct Visitors { + inc_token: Arc>, connections: Arc>>, } -pub struct PortForwarder { +pub struct PortForwarder { + poller: Poller<'static, error::Result>, accepter: A, - transport: T, visitors: Visitors, + transport: Transport, preprocessor: BoxedPreprocessor<'static, Connection, VisitorProtocol>, + _marked: PhantomData, } - -impl Accepter for PortForwarder +impl Accepter for PortForwarder where - A: Accepter + Unpin, - T: Unpin, + A: Accepter + Unpin + 'static, + T: Unpin + Send + 'static, + R: Runtime + Unpin + 'static, { type Output = (Connection, Connection); @@ -60,58 +71,170 @@ where mut self: std::pin::Pin<&mut Self>, ctx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - let poll = Pin::new(&mut self.accepter).poll_accept(ctx)?; - - match poll { - std::task::Poll::Pending => return Poll::Pending, - std::task::Poll::Ready(whence) => match whence { - Whence::Visitor(visitor) => { - // let preprocessor = self.preprocessor.clone(); - // let protocol = preprocessor.prepare(visitor).await; - - // match protocol { - // VisitorProtocol::Socks(conn, socks) => { - - // }, - // VisitorProtocol::Other(conn, _) => { - - // }, - // } - - // Self::new_transport(); - unimplemented!() + let mut polled = false; + + while !polled { + let whence = match Pin::new(&mut self.accepter).poll_accept(ctx)? { + std::task::Poll::Pending => None, + std::task::Poll::Ready(whence) => Some(whence), + }; + + polled = match whence { + None => true, + Some(Whence::Visitor(conn)) => { + let fut = Self::do_prepare_visitor( + conn, + self.visitors.clone(), + self.transport.clone(), + self.preprocessor.clone(), + ); + + self.poller.add(fut); + + false + } + Some(Whence::Transport(conn)) => { + let fut = Self::new_transport(conn, self.transport.clone()); + self.poller.add(fut); + + false } - Whence::Transport(transport) => { - // Self::handshake_transport(transport); - unimplemented!() + }; + + if let Poll::Ready(r) = Pin::new(&mut self.poller).poll(ctx) { + match r { + Err(_) => unimplemented!(), + Ok(forward) => match forward { + Outcome::Pending(token) => { + self.poller.add(Self::wait_transport( + token, + std::time::Duration::from_secs(10), + )); + } + Outcome::Timeout(token) => match self.visitors.take(token) { + Some(_) => {} + None => {} + }, + Outcome::Ready(token, transport) => { + match self.visitors.take(token) { + Some(visitor) => return Poll::Ready(Ok((visitor, transport))), + None => {} + }; + } + }, } - }, + } } + + Poll::Pending } } +#[cfg(feature = "fuso-runtime")] +impl PortForwarder +where + A: Accepter + Unpin + 'static, + T: AsyncRead + AsyncWrite + Unpin + 'static, +{ + pub fn new_with_runtime

(stream: T, accepter: A, preprocessor: P) -> Self + where + P: Preprocessor + Send + Sync + 'static, + { + let (reader, writer) = stream.split(); + Self { + poller: Poller::new(), + accepter, + visitors: Default::default(), + transport: Transport::new(reader, writer), + preprocessor: BoxedPreprocessor(Arc::new(preprocessor)), + _marked: PhantomData, + } + } +} -impl PortForwarder +impl PortForwarder where - A: Accepter, - T: AsyncRead + AsyncWrite, + R: Runtime, { - pub fn new(transport: T, accepter: A) -> Self { - // Self { - // accepter, - // transport, - // } - unimplemented!() + async fn wait_transport(token: u64, timeout: std::time::Duration) -> error::Result { + R::sleep(timeout).await; + Ok(Outcome::Timeout(token)) + } + + async fn do_prepare_visitor( + conn: Connection, + visitors: Visitors, + transport: Transport, + preprocessor: BoxedPreprocessor<'_, Connection, VisitorProtocol>, + ) -> error::Result { + let mut transport = transport; + + let (conn, addr) = match preprocessor.prepare(conn).await { + VisitorProtocol::Other(conn, addr) => (conn, addr), + VisitorProtocol::Socks(conn, socks) => match socks { + port_forward::WithSocks::Tcp(addr) => (conn, Some(addr)), + port_forward::WithSocks::Udp() => todo!(), + }, + }; + + let token = visitors.store(conn); + + match transport.call(Request::New(token, addr)).await { + Err(_) => {} + Ok(resp) => match resp { + port_forward::Response::Ok => {} + port_forward::Response::Error() => todo!(), + }, + } + + Ok(Outcome::Pending(token)) + } + + async fn new_transport(conn: Connection, transport: Transport) -> error::Result { + Ok(Outcome::Ready(0, conn)) } } -impl PortForwarder { - async fn new_transport(transport: Transport, token: u64) -> error::Result { - // let a = transport.call(Request::New(token, None)).await; - // let a = transport.send_packet(NewTransport()).await; +impl Default for Visitors { + fn default() -> Self { + Self { + inc_token: Arc::new(Mutex::new(1)), + connections: Default::default(), + } + } +} - unimplemented!() +impl Visitors { + fn take(&self, token: u64) -> Option { + self.connections.lock().remove(&token) + } + + fn store(&self, conn: Connection) -> u64 { + let token = self.next_token(); + self.connections.lock().insert(token, conn); + token + } + + fn next_token(&self) -> u64 { + let connections = self.connections.lock(); + let mut inc_token = self.inc_token.lock(); + + loop { + let cur_tok = *inc_token; + + let (cur_tok, overflow) = if connections.contains_key(&cur_tok) { + cur_tok.overflowing_add(1) + } else { + (cur_tok, false) + }; + + if overflow { + *inc_token = 1; + } else { + break cur_tok; + } + } } } diff --git a/src/server/port_forward/preprocessor.rs b/src/server/port_forward/preprocessor.rs new file mode 100644 index 0000000..07704c4 --- /dev/null +++ b/src/server/port_forward/preprocessor.rs @@ -0,0 +1,11 @@ +use crate::core::{processor::Preprocessor, rpc::structs::port_forward::VisitorProtocol}; + +use super::Connection; + +impl Preprocessor for () { + type Output = VisitorProtocol; + + fn prepare<'a>(&'a self, input: Connection) -> crate::core::BoxedFuture<'a, Self::Output> { + Box::pin(async move { VisitorProtocol::Other(input, None) }) + } +} diff --git a/src/server/port_forward/transport.rs b/src/server/port_forward/transport.rs index 93fa8c8..69c0da2 100644 --- a/src/server/port_forward/transport.rs +++ b/src/server/port_forward/transport.rs @@ -1,127 +1,39 @@ -use std::future::Future; -use std::net::SocketAddr; -use std::pin::Pin; -use std::sync::Arc; -use std::task::Poll; - -use super::{Connection, Rc4MagicHandshake, Whence}; -use crate::core::rpc::structs::port_forward::{Request, Response}; -use crate::core::rpc::AsyncCall; -use crate::core::BoxedFuture; -use crate::core::{accepter::Accepter, BoxedStream}; -use crate::error; - - - -pub struct Transport{ - -} - - - - -pub struct ShareAccepter { - accepter: A, - accepted: Vec, - handshaker: Arc, - connections: Vec>>, +use crate::{ + core::{ + rpc::{ + structs::port_forward::{Request, Response}, + AsyncCall, + }, + split::{ReadHalf, WriteHalf}, + BoxedFuture, + }, + error, +}; + +pub struct Transport { + reader: ReadHalf, + writer: WriteHalf, } -impl Accepter for ShareAccepter -where - A: Accepter)> + Unpin + 'static, -{ - type Output = Whence; - - fn poll_accept( - mut self: std::pin::Pin<&mut Self>, - ctx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - let mut polled = false; - - loop { - polled = match self.accepted.pop() { - Some(whence) => break Poll::Ready(Ok(whence)), - None => match polled { - true => break Poll::Pending, - false => match Pin::new(&mut self.accepter).poll_accept(ctx)? { - std::task::Poll::Pending => true, - std::task::Poll::Ready(conn) => { - let handshaker = self.handshaker.clone(); - self.connections.push(Box::pin(Self::do_discern( - handshaker, - Connection::from(conn), - ))); - - false - } - }, - }, - }; - - let mut accepted = Vec::new(); - - self.connections - .retain_mut(|fut| match Pin::new(fut).poll(ctx) { - Poll::Pending => true, - Poll::Ready(r) => match r { - Ok(whence) => { - accepted.push(whence); - false - } - Err(e) => { - log::error!("{:?}", e); - false - } - }, - }); - - drop(std::mem::replace(&mut self.accepted, accepted)); - } +impl Transport { + pub fn new(reader: ReadHalf, writer: WriteHalf) -> Self { + Self { reader, writer } } } +impl AsyncCall for Transport { + type Output = error::Result; - -impl ShareAccepter { - async fn do_discern( - handshaker: Arc, - connection: Connection, - ) -> error::Result { - - unimplemented!() + fn call<'a>(&'a mut self, data: Request) -> BoxedFuture<'a, Self::Output> { + Box::pin(async move { unimplemented!() }) } } - - - -impl ShareAccepter -where - A: Accepter)> + Unpin + Send, -{ - pub fn new(accepter: A, magic: u32, secret: Vec) -> Self { +impl Clone for Transport { + fn clone(&self) -> Self { Self { - accepter, - accepted: Vec::new(), - connections: Default::default(), - handshaker: Arc::new(Rc4MagicHandshake { - expect: magic, - secret, - }), + reader: self.reader.clone(), + writer: self.writer.clone(), } } } - - - -impl AsyncCall for Transport{ - type Output = error::Result; - - fn call<'a>(&'a mut self, data: Request) -> BoxedFuture<'a, Self::Output> { - Box::pin(async move{ - unimplemented!() - }) - } - -} \ No newline at end of file From d53278f82046c43746fdb460acaa8515506dcde2 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Fri, 15 Mar 2024 17:49:00 +0000 Subject: [PATCH 05/29] =?UTF-8?q?feat:=20=E7=BC=96=E5=86=99=E7=AB=AF?= =?UTF-8?q?=E5=8F=A3=E8=BD=AC=E5=8F=91=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 55 +++++++++++ Cargo.toml | 2 + src/bin/server.rs | 19 ++-- src/core/io/mod.rs | 48 +++++++++ src/core/mod.rs | 100 ++++++++++++++++++- src/core/processor.rs | 14 +-- src/error/mod.rs | 2 + src/runtime/mod.rs | 4 +- src/runtime/tokio/mod.rs | 19 ++-- src/runtime/tokio/port_forward/mod.rs | 16 +-- src/server/port_forward/accepter.rs | 58 ++++++++--- src/server/port_forward/handshake.rs | 48 +-------- src/server/port_forward/mod.rs | 125 ++++++++++++++++-------- src/server/port_forward/preprocessor.rs | 16 ++- src/server/port_forward/transport.rs | 14 +-- 15 files changed, 399 insertions(+), 141 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c3ddf18..66cba47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -237,6 +237,8 @@ dependencies = [ "log", "parking_lot", "pin-project", + "rand", + "random", "rc4", "serde", "tokio", @@ -353,6 +355,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "gimli" version = "0.28.1" @@ -534,6 +547,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.78" @@ -552,6 +571,42 @@ 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 = "random" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c7093a82705b48f10d4b165c6482dbe9f58cd63a870d85537e857b2badbd88" + [[package]] name = "rc4" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 934c518..f865541 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,8 @@ env_logger = "0.11.2" log = "0.4.16" pin-project = "1.1.4" parking_lot = "0.12.1" +random = "0.14.0" +rand = "0.8.5" [dependencies.kcp-rust] optional = true diff --git a/src/bin/server.rs b/src/bin/server.rs index 9c2b353..e3862ae 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -81,12 +81,19 @@ async fn enter_fuso_main(conf: Config) -> error::Result<()> { loop { let (tag, (addr, transport)) = accepter.accept().await?; - let a = StreamAccepter::new({ - fuso::core::net::TcpListener::bind(SocketAddr::from_str("0.0.0.0:9999").unwrap()).await? - }); - - let mut forwarder = - PortForwarder::new(transport, ShareAccepter::new(a, 1110, Vec::new()), ()); + let mut forwarder = PortForwarder::new( + transport, + ShareAccepter::new(1110, rand::random(), { + StreamAccepter::new({ + fuso::core::net::TcpListener::bind( + SocketAddr::from_str("0.0.0.0:9999").unwrap(), + ) + .await? + }) + }), + (), + None, + ); let (c1, c2) = forwarder.accept().await?; diff --git a/src/core/io/mod.rs b/src/core/io/mod.rs index a03d366..dc357d7 100644 --- a/src/core/io/mod.rs +++ b/src/core/io/mod.rs @@ -35,6 +35,17 @@ pub trait AsyncReadExt: AsyncRead + Unpin { { Read { reader: self, buf } } + + fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self> + where + Self: Sized, + { + ReadExact { + reader: self, + off: 0, + buf: buf, + } + } } pub trait AsyncWriteExt: AsyncWrite + Unpin { @@ -107,6 +118,14 @@ pub struct Read<'a, R> { buf: &'a mut [u8], } +#[pin_project::pin_project] +pub struct ReadExact<'a, R> { + #[pin] + buf: &'a mut [u8], + off: usize, + reader: &'a mut R, +} + #[pin_project::pin_project] pub struct Write<'a, W> { writer: &'a mut W, @@ -139,6 +158,35 @@ where } } +impl<'a, R> std::future::Future for ReadExact<'a, R> +where + R: AsyncRead + Unpin, +{ + type Output = error::Result<()>; + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + + while *this.off < this.buf.len() { + let rem = &mut this.buf[*this.off..]; + match Pin::new(&mut *this.reader).poll_read(cx, rem)? { + Poll::Pending => break, + Poll::Ready(0) => { + return Poll::Ready(Err(io::Error::from(io::ErrorKind::BrokenPipe).into())) + } + Poll::Ready(n) => { + *this.off += n; + } + }; + } + + if *this.off < this.buf.len() { + Poll::Pending + } else { + Poll::Ready(Ok(())) + } + } +} + impl<'a, W> std::future::Future for Write<'a, W> where W: AsyncWrite + Unpin, diff --git a/src/core/mod.rs b/src/core/mod.rs index 094806e..cb0bcdc 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,4 +1,12 @@ -use std::{future::Future, net::SocketAddr, pin::Pin}; +use std::{ + future::Future, + io::{Cursor, Read, Write}, + net::SocketAddr, + pin::Pin, + task::Poll, +}; + +use self::io::{AsyncRead, AsyncWrite}; pub mod accepter; pub mod future; @@ -25,6 +33,8 @@ pub trait Stream: io::AsyncRead + io::AsyncWrite {} pub struct Connection<'a> { addr: SocketAddr, stream: BoxedStream<'a>, + cursor: Option>>, + marked: Option>>, } pub struct BoxedStream<'a>(Box); @@ -72,6 +82,94 @@ impl<'a> From<(SocketAddr, BoxedStream<'a>)> for Connection<'a> { Self { addr: value.0, stream: value.1, + cursor: Default::default(), + marked: Default::default(), + } + } +} + +impl Connection<'_> { + + pub fn addr(&self) -> &SocketAddr{ + &self.addr + } + + pub fn mark(&mut self) { + match self.marked.take() { + None => drop(self.marked.replace(Default::default())), + Some(marked) => match self.cursor.as_mut() { + None => drop(self.cursor.replace(marked)), + Some(cursor) => { + let _ = cursor.write_all(&marked.into_inner()); + } + }, + } + } + + pub fn reset(&mut self) { + if let Some(mut marked) = self.marked.take() { + match self.cursor.take() { + None => { + marked.set_position(0); + drop(self.cursor.replace(marked)) + } + Some(cursor) => { + let pos = cursor.position(); + let buf = &cursor.into_inner()[pos as usize..]; + let mut cursor = Cursor::new(buf.to_vec()); + let _ = cursor.write_all(&marked.into_inner()); + self.cursor.replace(cursor); + } + } + } + } + + pub fn discard(&mut self) { + drop(self.cursor.take()); + drop(self.marked.take()); + } +} + +impl<'a> AsyncRead for Connection<'a> { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + if let Some(cursor) = self.cursor.as_mut() { + let n = cursor.read(buf)?; + if n > 0 { + return Poll::Ready(Ok(n)); + } } + + match Pin::new(&mut self.stream).poll_read(cx, buf)? { + Poll::Pending => Poll::Pending, + Poll::Ready(n) => { + if let Some(marked) = self.marked.as_mut() { + marked.write_all(&buf[..n])?; + } + + Poll::Ready(Ok(n)) + } + } + } +} + +impl<'a> AsyncWrite for Connection<'a> { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + self.discard(); + Pin::new(&mut self.stream).poll_write(cx, buf) + } + + fn poll_flush( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + Pin::new(&mut self.stream).poll_flush(cx) } } diff --git a/src/core/processor.rs b/src/core/processor.rs index 6c24df9..0a2f090 100644 --- a/src/core/processor.rs +++ b/src/core/processor.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{pin::Pin, sync::Arc}; use super::{ io::{AsyncRead, AsyncWrite}, @@ -39,8 +39,9 @@ pub struct Processor<'a, A, R> { processors: Vec>, } -pub struct BoxedPreprocessor<'a, In, Out>(pub(crate) Arc + Sync + Send + 'a>); - +pub struct WrappedPreprocessor<'a, In, Out>( + pub(crate) Arc + Sync + Send + 'a>, +); impl IProcessor for BoxedProcessor<'_, A, R> where @@ -86,15 +87,14 @@ impl<'a, A, R> Processor<'a, A, R> { } } - -impl Preprocessor for BoxedPreprocessor<'_, In, Out>{ +impl Preprocessor for WrappedPreprocessor<'_, In, Out> { type Output = Out; fn prepare<'a>(&'a self, input: In) -> BoxedFuture<'a, Self::Output> { - unimplemented!() + self.0.prepare(input) } } -impl Clone for BoxedPreprocessor<'_, In, Out>{ +impl Clone for WrappedPreprocessor<'_, In, Out> { fn clone(&self) -> Self { Self(self.0.clone()) } diff --git a/src/error/mod.rs b/src/error/mod.rs index 52a0111..edb818a 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -4,6 +4,8 @@ pub type Result = std::result::Result; #[derive(Debug)] pub enum FusoError{ BadMagic, + Timeout, + Abort, TomlDeError(toml::de::Error), StdIo(std::io::Error), } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 4c3157c..d272cf7 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -10,9 +10,9 @@ pub trait Runtime { F: std::future::Future + Send + 'static, O: Send + 'static; - fn wait_for(timeout: std::time::Duration, fut: F) -> BoxedFuture<'static, error::Result> + fn wait_for<'a, F, O>(timeout: std::time::Duration, fut: F) -> BoxedFuture<'a, error::Result> where - F: std::future::Future + Send + 'static; + F: std::future::Future + Send + 'a; fn sleep(timeout: std::time::Duration) -> BoxedFuture<'static, ()>; } diff --git a/src/runtime/tokio/mod.rs b/src/runtime/tokio/mod.rs index d280158..4e28d33 100644 --- a/src/runtime/tokio/mod.rs +++ b/src/runtime/tokio/mod.rs @@ -8,6 +8,8 @@ pub use tcp::*; pub use port_forward::*; +use crate::error; + pub struct TokioRuntime; pub fn block_on(f: F) -> T @@ -30,20 +32,23 @@ impl super::Runtime for TokioRuntime { tokio::spawn(fut); } - fn wait_for( + fn wait_for<'a, F, O>( timeout: std::time::Duration, future: F, - ) -> crate::core::BoxedFuture<'static, crate::error::Result> + ) -> crate::core::BoxedFuture<'a, crate::error::Result> where - F: std::future::Future + Send + 'static, + F: std::future::Future + Send + 'a, { Box::pin(async move { - let a = tokio::time::timeout(timeout, future).await; - unimplemented!() + tokio::time::timeout(timeout, future) + .await + .map_err(|_| error::FusoError::Timeout) }) } - + fn sleep(timeout: std::time::Duration) -> crate::core::BoxedFuture<'static, ()> { - todo!() + Box::pin(async move { + tokio::time::sleep(timeout).await; + }) } } diff --git a/src/runtime/tokio/port_forward/mod.rs b/src/runtime/tokio/port_forward/mod.rs index 044dcac..332333f 100644 --- a/src/runtime/tokio/port_forward/mod.rs +++ b/src/runtime/tokio/port_forward/mod.rs @@ -6,8 +6,9 @@ use crate::{ io::{AsyncRead, AsyncWrite}, processor::Preprocessor, rpc::structs::port_forward::VisitorProtocol, - BoxedStream, Connection, + BoxedStream, Connection, Stream, }, + error, server::port_forward::{PortForwarder, ShareAccepter, Whence}, }; @@ -17,7 +18,7 @@ impl ShareAccepter where A: Accepter)> + Unpin + Send, { - pub fn new(accepter: A, magic: u32, secret: Vec) -> Self { + pub fn new(magic: u32, secret: [u8; 16], accepter: A) -> Self { ShareAccepter::::new_runtime(accepter, magic, secret) } } @@ -25,12 +26,15 @@ where impl PortForwarder where A: Accepter + Unpin + 'static, - T: AsyncRead + AsyncWrite + Unpin + 'static, + T: Stream + Send + Unpin + 'static, { - pub fn new

(stream: T, accepter: A, preprocessor: P) -> Self + pub fn new(stream: T, accepter: A, prepvis: P, prepmap: M) -> Self where - P: Preprocessor, Output = VisitorProtocol> + Send + Sync + 'static, + P: Preprocessor, Output = error::Result>, + P: Send + Sync + 'static, + M: Preprocessor, Output = error::Result>>, + M: Send + Sync + 'static, { - PortForwarder::::new_with_runtime(stream, accepter, preprocessor) + PortForwarder::::new_with_runtime(stream, accepter, prepvis, prepmap) } } diff --git a/src/server/port_forward/accepter.rs b/src/server/port_forward/accepter.rs index 561528c..07eb0ab 100644 --- a/src/server/port_forward/accepter.rs +++ b/src/server/port_forward/accepter.rs @@ -5,8 +5,11 @@ use std::pin::Pin; use std::sync::Arc; use std::task::Poll; +use rc4::{KeyInit, Rc4, StreamCipher}; + use super::{Connection, Rc4MagicHandshake, Whence}; use crate::core::future::Poller; +use crate::core::io::AsyncReadExt; use crate::core::{accepter::Accepter, BoxedStream}; use crate::error; use crate::runtime::Runtime; @@ -36,11 +39,29 @@ where std::task::Poll::Pending => true, std::task::Poll::Ready(conn) => { let handshaker = self.handshaker.clone(); - let fut = R::wait_for( - std::time::Duration::from_secs(10), - Self::do_discern(handshaker, Connection::from(conn)), - ); - self.connections.add(async move { fut.await? }); + self.connections.add(async move { + let mut connection = Connection::from(conn); + + connection.mark(); + + let r = R::wait_for( + std::time::Duration::from_secs(10), + Self::try_handshake(handshaker, &mut connection), + ) + .await; + + match r { + Ok(Ok(true)) => { + connection.discard(); + Ok(Whence::Mapping(connection)) + } + _ => { + connection.reset(); + Ok(Whence::Visitor(connection)) + } + } + }); + false } }; @@ -54,32 +75,37 @@ where } } - -impl ShareAccepter { - async fn do_discern( +impl ShareAccepter +where + R: Runtime, +{ + async fn try_handshake( handshaker: Arc, - connection: Connection, - ) -> error::Result { - - + connection: &mut Connection, + ) -> error::Result { + let mut buf = [0u8; 4]; + + connection.read_exact(&mut buf).await?; - unimplemented!() + Rc4::new((&handshaker.secret).into()) + .try_apply_keystream(&mut buf) + .map(|()| handshaker.expect.eq(&u32::from_be_bytes(buf))) + .map_or_else(|_| Ok(false), |z| Ok(z)) } } - #[cfg(feature = "fuso-runtime")] impl ShareAccepter where A: Accepter)> + Unpin + Send, { - pub fn new_runtime(accepter: A, magic: u32, secret: Vec) -> Self { + pub fn new_runtime(accepter: A, magic: u32, secret: [u8; 16]) -> Self { Self { accepter, connections: Poller::new(), handshaker: Arc::new(Rc4MagicHandshake { - expect: magic, secret, + expect: magic, }), _marked: PhantomData, } diff --git a/src/server/port_forward/handshake.rs b/src/server/port_forward/handshake.rs index 2e1db17..09812b9 100644 --- a/src/server/port_forward/handshake.rs +++ b/src/server/port_forward/handshake.rs @@ -1,50 +1,4 @@ -use crate::core::{ - handshake::Handshaker, - io::AsyncReadExt, - io::{AsyncRead, AsyncWrite}, -}; - pub struct Rc4MagicHandshake { pub expect: u32, - pub secret: Vec, -} - -impl Handshaker for Rc4MagicHandshake -where - T: AsyncRead + AsyncWrite + Send + Unpin, -{ - fn do_handshake<'a>( - &'a mut self, - stream: &'a mut T, - ) -> crate::core::BoxedFuture<'a, crate::error::Result<()>> { - Box::pin(async move { - let mut buf = [0u8; 1024]; - - Ok(()) - }) - } -} - -#[cfg(test)] -mod tests { - use rc4::{KeyInit, StreamCipher}; - - #[test] - fn test_rc4_magic_handshake() { - // let s = [u32;16]; - - // let mut a = rc4::Rc4::new_from_slice(&s).unwrap(); - // let mut b = b"11111".to_vec(); - - // println!("{:?}", b); - // let mut c = Vec::new(); - - // a.apply_keystream( &mut c); - - // println!("{:?}", b); - // let mut a = rc4::Rc4::new(b"hello world".into()); - // a.apply_keystream(&mut b); - - // println!("{:?}", b); - } + pub secret: [u8; 16], } diff --git a/src/server/port_forward/mod.rs b/src/server/port_forward/mod.rs index a018966..bfcde62 100644 --- a/src/server/port_forward/mod.rs +++ b/src/server/port_forward/mod.rs @@ -15,13 +15,15 @@ use std::marker::PhantomData; use std::{collections::HashMap, pin::Pin, sync::Arc, task::Poll}; use crate::core::future::Poller; +use crate::core::io::AsyncReadExt; use crate::core::rpc::structs::port_forward; +use crate::core::Stream; use crate::runtime::Runtime; use crate::{ core::{ accepter::Accepter, io::{AsyncRead, AsyncWrite}, - processor::{BoxedPreprocessor, Preprocessor}, + processor::{Preprocessor, WrappedPreprocessor}, rpc::{ structs::port_forward::{Request, VisitorProtocol}, AsyncCall, @@ -37,11 +39,12 @@ enum Outcome { Ready(u64, Connection), Pending(u64), Timeout(u64), + Stopped(Option), } pub enum Whence { Visitor(Connection), - Transport(Connection), + Mapping(Connection), } #[derive(Clone)] @@ -55,14 +58,15 @@ pub struct PortForwarder { accepter: A, visitors: Visitors, transport: Transport, - preprocessor: BoxedPreprocessor<'static, Connection, VisitorProtocol>, + prepmap: WrappedPreprocessor<'static, Connection, error::Result>, + prepvis: WrappedPreprocessor<'static, Connection, error::Result>, _marked: PhantomData, } impl Accepter for PortForwarder where A: Accepter + Unpin + 'static, - T: Unpin + Send + 'static, + T: Stream + Unpin + Send + 'static, R: Runtime + Unpin + 'static, { type Output = (Connection, Connection); @@ -86,60 +90,81 @@ where conn, self.visitors.clone(), self.transport.clone(), - self.preprocessor.clone(), + self.prepvis.clone(), ); self.poller.add(fut); false } - Some(Whence::Transport(conn)) => { - let fut = Self::new_transport(conn, self.transport.clone()); + Some(Whence::Mapping(conn)) => { + let fut = Self::do_prepare_mapping( + conn, + self.transport.clone(), + self.prepmap.clone(), + ); + self.poller.add(fut); false } }; - if let Poll::Ready(r) = Pin::new(&mut self.poller).poll(ctx) { - match r { + polled = match Pin::new(&mut self.poller).poll(ctx) { + Poll::Pending => polled, + Poll::Ready(r) => match r { Err(_) => unimplemented!(), - Ok(forward) => match forward { - Outcome::Pending(token) => { - self.poller.add(Self::wait_transport( - token, - std::time::Duration::from_secs(10), - )); - } - Outcome::Timeout(token) => match self.visitors.take(token) { - Some(_) => {} - None => {} - }, - Outcome::Ready(token, transport) => { - match self.visitors.take(token) { - Some(visitor) => return Poll::Ready(Ok((visitor, transport))), - None => {} - }; + Ok(forward) => { + match forward { + Outcome::Pending(token) => { + self.poller.add(Self::wait_transport( + token, + std::time::Duration::from_secs(10), + )); + } + Outcome::Timeout(token) => { + if let Some(conn) = self.visitors.take(token) { + log::warn!("failed to create mapping {{ token={}, addr={}, msg='timeout' }}", token, conn.addr()); + } + } + Outcome::Ready(token, transport) => { + match self.visitors.take(token) { + Some(visitor) => return Poll::Ready(Ok((visitor, transport))), + None => log::warn!("invalid mapping because the client has been closed {{ token={token} }}") + }; + } + Outcome::Stopped(error) => { + return { + match error { + Some(e) => Poll::Ready(Err(e)), + None => Poll::Ready(Err(error::FusoError::Abort)), + } + } + } } - }, - } - } + + false + } + }, + }; } Poll::Pending } } - #[cfg(feature = "fuso-runtime")] impl PortForwarder where A: Accepter + Unpin + 'static, - T: AsyncRead + AsyncWrite + Unpin + 'static, + T: Stream + Unpin + Send + 'static, { - pub fn new_with_runtime

(stream: T, accepter: A, preprocessor: P) -> Self + pub fn new_with_runtime(stream: T, accepter: A, prepvis: P, prepmap: M) -> Self where - P: Preprocessor + Send + Sync + 'static, + P: Preprocessor>, + P: Send + Sync + 'static, + M: Preprocessor>, + M: Send + Sync + 'static, { let (reader, writer) = stream.split(); Self { @@ -147,7 +172,8 @@ where accepter, visitors: Default::default(), transport: Transport::new(reader, writer), - preprocessor: BoxedPreprocessor(Arc::new(preprocessor)), + prepvis: WrappedPreprocessor(Arc::new(prepvis)), + prepmap: WrappedPreprocessor(Arc::new(prepmap)), _marked: PhantomData, } } @@ -156,8 +182,10 @@ where impl PortForwarder where R: Runtime, + T: AsyncWrite + AsyncRead + Send + Unpin, { async fn wait_transport(token: u64, timeout: std::time::Duration) -> error::Result { + log::debug!("wait for mapping {{ token={token}, timeout={timeout:?} }}"); R::sleep(timeout).await; Ok(Outcome::Timeout(token)) } @@ -166,11 +194,11 @@ where conn: Connection, visitors: Visitors, transport: Transport, - preprocessor: BoxedPreprocessor<'_, Connection, VisitorProtocol>, + preprocessor: WrappedPreprocessor<'_, Connection, error::Result>, ) -> error::Result { let mut transport = transport; - let (conn, addr) = match preprocessor.prepare(conn).await { + let (conn, addr) = match preprocessor.prepare(conn).await? { VisitorProtocol::Other(conn, addr) => (conn, addr), VisitorProtocol::Socks(conn, socks) => match socks { port_forward::WithSocks::Tcp(addr) => (conn, Some(addr)), @@ -178,6 +206,8 @@ where }, }; + log::debug!("create mapping {} -- [T] -- {:?}", conn.addr(), addr); + let token = visitors.store(conn); match transport.call(Request::New(token, addr)).await { @@ -191,12 +221,25 @@ where Ok(Outcome::Pending(token)) } - async fn new_transport(conn: Connection, transport: Transport) -> error::Result { - Ok(Outcome::Ready(0, conn)) + async fn do_prepare_mapping( + conn: Connection, + _: Transport, + preprocessor: WrappedPreprocessor<'_, Connection, error::Result>, + ) -> error::Result { + let mut conn = preprocessor.prepare(conn).await?; + + let mut buf = [0u8; 8]; + + conn.read_exact(&mut buf).await?; + + let token = u64::from_be_bytes(buf); + + log::debug!("created mapping {{ token={token} }}"); + + Ok(Outcome::Ready(token, conn)) } } - impl Default for Visitors { fn default() -> Self { Self { @@ -213,7 +256,9 @@ impl Visitors { fn store(&self, conn: Connection) -> u64 { let token = self.next_token(); + self.connections.lock().insert(token, conn); + token } @@ -227,13 +272,13 @@ impl Visitors { let (cur_tok, overflow) = if connections.contains_key(&cur_tok) { cur_tok.overflowing_add(1) } else { - (cur_tok, false) + break cur_tok; }; if overflow { *inc_token = 1; } else { - break cur_tok; + *inc_token = cur_tok; } } } diff --git a/src/server/port_forward/preprocessor.rs b/src/server/port_forward/preprocessor.rs index 07704c4..ff90dc0 100644 --- a/src/server/port_forward/preprocessor.rs +++ b/src/server/port_forward/preprocessor.rs @@ -1,11 +1,21 @@ -use crate::core::{processor::Preprocessor, rpc::structs::port_forward::VisitorProtocol}; +use crate::{ + core::{processor::Preprocessor, rpc::structs::port_forward::VisitorProtocol}, + error, +}; use super::Connection; impl Preprocessor for () { - type Output = VisitorProtocol; + type Output = error::Result; fn prepare<'a>(&'a self, input: Connection) -> crate::core::BoxedFuture<'a, Self::Output> { - Box::pin(async move { VisitorProtocol::Other(input, None) }) + Box::pin(async move { Ok(VisitorProtocol::Other(input, None)) }) + } +} + +impl Preprocessor for Option<()> { + type Output = error::Result; + fn prepare<'a>(&'a self, input: Connection) -> crate::core::BoxedFuture<'a, Self::Output> { + Box::pin(async move { Ok(input) }) } } diff --git a/src/server/port_forward/transport.rs b/src/server/port_forward/transport.rs index 69c0da2..682f706 100644 --- a/src/server/port_forward/transport.rs +++ b/src/server/port_forward/transport.rs @@ -1,11 +1,9 @@ use crate::{ core::{ - rpc::{ + io::{AsyncRead, AsyncWrite, AsyncWriteExt}, rpc::{ structs::port_forward::{Request, Response}, AsyncCall, - }, - split::{ReadHalf, WriteHalf}, - BoxedFuture, + }, split::{ReadHalf, WriteHalf}, BoxedFuture }, error, }; @@ -21,11 +19,15 @@ impl Transport { } } -impl AsyncCall for Transport { +impl AsyncCall for Transport +where T: AsyncWrite + AsyncRead + Send + Unpin{ type Output = error::Result; fn call<'a>(&'a mut self, data: Request) -> BoxedFuture<'a, Self::Output> { - Box::pin(async move { unimplemented!() }) + Box::pin(async move { + self.writer.write(b"").await?; + Ok(Response::Ok) + }) } } From aba4ab0b75222547c30cc50f00331729b9f12006 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Mon, 18 Mar 2024 15:41:50 +0000 Subject: [PATCH 06/29] =?UTF-8?q?feat:=20=E7=BC=96=E5=86=99=E7=AB=AF?= =?UTF-8?q?=E5=8F=A3=E8=BD=AC=E5=8F=91=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 332 +++++++++++++++++++++++++- Cargo.toml | 9 +- src/bin/server.rs | 121 +++++++--- src/config/mod.rs | 19 ++ src/core/future.rs | 13 + src/core/mod.rs | 1 + src/core/rpc/caller.rs | 183 ++++++++++++++ src/core/rpc/channel.rs | 136 +++++++++++ src/core/rpc/mod.rs | 31 +++ src/core/rpc/structs.rs | 2 + src/core/task/mod.rs | 49 +++- src/core/token.rs | 37 +++ src/error/mod.rs | 1 + src/runtime/tokio/port_forward/mod.rs | 7 +- src/server/manager/mod.rs | 42 ++++ src/server/mod.rs | 14 +- src/server/port_forward/accepter.rs | 8 +- src/server/port_forward/mod.rs | 114 ++++----- src/server/port_forward/transport.rs | 62 +++-- src/server/proxy_tunnel/mod.rs | 19 ++ src/service/mod.rs | 1 + 21 files changed, 1070 insertions(+), 131 deletions(-) create mode 100644 src/core/rpc/caller.rs create mode 100644 src/core/rpc/channel.rs create mode 100644 src/core/token.rs create mode 100644 src/server/manager/mod.rs create mode 100644 src/server/proxy_tunnel/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 66cba47..6841b49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,6 +91,61 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "axum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.69" @@ -106,6 +161,15 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -225,10 +289,27 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[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 = "fuso" version = "1.0.5-beta" dependencies = [ + "axum", + "bincode", "cc", "clap", "env_logger", @@ -238,7 +319,6 @@ dependencies = [ "parking_lot", "pin-project", "rand", - "random", "rc4", "serde", "tokio", @@ -372,6 +452,25 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "h2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51ee2dd2e4f378392eeff5d51618cd9a63166a2513846bbc55f21cfacd9199d4" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.14.3" @@ -390,12 +489,94 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "hyper" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", +] + [[package]] name = "indexmap" version = "2.2.3" @@ -415,6 +596,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + [[package]] name = "kcp-rust" version = "0.1.2" @@ -447,12 +634,24 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "miniz_oxide" version = "0.7.2" @@ -492,6 +691,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + [[package]] name = "parking_lot" version = "0.12.1" @@ -515,6 +720,12 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pin-project" version = "1.1.4" @@ -601,12 +812,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "random" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c7093a82705b48f10d4b165c6482dbe9f58cd63a870d85537e857b2badbd88" - [[package]] name = "rc4" version = "0.1.0" @@ -660,6 +865,18 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + [[package]] name = "scopeguard" version = "1.2.0" @@ -686,6 +903,27 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_spanned" version = "0.6.5" @@ -695,6 +933,18 @@ 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 = "signal-hook-registry" version = "1.4.1" @@ -746,6 +996,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "tokio" version = "1.36.0" @@ -776,6 +1032,20 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + [[package]] name = "toml" version = "0.8.10" @@ -810,6 +1080,54 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + [[package]] name = "typenum" version = "1.17.0" diff --git a/Cargo.toml b/Cargo.toml index f865541..f599656 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,13 +33,14 @@ codegen-units = 1 panic = 'abort' [features] -default = ["fuso-config", "fuso-cli", "fuso-kcp", "fuso-rt-tokio"] +default = ["fuso-config", "fuso-cli", "fuso-kcp", "fuso-rt-tokio", "fuso-manager"] fuso-kcp = ["kcp-rust"] fuso-cli = ["clap"] fuso-serde = ["serde"] fuso-config = ["toml", "serde"] fuso-runtime = [] fuso-rt-tokio = ["dep:tokio", "fuso-runtime"] +fuso-manager = [ "dep:axum", "fuso-rt-tokio"] [build-dependencies] @@ -52,7 +53,7 @@ env_logger = "0.11.2" log = "0.4.16" pin-project = "1.1.4" parking_lot = "0.12.1" -random = "0.14.0" +bincode = "1.3.3" rand = "0.8.5" [dependencies.kcp-rust] @@ -66,6 +67,10 @@ optional = true version = "*" git = "https://github.com/editso/fuso-socks5" +[dependencies.axum] +optional = true +version = "0.7.4" + [dependencies.toml] optional = true version = "0.8.10" diff --git a/src/bin/server.rs b/src/bin/server.rs index e3862ae..ae9e30d 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -1,9 +1,10 @@ use std::{net::SocketAddr, str::FromStr}; use fuso::{ - config::server::Config, + config::{server::Config, Stateful}, core::{ accepter::{AccepterExt, MultiAccepter, StreamAccepter, TaggedAccepter}, + future::Select, handshake::Handshaker, io::{AsyncReadExt, AsyncWriteExt, StreamExt}, net::TcpListener, @@ -13,7 +14,10 @@ use fuso::{ stream::fallback::Fallback, }, error, - server::port_forward::{PortForwarder, ShareAccepter}, + server::{ + manager::{Manager, MultiServiceManager}, + port_forward::{MuxAccepter, PortForwarder}, + }, }; #[derive(Debug, Clone)] @@ -38,9 +42,78 @@ async fn enter_fuso_main(conf: Config) -> error::Result<()> { .filter_level(log::LevelFilter::Debug) .init(); + enter_fuso_serve(Stateful{ + conf + }).await?; + // axum::serve(tcp_listener, make_service) + + loop { + + // mgr.manage(|ctrl|async move{ + + // }); + + // mgr.service(|mgr| async move { + // enter_port_forward_service().await + // }); + + // let a = processor.process(a).await; + + // let a = PortForward::new(a, ShareAccepter::new( + // accepter, + // // Rc4Recognizer(prefix, a) + // )); + + // let (from, to) = a.accept().await?; + + // let n = reader.read(&mut buf).await?; + + // let (k, s) = a.into_inner(); + + // assert_eq!(k, Some(buf[..n].to_vec())); + + // println!("{:?}", String::from_utf8_lossy(&buf[..n])); + + // println!("{:?} => {}", tag, addr); + } + + Ok(()) +} + +// pub async fn enter_port_forward_service(mgr){ +// let (tag, (addr, transport)) = accepter.accept().await?; + +// let mut forwarder = PortForwarder::new( +// transport, +// { +// ShareAccepter::new(1110, rand::random(), { +// StreamAccepter::new({ +// fuso::core::net::TcpListener::bind( +// SocketAddr::from_str("0.0.0.0:9999").unwrap(), +// ) +// .await? +// }) +// }) +// }, +// (), +// None, +// ); + +// let mgr = 0; + +// let (c1, c2) = forwarder.accept().await?; + +// mgr.spawn(PortForward(), async move{ +// c1.copy(c2); +// }); + +// unimplemented!() +// } + +async fn enter_fuso_serve(conf: Stateful) -> error::Result<()> { let mut accepter = MultiAccepter::new(); - for listen in conf.listens { + for listen in &conf.listens { match listen { fuso::config::server::Listen::Kcp(kcp) => { accepter.add(TaggedAccepter::new( @@ -79,43 +152,29 @@ async fn enter_fuso_main(conf: Config) -> error::Result<()> { } loop { - let (tag, (addr, transport)) = accepter.accept().await?; - + let (addr, (_, transport)) = accepter.accept().await?; let mut forwarder = PortForwarder::new( transport, - ShareAccepter::new(1110, rand::random(), { - StreamAccepter::new({ - fuso::core::net::TcpListener::bind( - SocketAddr::from_str("0.0.0.0:9999").unwrap(), - ) - .await? + { + MuxAccepter::new(1110, rand::random(), { + StreamAccepter::new({ + fuso::core::net::TcpListener::bind( + SocketAddr::from_str("0.0.0.0:9999").unwrap(), + ) + .await? + }) }) - }), + }, (), None, ); - let (c1, c2) = forwarder.accept().await?; - - // let a = processor.process(a).await; - - // let a = PortForward::new(a, ShareAccepter::new( - // accepter, - // // Rc4Recognizer(prefix, a) - // )); + let mgr = 0; - // let (from, to) = a.accept().await?; - - // let n = reader.read(&mut buf).await?; - - // let (k, s) = a.into_inner(); - - // assert_eq!(k, Some(buf[..n].to_vec())); - - // println!("{:?}", String::from_utf8_lossy(&buf[..n])); - - println!("{:?} => {}", tag, addr); + let (c1, c2) = forwarder.accept().await?; } + let s = fuso::server::Serve {}; + Ok(()) } diff --git a/src/config/mod.rs b/src/config/mod.rs index 0aa9516..6eed130 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,3 +1,5 @@ +use std::ops::Deref; + use serde::{Deserialize, Serialize}; pub mod client; @@ -55,3 +57,20 @@ impl Default for BootKind { Self::Default } } + + +pub struct Stateful{ + pub conf: C +} + +impl Stateful{ + +} + +impl Deref for Stateful{ + type Target = C; + + fn deref(&self) -> &Self::Target { + &self.conf + } +} \ No newline at end of file diff --git a/src/core/future.rs b/src/core/future.rs index 8e4aa9a..c5cb1f2 100644 --- a/src/core/future.rs +++ b/src/core/future.rs @@ -64,6 +64,19 @@ impl<'a, O> Poller<'a, O> { } } +impl<'a, O> Select<'a, O> { + pub fn new() -> Self { + Self(Default::default()) + } + + pub fn add(&mut self, fut: F) + where + F: std::future::Future + Send + 'a, + { + self.0.push(Box::pin(fut)) + } +} + impl std::future::Future for Select<'_, O> { type Output = O; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/src/core/mod.rs b/src/core/mod.rs index cb0bcdc..f12426b 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -19,6 +19,7 @@ pub mod rpc; pub mod split; pub mod stream; pub mod task; +pub mod token; pub type BoxedFuture<'a, O> = Pin + Send + 'a>>; diff --git a/src/core/rpc/caller.rs b/src/core/rpc/caller.rs new file mode 100644 index 0000000..118cbb1 --- /dev/null +++ b/src/core/rpc/caller.rs @@ -0,0 +1,183 @@ +use std::{collections::HashMap, marker::PhantomData, pin::Pin, sync::Arc}; + +use parking_lot::Mutex; +use serde::{Deserialize, Serialize}; + +use crate::{ + core::{ + future::Select, + protocol::{AsyncPacketRead, AsyncPacketSend}, + rpc::channel, + split::{ReadHalf, WriteHalf}, + task::{setter, Setter}, + token::IncToken, + Stream, + }, + error, +}; + +use super::{ + channel::{Receiver, Sender}, + AsyncCall, Decoder, +}; +use crate::core::rpc::Encoder; +use crate::core::split::SplitStream; + +#[derive(Debug, Serialize, Deserialize)] +struct Request { + token: u64, + data: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +struct Response { + token: u64, + data: Vec, +} + +pub struct Reactor<'a>(Select<'a, error::Result<()>>); + +#[derive(Default, Clone)] +pub struct Calls { + call_list: Arc>>>>, + inc_token: IncToken, +} + +pub struct Caller<'a, S> { + calls: Calls, + request: Sender, + marked: PhantomData<(&'a (), S)>, +} + +impl<'a, S> Caller<'a, S> +where + S: Stream + Send + Unpin + 'a, +{ + pub fn new(stream: S) -> (Reactor<'a>, Self) { + let (reader, writer) = stream.split(); + let (req_rx, req_ax) = channel::open::(); + + let calls = Calls::default(); + let mut select = Select::new(); + + select.add(Reactor::run_recv_looper(calls.clone(), writer, req_ax)); + select.add(Reactor::run_send_looper(calls.clone(), reader)); + + ( + Reactor(select), + Self { + calls, + request: req_rx, + marked: PhantomData, + }, + ) + } +} + +impl<'caller, S, T> AsyncCall for Caller<'caller, S> +where + T: serde::Serialize + Send + 'static, + S: Send + Unpin + 'caller, +{ + type Output = error::Result>; + + fn call<'a>(&'a mut self, data: T) -> crate::core::BoxedFuture<'a, Self::Output> { + let data = data.encode(); + + Box::pin(async move { + let data = data?; + + let (setter, getter) = setter(); + + let token = self.calls.add(setter); + + match self.request.send(Request { token, data }).await { + Ok(()) => getter.await, + Err(e) => { + self.calls.cancel(token); + Err(e) + } + } + }) + } +} + +impl<'a> Reactor<'a> { + async fn run_recv_looper( + calls: Calls, + writer: WriteHalf, + receiver: Receiver, + ) -> error::Result<()> + where + S: Stream + Unpin, + { + let mut writer = writer; + loop { + let pkt = receiver.recv().await?.encode()?; + if let Err(_) = writer.send_packet(&pkt).await { + calls.cancel_all(); + }; + } + } + + async fn run_send_looper(calls: Calls, reader: ReadHalf) -> error::Result<()> + where + S: Stream + Unpin, + { + let mut reader = reader; + loop { + let data = reader.recv_packet().await?; + + if let Err(e) = calls.wake(data.decode()?) { + log::warn!("{:?}", e); + } + } + } +} + +impl<'a> std::future::Future for Reactor<'a> { + type Output = error::Result<()>; + fn poll( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + Pin::new(&mut self.0).poll(cx) + } +} + +impl Calls { + fn add(&self, setter: Setter>) -> u64 { + let mut calls = self.call_list.lock(); + + let token = self.inc_token.next(|token| !calls.contains_key(&token)); + + calls.insert(token, setter); + + token + } + + fn wake(&self, Response { token, data }: Response) -> error::Result<()> { + match self.call_list.lock().remove(&token) { + None => Err(error::FusoError::BadRpcCall(token)), + Some(setter) => setter.set(data), + } + } + + fn cancel(&self, token: u64) { + drop(self.call_list.lock().remove(&token)) + } + + fn cancel_all(&self) { + self.call_list.lock().clear(); + } +} + +impl Clone for Caller<'_, T> { + fn clone(&self) -> Self { + Caller { + calls: self.calls.clone(), + request: self.request.clone(), + marked: PhantomData, + } + } +} diff --git a/src/core/rpc/channel.rs b/src/core/rpc/channel.rs new file mode 100644 index 0000000..f49db90 --- /dev/null +++ b/src/core/rpc/channel.rs @@ -0,0 +1,136 @@ +use std::{ + collections::VecDeque, + sync::Arc, + task::{Poll, Waker}, +}; + +use parking_lot::Mutex; + +use crate::error; + +struct Container { + buffer: Arc>>, + waker: Arc>>, +} + +pub struct Sender { + container: Container, +} + +pub struct Receiver { + container: Container, +} + +pub struct Recv<'a, T> { + receiver: &'a Container, +} + +pub struct Send<'a, T> { + data: Option, + sender: &'a Container, +} + +impl Receiver { + pub fn recv<'a>(&'a self) -> Recv<'a, T> { + Recv { + receiver: &self.container, + } + } +} + +impl Sender { + pub fn send<'a>(&'a self, data: T) -> Send<'a, T> { + Send { + sender: &self.container, + data: Some(data), + } + } +} + +impl Clone for Sender { + fn clone(&self) -> Self { + Self { + container: self.container.clone(), + } + } +} + +impl Default for Container { + fn default() -> Self { + Self { + buffer: Default::default(), + waker: Default::default(), + } + } +} + +impl Clone for Container { + fn clone(&self) -> Self { + Self { + buffer: self.buffer.clone(), + waker: Default::default(), + } + } +} + +pub fn open() -> (Sender, Receiver) { + let container = Container::default(); + + ( + Sender { + container: container.clone(), + }, + Receiver { + container: container, + }, + ) +} + +impl<'a, T> std::future::Future for Send<'a, T> +where + T: Unpin, +{ + type Output = error::Result<()>; + + fn poll( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + let data = self.data.take().expect("invalid state"); + self.sender.push(data); + Poll::Ready(Ok(())) + } +} + +impl<'a, T> std::future::Future for Recv<'a, T> { + type Output = error::Result; + + fn poll( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + match self.receiver.take(cx.waker()) { + None => Poll::Pending, + Some(data) => Poll::Ready(Ok(data)), + } + } +} + +impl Container { + fn push(&self, data: T) { + self.buffer.lock().push_back(data); + if let Some(waker) = self.waker.lock().take() { + waker.wake(); + } + } + + fn take(&self, waker: &Waker) -> Option { + match self.buffer.lock().pop_front() { + Some(data) => Some(data), + None => { + self.waker.lock().replace(waker.clone()); + None + } + } + } +} diff --git a/src/core/rpc/mod.rs b/src/core/rpc/mod.rs index 8472251..df318e1 100644 --- a/src/core/rpc/mod.rs +++ b/src/core/rpc/mod.rs @@ -1,3 +1,5 @@ +pub mod caller; +pub mod channel; pub mod structs; use std::{pin::Pin, task::Poll}; @@ -11,3 +13,32 @@ pub trait AsyncCall { fn call<'a>(&'a mut self, data: T) -> BoxedFuture<'a, Self::Output>; } + +pub trait Encoder { + fn encode(self) -> error::Result>; +} + +pub trait Decoder { + type Output; + fn decode(self) -> error::Result; +} + +impl Encoder for T +where + T: serde::Serialize, +{ + fn encode(self) -> error::Result> { + Ok(bincode::serialize(&self).unwrap()) + } +} + +impl Decoder for Vec +where + T: serde::de::DeserializeOwned, +{ + type Output = T; + + fn decode(self) -> error::Result { + unimplemented!() + } +} diff --git a/src/core/rpc/structs.rs b/src/core/rpc/structs.rs index 124bcb1..64f1b3c 100644 --- a/src/core/rpc/structs.rs +++ b/src/core/rpc/structs.rs @@ -5,6 +5,8 @@ pub mod port_forward { #[derive(Debug, Serialize, Deserialize)] pub enum Request { + Ping, + Pong, New(u64, Option), } diff --git a/src/core/task/mod.rs b/src/core/task/mod.rs index ae9dcde..a2f0986 100644 --- a/src/core/task/mod.rs +++ b/src/core/task/mod.rs @@ -1,3 +1,50 @@ +use std::{sync::Arc, task::Waker}; + +use parking_lot::Mutex; + +use crate::error; + mod pool; -pub use pool::*; \ No newline at end of file +pub enum ValState { + Ok(V), + Nil, + Invalid +} + +pub struct Getter { + val: Arc>>>, + waker: Arc>, +} + +pub struct Setter { + val: Arc>>>, + waker: Arc>, +} + +impl Setter { + pub fn set(self, val: V) -> error::Result<()> { + unimplemented!() + } + + pub fn invalid(self) {} +} + +impl std::future::Future for Getter { + type Output = error::Result; + fn poll( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + let val = self.val.lock(); + + + + unimplemented!() + } +} + + +pub fn setter() -> (Setter, Getter) { + unimplemented!() +} diff --git a/src/core/token.rs b/src/core/token.rs new file mode 100644 index 0000000..81a1ad1 --- /dev/null +++ b/src/core/token.rs @@ -0,0 +1,37 @@ +use std::sync::Arc; + +use parking_lot::Mutex; + +#[derive(Debug, Clone)] +pub struct IncToken(Arc>); + +impl Default for IncToken { + fn default() -> Self { + Self(Arc::new(Mutex::new(1))) + } +} + +impl IncToken { + pub fn next(&self, f: F) -> u64 + where + F: Fn(u64) -> bool, + { + let mut inc_token = self.0.lock(); + + loop { + let cur_tok = *inc_token; + + let (cur_tok, overflow) = if f(cur_tok) { + cur_tok.overflowing_add(1) + } else { + break cur_tok; + }; + + if overflow { + *inc_token = 1; + } else { + *inc_token = cur_tok; + } + } + } +} diff --git a/src/error/mod.rs b/src/error/mod.rs index edb818a..ad89de7 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -6,6 +6,7 @@ pub enum FusoError{ BadMagic, Timeout, Abort, + BadRpcCall(u64), TomlDeError(toml::de::Error), StdIo(std::io::Error), } diff --git a/src/runtime/tokio/port_forward/mod.rs b/src/runtime/tokio/port_forward/mod.rs index 332333f..f66562f 100644 --- a/src/runtime/tokio/port_forward/mod.rs +++ b/src/runtime/tokio/port_forward/mod.rs @@ -3,23 +3,22 @@ use std::net::SocketAddr; use crate::{ core::{ accepter::Accepter, - io::{AsyncRead, AsyncWrite}, processor::Preprocessor, rpc::structs::port_forward::VisitorProtocol, BoxedStream, Connection, Stream, }, error, - server::port_forward::{PortForwarder, ShareAccepter, Whence}, + server::port_forward::{PortForwarder, MuxAccepter, Whence}, }; use super::TokioRuntime; -impl ShareAccepter +impl MuxAccepter where A: Accepter)> + Unpin + Send, { pub fn new(magic: u32, secret: [u8; 16], accepter: A) -> Self { - ShareAccepter::::new_runtime(accepter, magic, secret) + MuxAccepter::::new_runtime(accepter, magic, secret) } } diff --git a/src/server/manager/mod.rs b/src/server/manager/mod.rs new file mode 100644 index 0000000..427b389 --- /dev/null +++ b/src/server/manager/mod.rs @@ -0,0 +1,42 @@ +use std::future::Future; + +use crate::error; + +pub trait Service { + fn run(&self) -> error::Result<()>; + + fn name(&self) -> String; +} + +pub trait Manager { + fn manage(&mut self, service: S) -> error::Result<()> + where + S: Into, + S1: Service + 'static; +} + +pub struct MultiServiceManager {} + +impl Manager for MultiServiceManager { + fn manage(&mut self, service: S) -> error::Result<()> + where + S: Into, + S1: Service + 'static, + { + unimplemented!() + } +} + +// impl Service for T +// where +// T: FnOnce() -> F, +// F: Future> + Send, +// { +// fn run(&self) -> error::Result<()> { +// todo!() +// } + +// fn name(&self) -> String { +// todo!() +// } +// } diff --git a/src/server/mod.rs b/src/server/mod.rs index 5116479..9170c0b 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,8 +1,14 @@ +use crate::{ + core::{accepter::Accepter}, + service::FusoService, +}; + mod handshake; -pub mod port_forward; +pub mod manager; + +pub mod port_forward; +pub mod proxy_tunnel; +pub struct Serve {} -pub trait Handshake { - -} \ No newline at end of file diff --git a/src/server/port_forward/accepter.rs b/src/server/port_forward/accepter.rs index 07eb0ab..81070fb 100644 --- a/src/server/port_forward/accepter.rs +++ b/src/server/port_forward/accepter.rs @@ -14,14 +14,14 @@ use crate::core::{accepter::Accepter, BoxedStream}; use crate::error; use crate::runtime::Runtime; -pub struct ShareAccepter { +pub struct MuxAccepter { accepter: A, handshaker: Arc, connections: Poller<'static, error::Result>, _marked: PhantomData, } -impl Accepter for ShareAccepter +impl Accepter for MuxAccepter where A: Accepter)> + Unpin + 'static, R: Runtime + Unpin + 'static, @@ -75,7 +75,7 @@ where } } -impl ShareAccepter +impl MuxAccepter where R: Runtime, { @@ -95,7 +95,7 @@ where } #[cfg(feature = "fuso-runtime")] -impl ShareAccepter +impl MuxAccepter where A: Accepter)> + Unpin + Send, { diff --git a/src/server/port_forward/mod.rs b/src/server/port_forward/mod.rs index bfcde62..d52bbd6 100644 --- a/src/server/port_forward/mod.rs +++ b/src/server/port_forward/mod.rs @@ -17,6 +17,7 @@ use std::{collections::HashMap, pin::Pin, sync::Arc, task::Poll}; use crate::core::future::Poller; use crate::core::io::AsyncReadExt; use crate::core::rpc::structs::port_forward; +use crate::core::token::IncToken; use crate::core::Stream; use crate::runtime::Runtime; use crate::{ @@ -28,14 +29,13 @@ use crate::{ structs::port_forward::{Request, VisitorProtocol}, AsyncCall, }, - split::SplitStream, }, error, }; type Connection = crate::core::Connection<'static>; -enum Outcome { +pub enum Outcome { Ready(u64, Connection), Pending(u64), Timeout(u64), @@ -49,7 +49,7 @@ pub enum Whence { #[derive(Clone)] pub struct Visitors { - inc_token: Arc>, + inc_token: IncToken, connections: Arc>>, } @@ -57,7 +57,7 @@ pub struct PortForwarder { poller: Poller<'static, error::Result>, accepter: A, visitors: Visitors, - transport: Transport, + transport: Transport<'static, T>, prepmap: WrappedPreprocessor<'static, Connection, error::Result>, prepvis: WrappedPreprocessor<'static, Connection, error::Result>, _marked: PhantomData, @@ -110,42 +110,44 @@ where } }; - polled = match Pin::new(&mut self.poller).poll(ctx) { - Poll::Pending => polled, - Poll::Ready(r) => match r { - Err(_) => unimplemented!(), - Ok(forward) => { - match forward { - Outcome::Pending(token) => { - self.poller.add(Self::wait_transport( - token, - std::time::Duration::from_secs(10), - )); - } - Outcome::Timeout(token) => { - if let Some(conn) = self.visitors.take(token) { - log::warn!("failed to create mapping {{ token={}, addr={}, msg='timeout' }}", token, conn.addr()); - } - } - Outcome::Ready(token, transport) => { - match self.visitors.take(token) { - Some(visitor) => return Poll::Ready(Ok((visitor, transport))), - None => log::warn!("invalid mapping because the client has been closed {{ token={token} }}") - }; - } - Outcome::Stopped(error) => { - return { - match error { - Some(e) => Poll::Ready(Err(e)), - None => Poll::Ready(Err(error::FusoError::Abort)), - } - } - } - } + let (need_poll, outcome) = match Pin::new(&mut self.poller).poll(ctx) { + Poll::Pending => continue, + Poll::Ready(Ok(o)) => (false, o), + Poll::Ready(Err(_)) => continue, + }; + + polled = need_poll; - false + match outcome { + Outcome::Pending(token) => { + self.poller.add(Self::wait_transport( + token, + std::time::Duration::from_secs(10), + )); + } + Outcome::Timeout(token) => { + if let Some(conn) = self.visitors.take(token) { + log::warn!( + "failed to create mapping {{ token={}, addr={}, msg='timeout' }}", + token, + conn.addr() + ); } - }, + } + Outcome::Ready(token, transport) => { + match self.visitors.take(token) { + Some(visitor) => return Poll::Ready(Ok((visitor, transport))), + None => log::warn!("invalid mapping because the client has been closed {{ token={token} }}") + }; + } + Outcome::Stopped(error) => { + return { + match error { + Some(e) => Poll::Ready(Err(e)), + None => Poll::Ready(Err(error::FusoError::Abort)), + } + } + } }; } @@ -166,12 +168,17 @@ where M: Preprocessor>, M: Send + Sync + 'static, { - let (reader, writer) = stream.split(); + let mut poller = Poller::new(); + + let (transport, hold) = Transport::new(stream); + + poller.add(hold); + Self { - poller: Poller::new(), + poller, accepter, + transport, visitors: Default::default(), - transport: Transport::new(reader, writer), prepvis: WrappedPreprocessor(Arc::new(prepvis)), prepmap: WrappedPreprocessor(Arc::new(prepmap)), _marked: PhantomData, @@ -193,7 +200,7 @@ where async fn do_prepare_visitor( conn: Connection, visitors: Visitors, - transport: Transport, + transport: Transport<'_, T>, preprocessor: WrappedPreprocessor<'_, Connection, error::Result>, ) -> error::Result { let mut transport = transport; @@ -223,7 +230,7 @@ where async fn do_prepare_mapping( conn: Connection, - _: Transport, + _: Transport<'_, T>, preprocessor: WrappedPreprocessor<'_, Connection, error::Result>, ) -> error::Result { let mut conn = preprocessor.prepare(conn).await?; @@ -243,7 +250,7 @@ where impl Default for Visitors { fn default() -> Self { Self { - inc_token: Arc::new(Mutex::new(1)), + inc_token: Default::default(), connections: Default::default(), } } @@ -264,22 +271,7 @@ impl Visitors { fn next_token(&self) -> u64 { let connections = self.connections.lock(); - let mut inc_token = self.inc_token.lock(); - - loop { - let cur_tok = *inc_token; - - let (cur_tok, overflow) = if connections.contains_key(&cur_tok) { - cur_tok.overflowing_add(1) - } else { - break cur_tok; - }; - - if overflow { - *inc_token = 1; - } else { - *inc_token = cur_tok; - } - } + self.inc_token + .next(|token| !connections.contains_key(&token)) } } diff --git a/src/server/port_forward/transport.rs b/src/server/port_forward/transport.rs index 682f706..33c343d 100644 --- a/src/server/port_forward/transport.rs +++ b/src/server/port_forward/transport.rs @@ -1,41 +1,69 @@ +use std::pin::Pin; +use std::task::Poll; + +use super::Outcome; + +use crate::core::rpc::caller::{Caller, Reactor}; +use crate::core::rpc::{self, Decoder}; + use crate::{ core::{ - io::{AsyncRead, AsyncWrite, AsyncWriteExt}, rpc::{ + rpc::{ structs::port_forward::{Request, Response}, AsyncCall, - }, split::{ReadHalf, WriteHalf}, BoxedFuture + }, + BoxedFuture, Stream, }, error, }; -pub struct Transport { - reader: ReadHalf, - writer: WriteHalf, +pub struct Transport<'a, T> { + caller: Caller<'a, T>, } -impl Transport { - pub fn new(reader: ReadHalf, writer: WriteHalf) -> Self { - Self { reader, writer } +pub struct TransportHold(Reactor<'static>); + +impl<'a, T> Transport<'a, T> +where + T: Stream + Send + Unpin + 'static, +{ + pub fn new(stream: T) -> (Self, TransportHold) { + let (reactor, caller) = rpc::caller::Caller::new(stream); + (Self { caller }, TransportHold(reactor)) } } -impl AsyncCall for Transport -where T: AsyncWrite + AsyncRead + Send + Unpin{ +impl<'transport, T> AsyncCall for Transport<'transport, T> +where + T: Stream + Send + Unpin + 'transport, +{ type Output = error::Result; fn call<'a>(&'a mut self, data: Request) -> BoxedFuture<'a, Self::Output> { - Box::pin(async move { - self.writer.write(b"").await?; - Ok(Response::Ok) - }) + Box::pin(async move { self.caller.call(data).await?.decode() }) } } -impl Clone for Transport { +impl<'transport, T> Clone for Transport<'transport, T> { fn clone(&self) -> Self { Self { - reader: self.reader.clone(), - writer: self.writer.clone(), + caller: self.caller.clone(), + } + } +} + +impl std::future::Future for TransportHold { + type Output = error::Result; + fn poll( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + match Pin::new(&mut self.0).poll(cx) { + std::task::Poll::Pending => Poll::Pending, + std::task::Poll::Ready(e) => match e { + Err(e) => Poll::Ready(Ok(Outcome::Stopped(Some(e)))), + Ok(()) => Poll::Ready(Ok(Outcome::Stopped(None))), + }, } } } diff --git a/src/server/proxy_tunnel/mod.rs b/src/server/proxy_tunnel/mod.rs new file mode 100644 index 0000000..ddb4b52 --- /dev/null +++ b/src/server/proxy_tunnel/mod.rs @@ -0,0 +1,19 @@ +use crate::{core::accepter::Accepter, error}; + +pub struct ProxyTunnel {} + +impl ProxyTunnel { + pub fn new() -> Self { + unimplemented!() + } +} + +impl Accepter for ProxyTunnel { + type Output = error::Result<()>; + fn poll_accept( + self: std::pin::Pin<&mut Self>, + ctx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + unimplemented!() + } +} diff --git a/src/service/mod.rs b/src/service/mod.rs index e69de29..2b8c7e0 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -0,0 +1 @@ +pub struct FusoService; \ No newline at end of file From 58c2a9cfbadaf5fbbba301676a1b26a302d863a6 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Thu, 21 Mar 2024 16:01:45 +0000 Subject: [PATCH 07/29] =?UTF-8?q?feat:=20=E7=BC=96=E5=86=99=E7=AB=AF?= =?UTF-8?q?=E5=8F=A3=E8=BD=AC=E5=8F=91=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bin/client.rs | 82 ++++++++++++++++++++++++++++++++-- src/bin/server.rs | 4 +- src/cli/client.rs | 23 ++++++++++ src/client/mod.rs | 1 + src/client/port_forward/mod.rs | 3 ++ src/config/client.rs | 47 ++++++++++++++----- src/config/mod.rs | 30 +++++++++++-- src/lib.rs | 2 +- src/runtime/mod.rs | 14 +++++- src/service/mod.rs | 81 ++++++++++++++++++++++++++++++++- 10 files changed, 262 insertions(+), 25 deletions(-) create mode 100644 src/client/port_forward/mod.rs diff --git a/src/bin/client.rs b/src/bin/client.rs index cee5e36..09f47d4 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -1,3 +1,79 @@ -fn main(){ - -} \ No newline at end of file +use fuso::{ + cli, + config::{ + client::{Config, Service, WithBridgeService, WithForwardService, WithProxyService}, + Stateful, + }, + error, + service::{FnService, NamedService, ServiceManager}, +}; + +fn main() { + match cli::client::parse() { + Ok(conf) => fuso::enter_async_main(enter_fuso_main(conf)).unwrap(), + Err(e) => { + log::error!("{e:?}") + } + } +} + +fn enter_fuso_main(mut conf: Config) -> ServiceManager { + let mut sp = ServiceManager::new(); + + let services = std::mem::replace(&mut conf.services, Default::default()); + + let conf = Stateful::new(conf); + + for (name, service) in services { + let sc = conf.clone(); + match service { + Service::Proxy(s) => { + sp.register( + s.restart.clone(), + NamedService::new(name, { + FnService::new(move || enter_proxy_service_main(sc.clone(), s.clone())) + }), + ); + } + Service::Bridge(s) => { + sp.register( + s.restart.clone(), + NamedService::new(name, { + FnService::new(move || enter_bridge_service_main(sc.clone(), s.clone())) + }), + ); + } + Service::Forward(s) => { + sp.register( + s.restart.clone(), + NamedService::new(name, { + FnService::new(move || enter_forward_service_main(sc.clone(), s.clone())) + }), + ); + } + } + } + + sp.build() +} + +async fn enter_proxy_service_main( + config: Stateful, + service: WithProxyService, +) -> error::Result<()> { + Ok(()) +} + +async fn enter_bridge_service_main( + config: Stateful, + service: WithBridgeService, +) -> error::Result<()> { + Ok(()) +} + +async fn enter_forward_service_main( + config: Stateful, + service: WithForwardService, +) -> error::Result<()> { + Ok(()) +} diff --git a/src/bin/server.rs b/src/bin/server.rs index ae9e30d..bda8478 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -42,9 +42,7 @@ async fn enter_fuso_main(conf: Config) -> error::Result<()> { .filter_level(log::LevelFilter::Debug) .init(); - enter_fuso_serve(Stateful{ - conf - }).await?; + enter_fuso_serve(Stateful::new(conf)).await?; // axum::serve(tcp_listener, make_service) loop { diff --git a/src/cli/client.rs b/src/cli/client.rs index e69de29..789dcda 100644 --- a/src/cli/client.rs +++ b/src/cli/client.rs @@ -0,0 +1,23 @@ +use clap::Parser; + +use crate::{config::client::Config, error}; + +#[derive(Debug, Parser)] +#[command(version, about, long_about = None)] +pub struct App { + #[arg(short, long)] + config: Option, +} + +pub fn parse() -> error::Result { + let mut app = App::parse(); + + app.config.replace(format!("config/client.toml")); + + let cfg = match app.config { + None => Config::default(), + Some(cfg) => toml::from_str(&{ std::fs::read_to_string(cfg)? })?, + }; + + Ok(cfg) +} diff --git a/src/client/mod.rs b/src/client/mod.rs index e69de29..2a2e6e7 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -0,0 +1 @@ +pub mod port_forward; \ No newline at end of file diff --git a/src/client/port_forward/mod.rs b/src/client/port_forward/mod.rs new file mode 100644 index 0000000..cc9ee6f --- /dev/null +++ b/src/client/port_forward/mod.rs @@ -0,0 +1,3 @@ +pub struct PortForwarder{ + +} \ No newline at end of file diff --git a/src/config/client.rs b/src/config/client.rs index abc6007..1cbfff9 100644 --- a/src/config/client.rs +++ b/src/config/client.rs @@ -1,12 +1,12 @@ use std::{ collections::{HashMap, HashSet}, io, - net::IpAddr, + net::{IpAddr, Ipv4Addr}, }; use serde::{Deserialize, Serialize}; -use super::{Authentication, BootKind, Compress, Crypto, KeepAlive}; +use super::{Authentication, BootKind, Compress, Crypto, KeepAlive, RestartPolicy}; #[derive(Debug, Serialize, Deserialize)] pub struct Config { @@ -40,14 +40,14 @@ pub struct Server { pub authentication: Authentication, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(untagged)] pub enum ServerAddr { WithIpAddr(Vec), WithDomain(Vec), } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "lowercase")] pub enum Service { /// 代理 @@ -57,11 +57,13 @@ pub enum Service { Forward(WithForwardService), } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct WithForwardService { /// 服务器运行方式 #[serde(default = "Default::default")] pub boot: BootKind, + #[serde(default = "Default::default")] + pub restart: RestartPolicy, /// 转发到的目标地址 pub target: FinalTarget, /// 对于某些长连接的请求,保持会话 @@ -82,20 +84,24 @@ pub struct WithForwardService { pub compress: HashSet, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct WithBridgeService { pub bind: IpAddr, pub port: u16, + #[serde(default = "Default::default")] + pub restart: RestartPolicy, #[serde(rename = "auth")] pub authentication: Authentication, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct WithProxyService { /// 监听地址 pub bind: IpAddr, /// 监听端口 pub port: u16, + #[serde(default = "Default::default")] + pub restart: RestartPolicy, /// 启动方式 #[serde(default = "Default::default")] pub boot: BootKind, @@ -114,20 +120,21 @@ pub struct WithProxyService { pub keep_alive: Option, } -#[derive(Debug, Serialize, Deserialize)] + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(tag = "with", rename_all = "lowercase")] pub enum Rewrite { #[serde(rename = "http_header")] HttpHeader(WithHttpHeaderRewrite), } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct WithHttpHeaderRewrite { #[serde(flatten)] pub headers: HashMap, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "lowercase")] pub enum FinalTarget { /// 静态地址 @@ -142,7 +149,6 @@ impl Default for FinalTarget { } } - impl ServerAddr { async fn connect(&self, port: u16) -> io::Result<()> { match self { @@ -156,6 +162,25 @@ impl ServerAddr { } } +impl Default for Config { + fn default() -> Self { + Config { + server: Server { + addr: ServerAddr::WithIpAddr(vec![IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))]), + ports: vec![6528], + retries: -1, // always + crypto: Default::default(), + compress: Default::default(), + authentication: Authentication::None, + }, + features: Default::default(), + services: Default::default(), + default_crypto: Default::default(), + default_compress: Default::default(), + } + } +} + #[cfg(test)] #[cfg(feature = "fuso-toml")] diff --git a/src/config/mod.rs b/src/config/mod.rs index 6eed130..853a328 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,4 +1,4 @@ -use std::ops::Deref; +use std::{ops::Deref, sync::Arc}; use serde::{Deserialize, Serialize}; @@ -52,25 +52,47 @@ pub struct KeepAlive { interval: u32, } + + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum RestartPolicy{ + Never, + Always, + Counter +} + impl Default for BootKind { fn default() -> Self { Self::Default } } +impl Default for RestartPolicy{ + fn default() -> Self { + RestartPolicy::Always + } +} pub struct Stateful{ - pub conf: C + pub conf: Arc } impl Stateful{ - + pub fn new(c: C) -> Self{ + Self { conf: Arc::new(c) } + } } impl Deref for Stateful{ - type Target = C; + type Target = Arc; fn deref(&self) -> &Self::Target { &self.conf } +} + +impl Clone for Stateful{ + fn clone(&self) -> Self { + Stateful { conf: self.conf.clone() } + } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 3dda2d7..d4b0499 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,5 +21,5 @@ pub fn enter_async_main(fut: F) -> error::Result<()> where F: std::future::Future> + Send, { - runtime::tokio::block_on(fut) + runtime::block_on(fut) } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index d272cf7..ae005a2 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -3,16 +3,26 @@ use crate::{core::BoxedFuture, error}; #[cfg(feature = "fuso-rt-tokio")] pub mod tokio; - pub trait Runtime { fn spawn(fut: F) -> () where F: std::future::Future + Send + 'static, O: Send + 'static; - fn wait_for<'a, F, O>(timeout: std::time::Duration, fut: F) -> BoxedFuture<'a, error::Result> + fn wait_for<'a, F, O>( + timeout: std::time::Duration, + fut: F, + ) -> BoxedFuture<'a, error::Result> where F: std::future::Future + Send + 'a; fn sleep(timeout: std::time::Duration) -> BoxedFuture<'static, ()>; } + +#[cfg(feature = "fuso-rt-tokio")] +pub fn block_on(f: F) -> T +where + F: std::future::Future, +{ + tokio::block_on(f) +} diff --git a/src/service/mod.rs b/src/service/mod.rs index 2b8c7e0..2ee5291 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -1 +1,80 @@ -pub struct FusoService; \ No newline at end of file +use crate::{config::RestartPolicy, core::BoxedFuture, error}; + +pub trait IService { + fn call(&self) -> BoxedFuture<'static, error::Result<()>>; +} + +pub struct BoxedService {} + +pub struct FusoService; + +pub struct ServiceManager {} + +pub struct ServiceBuilder {} + +pub struct NamedService { + name: String, +} + +pub struct FnService(Box BoxedFuture<'static, error::Result<()>>>); + +impl ServiceManager { + pub fn new() -> ServiceBuilder { + ServiceBuilder {} + } +} + +impl NamedService { + pub fn new(name: N, service: S) -> Self + where + N: ToString, + S: IService, + { + unimplemented!() + } +} + +impl ServiceBuilder { + pub fn register(&mut self, restart_policy: RestartPolicy, service: S) + where + S: IService + 'static, + { + unimplemented!() + } + + pub fn build(self) -> ServiceManager { + unimplemented!() + } +} + +impl std::future::Future for ServiceManager { + type Output = error::Result<()>; + fn poll( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + unimplemented!() + } +} + +impl IService for NamedService { + fn call(&self) -> BoxedFuture<'static, error::Result<()>> { + unimplemented!() + } +} + +impl IService for FnService { + fn call(&self) -> BoxedFuture<'static, error::Result<()>> { + unimplemented!() + } +} + +impl FnService { + pub fn new(f: F) -> Self + where + F: Fn() -> Fut + 'static, + Fut: std::future::Future> + Send + 'static, + { + Self(Box::new(move || Box::pin(f()))) + } +} From 8f0fd029bac1075b82397e078df18a811a09e4fe Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Sun, 24 Mar 2024 14:26:22 +0000 Subject: [PATCH 08/29] =?UTF-8?q?feat:=20=E7=BC=96=E5=86=99=E7=AB=AF?= =?UTF-8?q?=E5=8F=A3=E8=BD=AC=E5=8F=91=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/client.toml | 15 +- src/bin/client.rs | 154 ++++++++++++++--- src/bin/server.rs | 4 +- src/client/port_forward/mod.rs | 28 +++- src/config/client.rs | 96 ++++++++++- src/config/mod.rs | 1 + src/core/connector.rs | 10 ++ src/core/future.rs | 8 +- src/core/mod.rs | 1 + src/core/net/tcp.rs | 2 +- src/core/stream/compress/mod.rs | 8 +- src/core/stream/crypto/mod.rs | 9 +- src/core/stream/handshake.rs | 59 +++++++ src/core/stream/mod.rs | 28 +++- src/lib.rs | 2 +- src/runner/mod.rs | 289 ++++++++++++++++++++++++++++++++ src/runtime/tokio/tcp.rs | 3 +- src/server/mod.rs | 1 - src/service/mod.rs | 80 --------- 19 files changed, 664 insertions(+), 134 deletions(-) create mode 100644 src/core/connector.rs create mode 100644 src/core/stream/handshake.rs create mode 100644 src/runner/mod.rs delete mode 100644 src/service/mod.rs diff --git a/config/client.toml b/config/client.toml index efd44c2..48605b2 100644 --- a/config/client.toml +++ b/config/client.toml @@ -2,7 +2,7 @@ features = ["kcp", "socks5"] [server] addr = ["127.0.0.1"] -ports = [9999, 8080] +ports = [8080] retries = -1 crypto = ["rsa"] compress = ["lz4"] @@ -12,8 +12,18 @@ auth = { type = "secret", secret = "12345678" } [ssh1] type = "forward" boot = "fork" -exposes = [8080, 9999] +exposes = [8080] channel = 6777 +restart = "always" +target = { type = "static", port = 2222, addr = ["127.0.0.1"] } +keep_alive = { interval = 100 } + +[ssh2] +type = "forward" +boot = "fork" +exposes = [8080] +channel = 6777 +restart = "always" target = { type = "static", port = 2222, addr = ["127.0.0.1"] } keep_alive = { interval = 100 } @@ -21,7 +31,6 @@ keep_alive = { interval = 100 } type = "bridge" bind = "0.0.0.0" port = 9999 -auth = { type = "secret", secret = "111111111111111" } [proxy1] type = "proxy" diff --git a/src/bin/client.rs b/src/bin/client.rs index 09f47d4..144afc2 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -1,24 +1,45 @@ +use std::net::SocketAddr; + use fuso::{ cli, + client::port_forward::PortForwarder, config::{ - client::{Config, Service, WithBridgeService, WithForwardService, WithProxyService}, + client::{ + Config, Host, ServerAddr, Service, WithBridgeService, WithForwardService, + WithProxyService, + }, Stateful, }, + core::{ + accepter::AccepterExt, + io::StreamExt, + net::{TcpListener, TcpStream}, + rpc::{caller::Caller, AsyncCall}, + stream::{handshake::Handshake, UseCompress, UseCrypto}, + }, error, - service::{FnService, NamedService, ServiceManager}, + runner::{FnRunnable, NamedRunnable, Rise, ServiceRunner}, + runtime::tokio::TokioRuntime, }; fn main() { + env_logger::Builder::new() + .filter_level(log::LevelFilter::Debug) + .init(); + match cli::client::parse() { - Ok(conf) => fuso::enter_async_main(enter_fuso_main(conf)).unwrap(), + Ok(conf) => { + conf.check(); + fuso::enter_async_main(enter_fuso_main(conf)).unwrap() + } Err(e) => { log::error!("{e:?}") } } } -fn enter_fuso_main(mut conf: Config) -> ServiceManager { - let mut sp = ServiceManager::new(); +fn enter_fuso_main(mut conf: Config) -> ServiceRunner<'static, TokioRuntime> { + let mut sp = ServiceRunner::::new(); let services = std::mem::replace(&mut conf.services, Default::default()); @@ -30,24 +51,24 @@ fn enter_fuso_main(mut conf: Config) -> ServiceManager { Service::Proxy(s) => { sp.register( s.restart.clone(), - NamedService::new(name, { - FnService::new(move || enter_proxy_service_main(sc.clone(), s.clone())) + NamedRunnable::new(name, { + FnRunnable::new(move || enter_proxy_service_main(sc.clone(), s.clone())) }), ); } Service::Bridge(s) => { sp.register( s.restart.clone(), - NamedService::new(name, { - FnService::new(move || enter_bridge_service_main(sc.clone(), s.clone())) + NamedRunnable::new(name, { + FnRunnable::new(move || enter_bridge_service_main(sc.clone(), s.clone())) }), ); } Service::Forward(s) => { sp.register( s.restart.clone(), - NamedService::new(name, { - FnService::new(move || enter_forward_service_main(sc.clone(), s.clone())) + NamedRunnable::new(name, { + FnRunnable::new(move || enter_forward_service_main(sc.clone(), s.clone())) }), ); } @@ -57,23 +78,114 @@ fn enter_fuso_main(mut conf: Config) -> ServiceManager { sp.build() } +async fn enter_forward_service_main( + config: Stateful, + service: WithForwardService, +) -> error::Result { + let server = &config.server; + let crypto = &server.crypto; + let compress = &server.compress; + + let result = server + .try_connect(|host, port| async move { + log::debug!("connect to server {host}:{port}"); + match host { + Host::Ip(ip) => TcpStream::connect(SocketAddr::new(*ip, port)).await, + Host::Domain(domain) => TcpStream::connect(format!("{domain}:{port}")).await, + } + }) + .await? + .use_crypto(crypto.clone()) + .use_compress(compress.clone()) + .client_handshake(&server.authentication) + .await; + + log::debug!("..."); + + let transport = match result { + Ok(stream) => stream, + Err(e) => { + log::error!("fail to handshake {e:?}"); + return Ok(Rise::Fatal); + } + }; + + Ok(Rise::Fatal) +} + async fn enter_proxy_service_main( config: Stateful, service: WithProxyService, -) -> error::Result<()> { - Ok(()) +) -> error::Result { + let server = &config.server; + let crypto = &server.crypto; + let compress = &server.compress; + + let result = server + .try_connect(|host, port| async move { + log::debug!("connect to server {host}:{port}"); + match host { + Host::Ip(ip) => TcpStream::connect(SocketAddr::new(*ip, port)).await, + Host::Domain(domain) => TcpStream::connect(format!("{domain}:{port}")).await, + } + }) + .await? + .use_crypto(crypto.clone()) + .use_compress(compress.clone()) + .client_handshake(&server.authentication) + .await; + + log::debug!("..."); + + let transport = match result { + Ok(stream) => stream, + Err(e) => { + log::error!("fail to handshake {e:?}"); + return Ok(Rise::Fatal); + } + }; + + Ok(Rise::Fatal) } async fn enter_bridge_service_main( config: Stateful, service: WithBridgeService, -) -> error::Result<()> { - Ok(()) -} +) -> error::Result { + let mut listener = match TcpListener::bind(SocketAddr::new(service.bind, service.port)).await { + Ok(listener) => listener, + Err(e) => { + return Ok(Rise::Fatal); + } + }; -async fn enter_forward_service_main( - config: Stateful, - service: WithForwardService, -) -> error::Result<()> { - Ok(()) + log::debug!("bridge started ... {}:{}", service.bind, service.port); + + loop { + let (addr, mut stream) = listener.accept().await?; + + log::debug!("{:?}", addr); + + let server = config.server.clone(); + + tokio::spawn(async move { + let result = server + .try_connect(|host, port| async move { + match host { + Host::Ip(ip) => TcpStream::connect(SocketAddr::new(*ip, port)).await, + Host::Domain(domain) => { + TcpStream::connect(format!("{domain}:{port}")).await + } + } + }) + .await; + + match result { + Ok(upstream) => { + upstream.copy(&mut stream).await; + } + Err(e) => {} + }; + }); + } } diff --git a/src/bin/server.rs b/src/bin/server.rs index bda8478..4cac6f4 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -11,7 +11,7 @@ use fuso::{ processor::{IProcessor, Processor, StreamProcessor}, protocol::{AsyncPacketRead, AsyncPacketSend}, split::SplitStream, - stream::fallback::Fallback, + stream::{fallback::Fallback, handshake::Handshake}, }, error, server::{ @@ -151,6 +151,8 @@ async fn enter_fuso_serve(conf: Stateful) -> error::Result<()> { loop { let (addr, (_, transport)) = accepter.accept().await?; + + let mut forwarder = PortForwarder::new( transport, { diff --git a/src/client/port_forward/mod.rs b/src/client/port_forward/mod.rs index cc9ee6f..93b03c8 100644 --- a/src/client/port_forward/mod.rs +++ b/src/client/port_forward/mod.rs @@ -1,3 +1,25 @@ -pub struct PortForwarder{ - -} \ No newline at end of file +use env_logger::Target; + +use crate::core::{accepter::Accepter, Stream}; + +pub struct PortForwarder {} + +impl PortForwarder { + pub fn new(transport: S, connector: C) -> Self + where + S: Stream + Unpin, + { + unimplemented!() + } +} + +impl Accepter for PortForwarder { + type Output = Target; + + fn poll_accept( + self: std::pin::Pin<&mut Self>, + ctx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + todo!() + } +} diff --git a/src/config/client.rs b/src/config/client.rs index 1cbfff9..300024e 100644 --- a/src/config/client.rs +++ b/src/config/client.rs @@ -1,9 +1,12 @@ use std::{ collections::{HashMap, HashSet}, + fmt::Display, io, net::{IpAddr, Ipv4Addr}, }; +use crate::error; + use serde::{Deserialize, Serialize}; use super::{Authentication, BootKind, Compress, Crypto, KeepAlive, RestartPolicy}; @@ -27,7 +30,7 @@ pub enum Feature { Socks5, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Server { pub addr: ServerAddr, pub ports: Vec, @@ -90,8 +93,6 @@ pub struct WithBridgeService { pub port: u16, #[serde(default = "Default::default")] pub restart: RestartPolicy, - #[serde(rename = "auth")] - pub authentication: Authentication, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -120,7 +121,6 @@ pub struct WithProxyService { pub keep_alive: Option, } - #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(tag = "with", rename_all = "lowercase")] pub enum Rewrite { @@ -143,22 +143,71 @@ pub enum FinalTarget { Dynamic, } +#[derive(Debug)] +pub enum Host<'a> { + Ip(&'a IpAddr), + Domain(&'a str), +} + impl Default for FinalTarget { fn default() -> Self { Self::Dynamic } } +impl Display for Host<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Host::Ip(ip) => write!(f, "{ip}"), + Host::Domain(domain) => write!(f, "{domain}"), + } + } +} + impl ServerAddr { - async fn connect(&self, port: u16) -> io::Result<()> { + pub async fn try_connect<'a, F, O, Fut>(&'a self, port: u16, f: F) -> error::Result + where + F: Fn(Host<'a>, u16) -> Fut + 'a, + Fut: std::future::Future> + 'a, + { match self { - ServerAddr::WithIpAddr(addr) => { - unimplemented!() + ServerAddr::WithIpAddr(addrs) => { + for ip in addrs { + if let Ok(output) = f(Host::Ip(ip), port).await { + return Ok(output); + } + } } - ServerAddr::WithDomain(domain) => { - unimplemented!() + ServerAddr::WithDomain(domains) => { + for domain in domains { + if let Ok(output) = f(Host::Domain(domain), port).await { + return Ok(output); + } + } } } + + Err(error::FusoError::StdIo( + io::ErrorKind::ConnectionRefused.into(), + )) + } +} + +impl Server { + pub async fn try_connect<'a, F, O, Fut>(&'a self, f: F) -> error::Result + where + F: Fn(Host<'a>, u16) -> Fut + Copy + 'a, + Fut: std::future::Future> + 'a, + { + for port in &self.ports { + if let Ok(output) = self.addr.try_connect(*port, f).await { + return Ok(output); + } + } + + Err(error::FusoError::StdIo( + io::ErrorKind::ConnectionRefused.into(), + )) } } @@ -181,6 +230,35 @@ impl Default for Config { } } +impl Config { + pub fn check(&self) -> error::Result<()> { + let mut bind_ports = Vec::new(); + + let mut remote_ports = self.server.ports.clone(); + + for (name, service) in &self.services { + match service { + Service::Proxy(proxy) => { + bind_ports.push((name, proxy.port)); + } + Service::Bridge(bridge) => { + bind_ports.push((name, bridge.port)); + } + Service::Forward(forward) => {} + } + } + + for (name, port) in bind_ports { + if remote_ports.contains(&port) { + log::error!("loop call at {name}"); + return Err(error::FusoError::Abort); + } + } + + Ok(()) + } +} + #[cfg(test)] #[cfg(feature = "fuso-toml")] diff --git a/src/config/mod.rs b/src/config/mod.rs index 853a328..6d8206f 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -55,6 +55,7 @@ pub struct KeepAlive { #[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] pub enum RestartPolicy{ Never, Always, diff --git a/src/core/connector.rs b/src/core/connector.rs new file mode 100644 index 0000000..e233d26 --- /dev/null +++ b/src/core/connector.rs @@ -0,0 +1,10 @@ +pub trait Connector { + +} + + + + +pub trait MuxConnector { + +} \ No newline at end of file diff --git a/src/core/future.rs b/src/core/future.rs index c5cb1f2..a7a9994 100644 --- a/src/core/future.rs +++ b/src/core/future.rs @@ -41,7 +41,13 @@ impl<'a, O> StoredFuture<'a, O> { None => Box::pin(f()), }; - Pin::new(&mut fut).poll(cx) + match Pin::new(&mut fut).poll(cx) { + Poll::Ready(o) => Poll::Ready(o), + Poll::Pending => { + self.0.replace(fut); + Poll::Pending + } + } } } diff --git a/src/core/mod.rs b/src/core/mod.rs index f12426b..6e8054e 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -8,6 +8,7 @@ use std::{ use self::io::{AsyncRead, AsyncWrite}; +pub mod connector; pub mod accepter; pub mod future; pub mod handshake; diff --git a/src/core/net/tcp.rs b/src/core/net/tcp.rs index ce36e77..0b333bd 100644 --- a/src/core/net/tcp.rs +++ b/src/core/net/tcp.rs @@ -32,7 +32,7 @@ pub struct TcpStream { } impl TcpStream { - pub async fn connect(addr: A) -> error::Result + pub async fn connect_with_provider(addr: A) -> error::Result where P: TcpProvider, A: Into, diff --git a/src/core/stream/compress/mod.rs b/src/core/stream/compress/mod.rs index 8e8cd41..33f4d73 100644 --- a/src/core/stream/compress/mod.rs +++ b/src/core/stream/compress/mod.rs @@ -19,10 +19,10 @@ pub struct CompressedStream<'a> { compressor: BoxedCodec<'a>, } -pub async fn compress_stream<'a, S>( +pub fn compress_stream<'a, S>( stream: S, compress: Vec, -) -> error::Result> +) -> CompressedStream<'a> where S: AsyncRead + AsyncWrite + Send + Unpin + 'a, { @@ -54,10 +54,10 @@ where compressor }; - Ok(CompressedStream { + CompressedStream { stream: BoxedStream::new(stream), compressor, - }) + } } impl<'a> AsyncRead for CompressedStream<'a> { diff --git a/src/core/stream/crypto/mod.rs b/src/core/stream/crypto/mod.rs index 9338c07..9622ea7 100644 --- a/src/core/stream/crypto/mod.rs +++ b/src/core/stream/crypto/mod.rs @@ -41,10 +41,7 @@ pub struct EncryptedStream<'a> { crypto: BoxedCodec<'a>, } -pub async fn encrypt_stream<'a, S>( - stream: S, - cryptos: Vec, -) -> error::Result> +pub fn encrypt_stream<'a, S>(stream: S, cryptos: Vec) -> EncryptedStream<'a> where S: AsyncRead + AsyncWrite + Send + Unpin + 'a, { @@ -77,10 +74,10 @@ where crypto }; - Ok(EncryptedStream { + EncryptedStream { crypto, stream: BoxedStream::new(stream), - }) + } } impl AsyncCrypto for T where T: AsyncDecrypt + AsyncEncrypt {} diff --git a/src/core/stream/handshake.rs b/src/core/stream/handshake.rs new file mode 100644 index 0000000..64f05a3 --- /dev/null +++ b/src/core/stream/handshake.rs @@ -0,0 +1,59 @@ +use crate::{ + config::{AuthWithAccount, AuthWithSecret, Authentication}, + core::{ + protocol::{AsyncPacketRead, AsyncPacketSend}, + rpc::{Decoder, Encoder}, + BoxedFuture, Stream, + }, + error, +}; + +pub trait Handshake: Stream + Send + Unpin { + fn client_handshake<'a>( + mut self, + auth: &'a Authentication, + ) -> BoxedFuture<'a, error::Result> + where + Self: Sized + 'a, + { + Box::pin(async move { + match auth { + Authentication::None => Ok(self), + Authentication::Secret(secret) => { + self.send_packet(&secret.encode()?).await?; + let token: usize = self.recv_packet().await?.decode()?; + unimplemented!() + } + Authentication::Account(account) => { + self.send_packet(&account.encode()?).await?; + let token: usize = self.recv_packet().await?.decode()?; + unimplemented!() + } + } + }) + } + + fn server_handshake<'a>( + mut self, + auth: &'a Authentication, + ) -> BoxedFuture<'a, error::Result> + where + Self: Sized + 'a, + { + Box::pin(async move { + match auth { + Authentication::None => Ok(self), + Authentication::Secret(secret) => { + let client_secret: AuthWithSecret = self.recv_packet().await?.decode()?; + unimplemented!() + } + Authentication::Account(account) => { + let client_account: AuthWithAccount = self.recv_packet().await?.decode()?; + unimplemented!() + } + } + }) + } +} + +impl Handshake for T where T: Stream + Send + Unpin {} diff --git a/src/core/stream/mod.rs b/src/core/stream/mod.rs index dec0f31..aa3d1ff 100644 --- a/src/core/stream/mod.rs +++ b/src/core/stream/mod.rs @@ -1,4 +1,30 @@ +use self::{compress::CompressedStream, crypto::EncryptedStream}; + +use super::io::{AsyncRead, AsyncWrite}; + pub mod codec; +pub mod compress; pub mod crypto; pub mod fallback; -pub mod compress; \ No newline at end of file +pub mod handshake; + +pub trait UseCrypto<'a>: AsyncRead + AsyncWrite + Send + Unpin { + fn use_crypto(self, cryptos: Vec) -> EncryptedStream<'a> + where + Self: Sized + 'a, + { + crypto::encrypt_stream(self, cryptos) + } +} + +pub trait UseCompress<'a>: AsyncRead + AsyncWrite + Send + Unpin { + fn use_compress(self, compress: Vec) -> CompressedStream<'a> + where + Self: Sized + 'a, + { + compress::compress_stream(self, compress) + } +} + +impl<'a, T> UseCrypto<'a> for T where T: AsyncRead + AsyncWrite + Send + Unpin {} +impl<'a, T> UseCompress<'a> for T where T: AsyncRead + AsyncWrite + Send + Unpin {} diff --git a/src/lib.rs b/src/lib.rs index d4b0499..b052793 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ pub mod core; #[cfg(feature = "fuso-config")] pub mod config; -pub mod service; +pub mod runner; pub mod server; diff --git a/src/runner/mod.rs b/src/runner/mod.rs new file mode 100644 index 0000000..2bfc0cb --- /dev/null +++ b/src/runner/mod.rs @@ -0,0 +1,289 @@ +use std::{marker::PhantomData, pin::Pin, task::Poll}; + +use crate::{ + config::RestartPolicy, + core::{future::Poller, BoxedFuture}, + error, + runtime::Runtime, +}; + +pub trait IRunnable: Sync { + fn call(&self) -> BoxedFuture<'static, error::Result>; + + fn name<'a>(&'a self) -> &'a str { + "Unknown" + } + + fn restartable(&mut self) -> bool { + false + } +} + +pub struct BoxedRunnable<'a>(Box); + +pub enum Rise { + Exit, + Fatal, + Restart, +} + +pub struct ServiceRunner<'a, Runtime> { + poller: Poller<'a, (BoxedRunnable<'a>, error::Result)>, + _marked: PhantomData, +} + +pub struct RunnerBuilder<'a> { + poller: Poller<'a, (BoxedRunnable<'a>, error::Result)>, +} + +pub struct AlwayRestartRunnable { + runnable: R, +} + +pub struct OnceRestartRunnable { + runnable: R, +} + +pub struct CounterRestartRunnable { + runnable: R, + counter: usize, +} + +pub struct NamedRunnable { + name: String, + runnable: R, +} + +enum Runnable<'a> { + Running, + Pause(BoxedRunnable<'a>, BoxedFuture<'a, error::Result>), +} + +pub struct FnRunnable(Box BoxedFuture<'static, error::Result> + Send>); + +impl<'a> BoxedRunnable<'a> { + pub fn new(service: S) -> Self + where + S: IRunnable + Send + 'a, + { + Self(Box::new(service)) + } +} + +impl<'a, Runtime> ServiceRunner<'a, Runtime> { + pub fn new() -> RunnerBuilder<'a> { + RunnerBuilder { + poller: Poller::new(), + } + } +} + +impl<'a> RunnerBuilder<'a> { + pub fn register(&mut self, restart_policy: RestartPolicy, runnable: S) + where + S: IRunnable + Send + 'static, + { + self.poller.add({ + let fut = runnable.call(); + Runnable::Pause( + { + match restart_policy { + RestartPolicy::Never => { + BoxedRunnable::new(OnceRestartRunnable { runnable }) + } + RestartPolicy::Always => { + BoxedRunnable::new(AlwayRestartRunnable { runnable }) + } + RestartPolicy::Counter => BoxedRunnable::new(CounterRestartRunnable { + runnable, + counter: 1, + }), + } + }, + fut, + ) + }) + } + + pub fn build(self) -> ServiceRunner<'a, Runtime> { + ServiceRunner { + poller: self.poller, + _marked: PhantomData, + } + } +} + +impl std::future::Future for ServiceRunner<'_, R> +where + R: Runtime + Unpin, +{ + type Output = error::Result<()>; + fn poll( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + loop { + match Pin::new(&mut self.poller).poll(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready((mut runnable, result)) => { + log::debug!("{} stooped", runnable.name()); + match runnable.restartable() { + true => match result { + Ok(Rise::Fatal | Rise::Exit) => {} + _ => { + let fut = runnable.call(); + log::debug!("restart {}", runnable.name()); + self.poller.add(Runnable::Pause(runnable, { + Box::pin(async move { + R::sleep(std::time::Duration::from_secs(1)).await; + fut.await + }) + })) + } + }, + false => { + log::debug!("finished task") + } + }; + } + } + } + } +} + +impl FnRunnable { + pub fn new(f: F) -> Self + where + F: Fn() -> Fut + Send + 'static, + Fut: std::future::Future> + Send + 'static, + { + Self(Box::new(move || Box::pin(f()))) + } +} + +impl NamedRunnable +where + R: IRunnable, +{ + pub fn new(name: N, runnable: R) -> Self + where + N: ToString, + { + Self { + name: name.to_string(), + runnable, + } + } +} + +unsafe impl Sync for FnRunnable {} + +impl IRunnable for FnRunnable { + fn call(&self) -> BoxedFuture<'static, error::Result> { + (self.0)() + } +} + +impl IRunnable for BoxedRunnable<'_> { + fn call(&self) -> BoxedFuture<'static, error::Result> { + self.0.call() + } + + fn name<'a>(&'a self) -> &'a str { + self.0.name() + } + + fn restartable(&mut self) -> bool { + self.0.restartable() + } +} + +impl IRunnable for NamedRunnable +where + R: IRunnable, +{ + fn call(&self) -> BoxedFuture<'static, error::Result> { + self.runnable.call() + } + + fn name<'a>(&'a self) -> &'a str { + &self.name + } +} + +impl IRunnable for OnceRestartRunnable +where + R: IRunnable, +{ + fn call(&self) -> BoxedFuture<'static, error::Result> { + self.runnable.call() + } + + fn name<'a>(&'a self) -> &'a str { + self.runnable.name() + } + + fn restartable(&mut self) -> bool { + false + } +} + +impl IRunnable for CounterRestartRunnable +where + R: IRunnable, +{ + fn call(&self) -> BoxedFuture<'static, error::Result> { + self.runnable.call() + } + + fn name<'a>(&'a self) -> &'a str { + self.runnable.name() + } + + fn restartable(&mut self) -> bool { + if self.counter > 0 { + self.counter -= 1; + } + + self.counter > 0 + } +} + +impl IRunnable for AlwayRestartRunnable +where + R: IRunnable, +{ + fn call(&self) -> BoxedFuture<'static, error::Result> { + self.runnable.call() + } + + fn name<'a>(&'a self) -> &'a str { + self.runnable.name() + } + + fn restartable(&mut self) -> bool { + true + } +} + +impl<'a> std::future::Future for Runnable<'a> { + type Output = (BoxedRunnable<'a>, error::Result); + fn poll( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll { + match std::mem::replace(&mut *self, Runnable::Running) { + Runnable::Running => unsafe { std::hint::unreachable_unchecked() }, + Runnable::Pause(runnable, mut fut) => match Pin::new(&mut fut).poll(cx) { + Poll::Ready(r) => Poll::Ready((runnable, r)), + Poll::Pending => { + drop(std::mem::replace( + &mut *self, + Runnable::Pause(runnable, fut), + )); + Poll::Pending + } + }, + } + } +} diff --git a/src/runtime/tokio/tcp.rs b/src/runtime/tokio/tcp.rs index 1fc55e5..db04a17 100644 --- a/src/runtime/tokio/tcp.rs +++ b/src/runtime/tokio/tcp.rs @@ -44,7 +44,7 @@ impl io::AsyncWrite for tokio::net::TcpStream { } impl TcpStream { - pub async fn connect_with_tokio(addr: A) -> error::Result + pub async fn connect(addr: A) -> error::Result where A: tokio::net::ToSocketAddrs, { @@ -55,7 +55,6 @@ impl TcpStream { } } - #[cfg(feature = "fuso-rt-tokio")] impl TcpListener { pub async fn bind(addr: A) -> error::Result diff --git a/src/server/mod.rs b/src/server/mod.rs index 9170c0b..9772553 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,6 +1,5 @@ use crate::{ core::{accepter::Accepter}, - service::FusoService, }; mod handshake; diff --git a/src/service/mod.rs b/src/service/mod.rs deleted file mode 100644 index 2ee5291..0000000 --- a/src/service/mod.rs +++ /dev/null @@ -1,80 +0,0 @@ -use crate::{config::RestartPolicy, core::BoxedFuture, error}; - -pub trait IService { - fn call(&self) -> BoxedFuture<'static, error::Result<()>>; -} - -pub struct BoxedService {} - -pub struct FusoService; - -pub struct ServiceManager {} - -pub struct ServiceBuilder {} - -pub struct NamedService { - name: String, -} - -pub struct FnService(Box BoxedFuture<'static, error::Result<()>>>); - -impl ServiceManager { - pub fn new() -> ServiceBuilder { - ServiceBuilder {} - } -} - -impl NamedService { - pub fn new(name: N, service: S) -> Self - where - N: ToString, - S: IService, - { - unimplemented!() - } -} - -impl ServiceBuilder { - pub fn register(&mut self, restart_policy: RestartPolicy, service: S) - where - S: IService + 'static, - { - unimplemented!() - } - - pub fn build(self) -> ServiceManager { - unimplemented!() - } -} - -impl std::future::Future for ServiceManager { - type Output = error::Result<()>; - fn poll( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - unimplemented!() - } -} - -impl IService for NamedService { - fn call(&self) -> BoxedFuture<'static, error::Result<()>> { - unimplemented!() - } -} - -impl IService for FnService { - fn call(&self) -> BoxedFuture<'static, error::Result<()>> { - unimplemented!() - } -} - -impl FnService { - pub fn new(f: F) -> Self - where - F: Fn() -> Fut + 'static, - Fut: std::future::Future> + Send + 'static, - { - Self(Box::new(move || Box::pin(f()))) - } -} From 7f33235f3300a3351f5694539cdf8e447388bef4 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Sat, 6 Apr 2024 04:18:54 +0000 Subject: [PATCH 09/29] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/client.toml | 14 +-- config/server.toml | 2 +- src/bin/client.rs | 60 +++++++---- src/bin/server.rs | 107 ++++++++++++++----- src/client/port_forward/linker.rs | 16 +++ src/client/port_forward/mod.rs | 41 +++++-- src/config/client.rs | 6 +- src/config/mod.rs | 9 +- src/config/server.rs | 8 +- src/core/{rpc => }/channel.rs | 4 + src/core/connector.rs | 110 ++++++++++++++++++- src/core/future.rs | 14 ++- src/core/mod.rs | 20 +++- src/core/net/kcp.rs | 6 +- src/core/port_forward.rs | 73 +++++++++++++ src/core/processor.rs | 2 +- src/core/protocol.rs | 1 + src/core/rpc/callee.rs | 0 src/core/rpc/caller.rs | 147 ++++++++++++++++++-------- src/core/rpc/mod.rs | 58 ++++++++-- src/core/stream/compress/mod.rs | 45 ++++---- src/core/stream/crypto/mod.rs | 41 ++++--- src/core/stream/handshake.rs | 136 +++++++++++++++++++----- src/core/stream/mod.rs | 8 +- src/core/transfer.rs | 24 +++++ src/error/mod.rs | 47 +++++--- src/runner/mod.rs | 26 ++++- src/runtime/tokio/kcp.rs | 4 +- src/runtime/tokio/port_forward/mod.rs | 21 ++-- src/server/manager/mod.rs | 42 -------- src/server/mod.rs | 2 +- src/server/port_forward/accepter.rs | 36 +++++++ src/server/port_forward/mod.rs | 27 ++--- src/server/port_forward/transport.rs | 69 ------------ 34 files changed, 866 insertions(+), 360 deletions(-) create mode 100644 src/client/port_forward/linker.rs rename src/core/{rpc => }/channel.rs (97%) create mode 100644 src/core/port_forward.rs create mode 100644 src/core/rpc/callee.rs create mode 100644 src/core/transfer.rs delete mode 100644 src/server/manager/mod.rs delete mode 100644 src/server/port_forward/transport.rs diff --git a/config/client.toml b/config/client.toml index 48605b2..caa862e 100644 --- a/config/client.toml +++ b/config/client.toml @@ -2,7 +2,7 @@ features = ["kcp", "socks5"] [server] addr = ["127.0.0.1"] -ports = [8080] +ports = [8888] retries = -1 crypto = ["rsa"] compress = ["lz4"] @@ -12,7 +12,7 @@ auth = { type = "secret", secret = "12345678" } [ssh1] type = "forward" boot = "fork" -exposes = [8080] +exposes = [8082] channel = 6777 restart = "always" target = { type = "static", port = 2222, addr = ["127.0.0.1"] } @@ -21,9 +21,9 @@ keep_alive = { interval = 100 } [ssh2] type = "forward" boot = "fork" -exposes = [8080] -channel = 6777 -restart = "always" +exposes = [8081] +channel = 6771 +restart = "never" target = { type = "static", port = 2222, addr = ["127.0.0.1"] } keep_alive = { interval = 100 } @@ -35,7 +35,8 @@ port = 9999 [proxy1] type = "proxy" bind = "0.0.0.0" -port = 8080 +port = 8081 +restart = 'never' crypto = ["rsa", "aes"] target = { type = "static", addr = ["baidu.com"], port = 443 } rewrite = { with = "http_header", host = "https://baidu.com" } @@ -44,5 +45,6 @@ rewrite = { with = "http_header", host = "https://baidu.com" } type = "proxy" bind = "0.0.0.0" port = 7890 +restart = 'never' crypto = ["rsa", "aes"] target = { type = "dynamic" } \ No newline at end of file diff --git a/config/server.toml b/config/server.toml index 5f8761d..a65da44 100644 --- a/config/server.toml +++ b/config/server.toml @@ -5,7 +5,7 @@ features = ["kcp", "proxy", "multiprocess"] [auth] type = "secret" -secret = "fasedfhkasjdfh" +secret = "12345678" [manager] bind = "127.0.0.1" diff --git a/src/bin/client.rs b/src/bin/client.rs index 144afc2..f14d978 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -1,4 +1,4 @@ -use std::net::SocketAddr; +use std::{net::SocketAddr, time::Duration}; use fuso::{ cli, @@ -12,9 +12,11 @@ use fuso::{ }, core::{ accepter::AccepterExt, - io::StreamExt, + connector::MultiConnector, + io::{AsyncReadExt, StreamExt}, net::{TcpListener, TcpStream}, - rpc::{caller::Caller, AsyncCall}, + protocol::{AsyncPacketRead, AsyncPacketSend}, + rpc::{AsyncCall, Caller}, stream::{handshake::Handshake, UseCompress, UseCrypto}, }, error, @@ -95,24 +97,45 @@ async fn enter_forward_service_main( } }) .await? - .use_crypto(crypto.clone()) - .use_compress(compress.clone()) - .client_handshake(&server.authentication) + .use_crypto(crypto.iter()) + .use_compress(compress.iter()) + .client_handshake::( + &server.authentication, + Duration::from_secs(server.authentication_timeout as _), + ) .await; - log::debug!("..."); - - let transport = match result { + let mut stream = match result { Ok(stream) => stream, Err(e) => { log::error!("fail to handshake {e:?}"); - return Ok(Rise::Fatal); + return Ok(Rise::Restart); } }; - Ok(Rise::Fatal) + stream.write_config(&service).await?; + + let mut connector = MultiConnector::::new(); + + if let Some(channel) = service.channel.as_ref() { + // connector.add(); + } + + let mut forwarder = PortForwarder::new(stream, connector); + + loop { + let (linker, target) = forwarder.accept().await?; + + tokio::spawn(async move { + let a = linker.link().await; + }); + } + + } + + async fn enter_proxy_service_main( config: Stateful, service: WithProxyService, @@ -123,20 +146,21 @@ async fn enter_proxy_service_main( let result = server .try_connect(|host, port| async move { - log::debug!("connect to server {host}:{port}"); + // log::debug!("connect to server {host}:{port}"); match host { Host::Ip(ip) => TcpStream::connect(SocketAddr::new(*ip, port)).await, Host::Domain(domain) => TcpStream::connect(format!("{domain}:{port}")).await, } }) .await? - .use_crypto(crypto.clone()) - .use_compress(compress.clone()) - .client_handshake(&server.authentication) + .use_crypto(crypto.iter()) + .use_compress(compress.iter()) + .client_handshake::( + &server.authentication, + Duration::from_secs(server.authentication_timeout as _), + ) .await; - log::debug!("..."); - let transport = match result { Ok(stream) => stream, Err(e) => { @@ -145,7 +169,7 @@ async fn enter_proxy_service_main( } }; - Ok(Rise::Fatal) + Ok(Rise::Restart) } async fn enter_bridge_service_main( diff --git a/src/bin/server.rs b/src/bin/server.rs index 4cac6f4..f0cfc0e 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -1,7 +1,12 @@ -use std::{net::SocketAddr, str::FromStr}; +use std::{ + net::{IpAddr, SocketAddr}, + str::FromStr, + time::Duration, +}; +use bincode::de; use fuso::{ - config::{server::Config, Stateful}, + config::{client::WithForwardService, server::Config, Stateful}, core::{ accepter::{AccepterExt, MultiAccepter, StreamAccepter, TaggedAccepter}, future::Select, @@ -11,13 +16,16 @@ use fuso::{ processor::{IProcessor, Processor, StreamProcessor}, protocol::{AsyncPacketRead, AsyncPacketSend}, split::SplitStream, - stream::{fallback::Fallback, handshake::Handshake}, + stream::{ + fallback::Fallback, + handshake::{ClientConfig, ForwardConfig, Handshake}, + UseCompress, UseCrypto, + }, + BoxedStream, Stream, }, error, - server::{ - manager::{Manager, MultiServiceManager}, - port_forward::{MuxAccepter, PortForwarder}, - }, + runtime::tokio::TokioRuntime, + server::port_forward::{ForwardAccepter, MuxAccepter, PortForwarder}, }; #[derive(Debug, Clone)] @@ -150,31 +158,78 @@ async fn enter_fuso_serve(conf: Stateful) -> error::Result<()> { } loop { - let (addr, (_, transport)) = accepter.accept().await?; + let (_, (addr, transport)) = accepter.accept().await?; + log::debug!("connection from {addr}"); + let conf = conf.clone(); - let mut forwarder = PortForwarder::new( - transport, - { - MuxAccepter::new(1110, rand::random(), { - StreamAccepter::new({ - fuso::core::net::TcpListener::bind( - SocketAddr::from_str("0.0.0.0:9999").unwrap(), - ) - .await? - }) - }) - }, - (), - None, - ); - - let mgr = 0; + tokio::spawn(async move { + if let Err(e) = handle_connection(conf, transport).await { + log::error!("{:?}", e); + } + }); + } +} + +pub async fn handle_connection( + conf: Stateful, + stream: BoxedStream<'static>, +) -> error::Result<()> { + let crypto = &conf.crypto; + let compress = &conf.compress; + + let mut stream = stream + .use_crypto(crypto.iter()) + .use_compress(compress.iter()) + .server_handshake::( + &conf.authentication, + Duration::from_secs(conf.authentication_timeout as _), + ) + .await?; + + match stream.read_config().await? { + ClientConfig::Forward(forward) => enter_forwarder(forward, stream).await?, + } + + Ok(()) +} + +pub async fn enter_forwarder(conf: ForwardConfig, transport: S) -> error::Result<()> +where + S: Stream + Send + Unpin + 'static, +{ + log::debug!("{:#?}", conf); + let mut accepter = MultiAccepter::new(); + + if let Some(port) = conf.channel { + accepter.add(ForwardAccepter::new( + TcpListener::bind(format!("0:{port}")).await?, + )); + } + + let mut multi_mux_accepter = MultiAccepter::new(); + + for port in conf.exposes.iter() { + multi_mux_accepter.add(StreamAccepter::new( + TcpListener::bind(format!("0:{port}")).await?, + )); + } + + let mux_accepter = MuxAccepter::new(1110, rand::random(), { + StreamAccepter::new(multi_mux_accepter) + }); + + accepter.add(mux_accepter); + + let mut forwarder = PortForwarder::new(transport, accepter, (), None); + + log::debug!("forward started .."); + + loop { let (c1, c2) = forwarder.accept().await?; } - let s = fuso::server::Serve {}; Ok(()) } diff --git a/src/client/port_forward/linker.rs b/src/client/port_forward/linker.rs new file mode 100644 index 0000000..4397f68 --- /dev/null +++ b/src/client/port_forward/linker.rs @@ -0,0 +1,16 @@ +use crate::{core::Connection, error}; + +pub struct Linker {} + + + + +impl Linker { + pub async fn link(self) -> error::Result<()> { + unimplemented!() + } + + pub async fn reject(self) -> error::Result<()> { + unimplemented!() + } +} diff --git a/src/client/port_forward/mod.rs b/src/client/port_forward/mod.rs index 93b03c8..42413a9 100644 --- a/src/client/port_forward/mod.rs +++ b/src/client/port_forward/mod.rs @@ -1,20 +1,43 @@ -use env_logger::Target; +mod linker; -use crate::core::{accepter::Accepter, Stream}; +use std::marker::PhantomData; +use self::linker::Linker; -pub struct PortForwarder {} +use crate::{ + config::client::FinalTarget, + core::{ + accepter::Accepter, + port_forward::Transport, + rpc::{structs::port_forward::Request, AsyncCall}, + Connection, Stream, + }, + runtime::Runtime, +}; -impl PortForwarder { - pub fn new(transport: S, connector: C) -> Self + + +pub struct PortForwarder { + _marked: PhantomData, +} + +impl PortForwarder +where + R: Runtime + 'static, +{ + pub fn new_with_runtime(transport: S, connector: C) -> Self where - S: Stream + Unpin, + S: Stream + Unpin + Send + 'static, { - unimplemented!() + let (transport, hold) = Transport::new::(std::time::Duration::from_secs(1), transport); + + unimplemented!() } } -impl Accepter for PortForwarder { - type Output = Target; + + +impl Accepter for PortForwarder { + type Output = (Linker, FinalTarget); fn poll_accept( self: std::pin::Pin<&mut Self>, diff --git a/src/config/client.rs b/src/config/client.rs index 300024e..221e474 100644 --- a/src/config/client.rs +++ b/src/config/client.rs @@ -9,7 +9,7 @@ use crate::error; use serde::{Deserialize, Serialize}; -use super::{Authentication, BootKind, Compress, Crypto, KeepAlive, RestartPolicy}; +use super::{default_auth_timeout, Authentication, BootKind, Compress, Crypto, KeepAlive, RestartPolicy}; #[derive(Debug, Serialize, Deserialize)] pub struct Config { @@ -41,6 +41,9 @@ pub struct Server { pub compress: Vec, #[serde(rename = "auth")] pub authentication: Authentication, + #[serde(rename = "auth_timeout")] + #[serde(default = "default_auth_timeout")] + pub authentication_timeout: u32 } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -221,6 +224,7 @@ impl Default for Config { crypto: Default::default(), compress: Default::default(), authentication: Authentication::None, + authentication_timeout: default_auth_timeout() }, features: Default::default(), services: Default::default(), diff --git a/src/config/mod.rs b/src/config/mod.rs index 6d8206f..8a543c3 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -14,12 +14,12 @@ pub enum Authentication { Account(AuthWithAccount), } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct AuthWithSecret { secret: String, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct AuthWithAccount { username: String, password: String, @@ -96,4 +96,9 @@ impl Clone for Stateful{ fn clone(&self) -> Self { Stateful { conf: self.conf.clone() } } +} + + +pub(crate) fn default_auth_timeout() -> u32{ + 60 } \ No newline at end of file diff --git a/src/config/server.rs b/src/config/server.rs index cc0d49b..c97a920 100644 --- a/src/config/server.rs +++ b/src/config/server.rs @@ -6,7 +6,7 @@ use std::{ use serde::{Deserialize, Serialize}; -use super::{Authentication, Compress, Crypto}; +use super::{default_auth_timeout, Authentication, Compress, Crypto}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Config { @@ -22,6 +22,9 @@ pub struct Config { pub features: HashSet, #[serde(rename = "auth")] pub authentication: Authentication, + #[serde(rename = "auth_timeout")] + #[serde(default = "default_auth_timeout")] + pub authentication_timeout: u32 } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -68,6 +71,7 @@ pub struct KcpListenMetadata { impl ListenMetadata { pub fn as_socket_addr(self) -> SocketAddr { + log::debug!("{}:{}", self.bind, self.port); SocketAddr::new(self.bind, self.port) } } @@ -94,10 +98,12 @@ impl Default for Config { authentication: Authentication::None, crypto: Default::default(), compress: Default::default(), + authentication_timeout: default_auth_timeout(), } } } + #[cfg(test)] #[cfg(feature = "fuso-toml")] mod tests { diff --git a/src/core/rpc/channel.rs b/src/core/channel.rs similarity index 97% rename from src/core/rpc/channel.rs rename to src/core/channel.rs index f49db90..67130b0 100644 --- a/src/core/rpc/channel.rs +++ b/src/core/channel.rs @@ -45,6 +45,10 @@ impl Sender { data: Some(data), } } + + pub fn send_sync(&self, data: T) { + self.container.push(data); + } } impl Clone for Sender { diff --git a/src/core/connector.rs b/src/core/connector.rs index e233d26..53745c8 100644 --- a/src/core/connector.rs +++ b/src/core/connector.rs @@ -1,10 +1,110 @@ -pub trait Connector { - +use std::{ + pin::Pin, + task::{Context, Poll}, +}; + +use crate::error; + +pub trait Connector { + type Output; + + fn poll_connect( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + target: &Target, + ) -> Poll>; +} + +#[pin_project::pin_project] +pub struct Connect<'a, C, T> { + target: T, + #[pin] + connector: &'a mut C, +} + +pub trait ConnectExt: Connector { + fn connect<'a>(&'a mut self, target: T) -> Connect<'a, Self, T> + where + Self: Sized, + { + Connect { + target, + connector: self, + } + } +} + +impl ConnectExt for C where C: Connector {} + +pub struct BoxedConnector<'connector, T, O>( + Box + Send + Unpin + 'connector>, +); + +pub struct MultiConnector<'a, T, O> { + connectors: Vec>, +} + +impl<'a, T, O> MultiConnector<'a, T, O> { + pub fn new() -> Self { + Self { + connectors: Default::default(), + } + } + + pub fn add(&mut self, connector: C) + where + C: Connector + Send + Unpin + 'a, + { + self.connectors.push(BoxedConnector(Box::new(connector))); + } +} + +impl std::future::Future for Connect<'_, C, T> +where + C: Connector + Unpin, +{ + type Output = error::Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + Pin::new(&mut **this.connector).poll_connect(cx, &this.target) + } } +impl Connector for BoxedConnector<'_, T, O> { + type Output = O; + fn poll_connect( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + target: &T, + ) -> Poll> { + Pin::new(&mut *self.0).poll_connect(cx, target) + } +} +impl Connector for MultiConnector<'_, T, O> { + type Output = O; + fn poll_connect( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + target: &T, + ) -> Poll> { + let mut error = Option::None; -pub trait MuxConnector { - -} \ No newline at end of file + for connector in self.connectors.iter_mut() { + match Pin::new(connector).poll_connect(cx, target) { + Poll::Pending => continue, + Poll::Ready(Ok(o)) => return Poll::Ready(Ok(o)), + Poll::Ready(Err(e)) => { + error.replace(e); + } + } + } + + match error { + None => Poll::Pending, + Some(e) => Poll::Ready(Err(e)), + } + } +} diff --git a/src/core/future.rs b/src/core/future.rs index a7a9994..202a17b 100644 --- a/src/core/future.rs +++ b/src/core/future.rs @@ -21,16 +21,16 @@ macro_rules! select { }; } -pub struct StoredFuture<'a, O>(Option + 'a>>>); +pub struct LazyFuture<'a, O>(Option + 'a>>>); pub struct Select<'a, O>(pub Vec>); pub struct Poller<'a, O>(pub Vec>); -unsafe impl<'a, O> Send for StoredFuture<'a, O> {} -unsafe impl<'a, O> Sync for StoredFuture<'a, O> {} +unsafe impl<'a, O> Send for LazyFuture<'a, O> {} +unsafe impl<'a, O> Sync for LazyFuture<'a, O> {} -impl<'a, O> StoredFuture<'a, O> { +impl<'a, O> LazyFuture<'a, O> { pub fn poll(&mut self, cx: &mut Context<'_>, f: F) -> Poll where F: Fn() -> Fut, @@ -51,7 +51,7 @@ impl<'a, O> StoredFuture<'a, O> { } } -impl<'a, O> StoredFuture<'a, O> { +impl<'a, O> LazyFuture<'a, O> { pub fn new() -> Self { Self(Default::default()) } @@ -68,6 +68,10 @@ impl<'a, O> Poller<'a, O> { { self.0.push(Box::pin(fut)) } + + pub fn has_more(&self) -> bool{ + self.0.len() > 0 + } } impl<'a, O> Select<'a, O> { diff --git a/src/core/mod.rs b/src/core/mod.rs index 6e8054e..8bbfa38 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -8,12 +8,14 @@ use std::{ use self::io::{AsyncRead, AsyncWrite}; -pub mod connector; pub mod accepter; +pub mod channel; +pub mod connector; pub mod future; pub mod handshake; pub mod io; pub mod net; +pub mod port_forward; pub mod processor; pub mod protocol; pub mod rpc; @@ -21,6 +23,7 @@ pub mod split; pub mod stream; pub mod task; pub mod token; +pub mod transfer; pub type BoxedFuture<'a, O> = Pin + Send + 'a>>; @@ -90,9 +93,20 @@ impl<'a> From<(SocketAddr, BoxedStream<'a>)> for Connection<'a> { } } -impl Connection<'_> { +impl<'a> Connection<'a> { + pub fn new(addr: SocketAddr, stream: S) -> Self + where + S: Into>, + { + Self { + addr, + stream: stream.into(), + cursor: None, + marked: None, + } + } - pub fn addr(&self) -> &SocketAddr{ + pub fn addr(&self) -> &SocketAddr { &self.addr } diff --git a/src/core/net/kcp.rs b/src/core/net/kcp.rs index 69b6d2a..db5446c 100644 --- a/src/core/net/kcp.rs +++ b/src/core/net/kcp.rs @@ -8,7 +8,7 @@ use std::{ use crate::{ core::{ accepter::Accepter, - future::StoredFuture, + future::LazyFuture, io::{AsyncRead, AsyncWrite}, }, error, @@ -24,7 +24,7 @@ pub trait KcpProvider: UdpProvider { pub struct KcpListener { #[pin] pub(crate) inner: Arc>>, - pub(crate) stored: StoredFuture< + pub(crate) stored: LazyFuture< 'static, std::io::Result<(SocketAddr, kcp_rust::KcpStream)>, >, @@ -48,7 +48,7 @@ impl KcpListener { Ok(KcpListener { inner: Arc::new(listener), - stored: StoredFuture::new(), + stored: LazyFuture::new(), }) } } diff --git a/src/core/port_forward.rs b/src/core/port_forward.rs new file mode 100644 index 0000000..ed5356f --- /dev/null +++ b/src/core/port_forward.rs @@ -0,0 +1,73 @@ +use std::pin::Pin; +use std::task::Poll; + +use crate::core::rpc::{self, Decoder, ICallExt}; +use crate::core::rpc::{Caller, Looper}; + +use crate::runtime::Runtime; +use crate::{ + core::{ + rpc::{ + structs::port_forward::{Request, Response}, + AsyncCall, + }, + BoxedFuture, Stream, + }, + error, +}; + +pub struct Transport { + caller: Caller, +} + +pub struct TransportHold(Looper<'static>); + +impl<'a, T> Transport +where + T: Stream + Send + Unpin + 'static, +{ + pub fn new(heartbeat_delay: std::time::Duration, stream: T) -> (Self, TransportHold) + where + R: Runtime + 'static, + { + let (looper, caller) = rpc::Caller::new::(stream, heartbeat_delay); + + (Self { caller }, TransportHold(looper)) + } +} + +impl AsyncCall for Transport +where + T: Stream + Send + Unpin, +{ + type Output = error::Result; + + fn poll_call( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + arg: &Request, + ) -> std::task::Poll { + match Pin::new(&mut self.caller).poll_call(cx, arg)? { + Poll::Pending => Poll::Pending, + Poll::Ready(data) => Poll::Ready(Ok(data.decode()?)), + } + } +} + +impl Clone for Transport { + fn clone(&self) -> Self { + Self { + caller: self.caller.clone(), + } + } +} + +impl std::future::Future for TransportHold { + type Output = error::Result<()>; + fn poll( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + Pin::new(&mut self.0).poll(cx) + } +} diff --git a/src/core/processor.rs b/src/core/processor.rs index 0a2f090..e24fe89 100644 --- a/src/core/processor.rs +++ b/src/core/processor.rs @@ -1,4 +1,4 @@ -use std::{pin::Pin, sync::Arc}; +use std::sync::Arc; use super::{ io::{AsyncRead, AsyncWrite}, diff --git a/src/core/protocol.rs b/src/core/protocol.rs index fa0d56f..6186512 100644 --- a/src/core/protocol.rs +++ b/src/core/protocol.rs @@ -51,6 +51,7 @@ pub trait AsyncPacketSend: AsyncWrite { }, } } + } impl AsyncPacketRead for T where T: AsyncRead {} diff --git a/src/core/rpc/callee.rs b/src/core/rpc/callee.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/core/rpc/caller.rs b/src/core/rpc/caller.rs index 118cbb1..53489d9 100644 --- a/src/core/rpc/caller.rs +++ b/src/core/rpc/caller.rs @@ -5,37 +5,35 @@ use serde::{Deserialize, Serialize}; use crate::{ core::{ - future::Select, + channel::{self, Receiver, Sender}, + future::{LazyFuture, Select}, protocol::{AsyncPacketRead, AsyncPacketSend}, - rpc::channel, split::{ReadHalf, WriteHalf}, task::{setter, Setter}, token::IncToken, Stream, }, error, + runtime::Runtime, }; -use super::{ - channel::{Receiver, Sender}, - AsyncCall, Decoder, -}; +use super::{AsyncCall, Decoder}; use crate::core::rpc::Encoder; use crate::core::split::SplitStream; #[derive(Debug, Serialize, Deserialize)] -struct Request { - token: u64, - data: Vec, +enum Request { + Ping, + Data { token: u64, data: Vec }, } #[derive(Debug, Serialize, Deserialize)] -struct Response { - token: u64, - data: Vec, +enum Response { + Pong, + Data { token: u64, data: Vec }, } -pub struct Reactor<'a>(Select<'a, error::Result<()>>); +pub struct Looper<'a>(Select<'a, error::Result<()>>); #[derive(Default, Clone)] pub struct Calls { @@ -43,67 +41,120 @@ pub struct Calls { inc_token: IncToken, } -pub struct Caller<'a, S> { +#[pin_project::pin_project] +pub struct Caller { calls: Calls, request: Sender, - marked: PhantomData<(&'a (), S)>, + #[pin] + fut_call: Arc>>>>, + marked: PhantomData, } -impl<'a, S> Caller<'a, S> +impl<'a, S> Caller where S: Stream + Send + Unpin + 'a, { - pub fn new(stream: S) -> (Reactor<'a>, Self) { + pub fn new(stream: S, heartbeat_delay: std::time::Duration) -> (Looper<'a>, Self) + where + R: Runtime + 'a, + { let (reader, writer) = stream.split(); let (req_rx, req_ax) = channel::open::(); + let (heart_rx, heart_ax) = channel::open::(); let calls = Calls::default(); let mut select = Select::new(); - select.add(Reactor::run_recv_looper(calls.clone(), writer, req_ax)); - select.add(Reactor::run_send_looper(calls.clone(), reader)); + select.add(Looper::run_heartbeat::( + heartbeat_delay, + req_rx.clone(), + heart_ax, + )); + + select.add(Looper::run_send_loop(calls.clone(), reader, heart_rx)); + + select.add(Looper::run_recv_loop(calls.clone(), writer, req_ax)); ( - Reactor(select), + Looper(select), Self { calls, request: req_rx, + fut_call: Arc::new(Mutex::new(LazyFuture::new())), marked: PhantomData, }, ) } } -impl<'caller, S, T> AsyncCall for Caller<'caller, S> +impl<'caller, S, T> AsyncCall for Caller where T: serde::Serialize + Send + 'static, S: Send + Unpin + 'caller, { type Output = error::Result>; - fn call<'a>(&'a mut self, data: T) -> crate::core::BoxedFuture<'a, Self::Output> { - let data = data.encode(); + fn poll_call( + self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + arg: &T, + ) -> std::task::Poll { + let this = self.project(); - Box::pin(async move { - let data = data?; + let mut fut_call = this.fut_call.lock(); + Pin::new(&mut fut_call).poll(cx, move || { + let data = arg.encode(); let (setter, getter) = setter(); + let token = this.calls.add(setter); - let token = self.calls.add(setter); + let result = match data { + Ok(data) => { + this.request.send_sync(Request::Data { token, data }); + Ok(()) + } + Err(error) => { + this.calls.cancel(token); + Err(error) + } + }; - match self.request.send(Request { token, data }).await { - Ok(()) => getter.await, - Err(e) => { - self.calls.cancel(token); - Err(e) + async move { + match result { + Ok(_) => getter.await, + Err(e) => Err(e), } } }) } } -impl<'a> Reactor<'a> { - async fn run_recv_looper( +impl<'a> Looper<'a> { + async fn run_heartbeat( + delay: std::time::Duration, + sender: Sender, + receiver: Receiver, + ) -> error::Result<()> + where + R: Runtime, + { + let mut last = std::time::Instant::now(); + + loop { + sender.send(Request::Ping).await?; + + match R::wait_for(delay, receiver.recv()).await?? { + Response::Pong => { + last = std::time::Instant::now(); + } + _ => unsafe { std::hint::unreachable_unchecked() }, + } + + R::sleep(delay).await; + } + } + + async fn run_recv_loop( calls: Calls, writer: WriteHalf, receiver: Receiver, @@ -120,22 +171,28 @@ impl<'a> Reactor<'a> { } } - async fn run_send_looper(calls: Calls, reader: ReadHalf) -> error::Result<()> + async fn run_send_loop( + calls: Calls, + reader: ReadHalf, + sender: Sender, + ) -> error::Result<()> where S: Stream + Unpin, { let mut reader = reader; loop { let data = reader.recv_packet().await?; + let response: Response = data.decode()?; - if let Err(e) = calls.wake(data.decode()?) { - log::warn!("{:?}", e); - } + match response { + Response::Pong => sender.send(Response::Pong).await, + response => calls.wake(response), + }?; } } } -impl<'a> std::future::Future for Reactor<'a> { +impl<'a> std::future::Future for Looper<'a> { type Output = error::Result<()>; fn poll( mut self: Pin<&mut Self>, @@ -156,10 +213,13 @@ impl Calls { token } - fn wake(&self, Response { token, data }: Response) -> error::Result<()> { - match self.call_list.lock().remove(&token) { - None => Err(error::FusoError::BadRpcCall(token)), - Some(setter) => setter.set(data), + fn wake(&self, resp: Response) -> error::Result<()> { + match resp { + Response::Pong => unsafe { std::hint::unreachable_unchecked() }, + Response::Data { token, data } => match self.call_list.lock().remove(&token) { + None => Err(error::FusoError::BadRpcCall(token)), + Some(setter) => setter.set(data), + }, } } @@ -172,12 +232,13 @@ impl Calls { } } -impl Clone for Caller<'_, T> { +impl Clone for Caller { fn clone(&self) -> Self { Caller { calls: self.calls.clone(), request: self.request.clone(), marked: PhantomData, + fut_call: self.fut_call.clone(), } } } diff --git a/src/core/rpc/mod.rs b/src/core/rpc/mod.rs index df318e1..6461e19 100644 --- a/src/core/rpc/mod.rs +++ b/src/core/rpc/mod.rs @@ -1,17 +1,44 @@ -pub mod caller; -pub mod channel; +mod callee; +mod caller; + pub mod structs; -use std::{pin::Pin, task::Poll}; +use std::{ + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; use crate::error; -use super::BoxedFuture; +pub use callee::*; +pub use caller::*; + +#[pin_project::pin_project] +pub struct Call<'caller, C, A, O> { + arg: A, + #[pin] + caller: &'caller mut C, + _marked: PhantomData, +} pub trait AsyncCall { type Output; - fn call<'a>(&'a mut self, data: T) -> BoxedFuture<'a, Self::Output>; + fn poll_call(self: Pin<&mut Self>, cx: &mut Context<'_>, arg: &T) -> Poll; +} + +pub trait ICallExt: AsyncCall { + fn call<'caller>(&'caller mut self, arg: A) -> Call<'caller, Self, A, O> + where + Self: Sized + Unpin, + { + Call { + caller: self, + arg, + _marked: PhantomData, + } + } } pub trait Encoder { @@ -23,12 +50,14 @@ pub trait Decoder { fn decode(self) -> error::Result; } +impl ICallExt for T where T: AsyncCall {} + impl Encoder for T where T: serde::Serialize, { fn encode(self) -> error::Result> { - Ok(bincode::serialize(&self).unwrap()) + bincode::serialize(&self).map_err(Into::into) } } @@ -39,6 +68,21 @@ where type Output = T; fn decode(self) -> error::Result { - unimplemented!() + bincode::deserialize(&self).map_err(Into::into) + } +} + +impl<'caller, C, A, O> std::future::Future for Call<'caller, C, A, O> +where + C: AsyncCall + Unpin, +{ + type Output = O; + + fn poll( + self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + let mut this = self.project(); + Pin::new(&mut **this.caller).poll_call(cx, &this.arg) } } diff --git a/src/core/stream/compress/mod.rs b/src/core/stream/compress/mod.rs index 33f4d73..69db706 100644 --- a/src/core/stream/compress/mod.rs +++ b/src/core/stream/compress/mod.rs @@ -19,11 +19,9 @@ pub struct CompressedStream<'a> { compressor: BoxedCodec<'a>, } -pub fn compress_stream<'a, S>( - stream: S, - compress: Vec, -) -> CompressedStream<'a> +pub fn compress_stream<'a, 'compress, C, S>(stream: S, mut compress: C) -> CompressedStream<'a> where + C: Iterator, S: AsyncRead + AsyncWrite + Send + Unpin + 'a, { fn use_compress<'a>(compress: &Compress) -> BoxedCodec<'a> { @@ -32,28 +30,26 @@ where } } - let compressor = if compress.is_empty() { - BoxedCodec(Box::new(EmptyCodec)) - } else if compress.len() == 1 { - use_compress(&compress[0]) - } else { - let mut compressor = BoxedCodec(Box::new({ - PairCodec { - first: use_compress(&compress[0]), - second: use_compress(&compress[1]), - } - })); - - for crypto_type in &compress[2..] { - compressor = BoxedCodec(Box::new(PairCodec { - first: compressor, - second: use_compress(crypto_type), - })) - } - - compressor + let mut compressor = match compress.next() { + None => BoxedCodec(Box::new(EmptyCodec)), + Some(comp) => match compress.next() { + None => use_compress(comp), + Some(next) => BoxedCodec(Box::new({ + PairCodec { + first: use_compress(comp), + second: use_compress(next), + } + })), + }, }; + for crypto_type in compress { + compressor = BoxedCodec(Box::new(PairCodec { + first: compressor, + second: use_compress(crypto_type), + })) + } + CompressedStream { stream: BoxedStream::new(stream), compressor, @@ -71,7 +67,6 @@ impl<'a> AsyncRead for CompressedStream<'a> { } } - impl<'a> AsyncWrite for CompressedStream<'a> { fn poll_flush( self: std::pin::Pin<&mut Self>, diff --git a/src/core/stream/crypto/mod.rs b/src/core/stream/crypto/mod.rs index 9622ea7..6b6ca65 100644 --- a/src/core/stream/crypto/mod.rs +++ b/src/core/stream/crypto/mod.rs @@ -41,8 +41,9 @@ pub struct EncryptedStream<'a> { crypto: BoxedCodec<'a>, } -pub fn encrypt_stream<'a, S>(stream: S, cryptos: Vec) -> EncryptedStream<'a> +pub fn encrypt_stream<'a, 'crypto, C, S>(stream: S, mut cryptos: C) -> EncryptedStream<'a> where + C: Iterator, S: AsyncRead + AsyncWrite + Send + Unpin + 'a, { fn use_crypto<'a>(crypto: &Crypto) -> BoxedCodec<'a> { @@ -52,28 +53,26 @@ where } } - let crypto = if cryptos.is_empty() { - BoxedCodec(Box::new(EmptyCodec)) - } else if cryptos.len() == 1 { - use_crypto(&cryptos[0]) - } else { - let mut crypto = BoxedCodec(Box::new({ - PairCodec { - first: use_crypto(&cryptos[0]), - second: use_crypto(&cryptos[1]), - } - })); - - for crypto_type in &cryptos[2..] { - crypto = BoxedCodec(Box::new(PairCodec { - first: crypto, - second: use_crypto(crypto_type), - })) - } - - crypto + let mut crypto = match cryptos.next() { + None => BoxedCodec(Box::new(EmptyCodec)), + Some(crypto) => match cryptos.next() { + None => use_crypto(&crypto), + Some(next) => BoxedCodec(Box::new({ + PairCodec { + first: use_crypto(&crypto), + second: use_crypto(&next), + } + })), + }, }; + for crypto_type in cryptos { + crypto = BoxedCodec(Box::new(PairCodec { + first: crypto, + second: use_crypto(&crypto_type), + })) + } + EncryptedStream { crypto, stream: BoxedStream::new(stream), diff --git a/src/core/stream/handshake.rs b/src/core/stream/handshake.rs index 64f05a3..6988e07 100644 --- a/src/core/stream/handshake.rs +++ b/src/core/stream/handshake.rs @@ -1,59 +1,143 @@ +use std::collections::HashSet; + +use serde::{Deserialize, Serialize}; + use crate::{ - config::{AuthWithAccount, AuthWithSecret, Authentication}, + config::{ + client::WithForwardService, AuthWithAccount, AuthWithSecret, Authentication, Compress, + Crypto, + }, core::{ protocol::{AsyncPacketRead, AsyncPacketSend}, rpc::{Decoder, Encoder}, BoxedFuture, Stream, }, - error, + error::{self, FusoError}, + runtime::Runtime, }; +#[derive(Debug, Serialize, Deserialize)] +enum AuthState { + Ok, + Reject, +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum ClientConfig { + Forward(ForwardConfig), +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ForwardConfig { + pub exposes: Vec, + pub channel: Option, + pub cryptos: HashSet, + pub compress: HashSet, +} + +impl Handshake for T where T: Stream + Send + Unpin {} + pub trait Handshake: Stream + Send + Unpin { - fn client_handshake<'a>( + fn client_handshake<'a, R>( mut self, auth: &'a Authentication, + timeout: std::time::Duration, ) -> BoxedFuture<'a, error::Result> where + R: Runtime, Self: Sized + 'a, { Box::pin(async move { - match auth { - Authentication::None => Ok(self), - Authentication::Secret(secret) => { - self.send_packet(&secret.encode()?).await?; - let token: usize = self.recv_packet().await?.decode()?; - unimplemented!() + R::wait_for(timeout, async move { + match auth { + Authentication::None => return Ok(self), + Authentication::Secret(secret) => { + self.send_packet(&secret.encode()?).await?; + } + Authentication::Account(account) => { + self.send_packet(&account.encode()?).await?; + } } - Authentication::Account(account) => { - self.send_packet(&account.encode()?).await?; - let token: usize = self.recv_packet().await?.decode()?; - unimplemented!() + + let state: AuthState = self.recv_packet().await?.decode()?; + + match state { + AuthState::Ok => Ok(self), + AuthState::Reject => Err(FusoError::AuthError), } - } + }) + .await? }) } - fn server_handshake<'a>( + fn server_handshake<'a, R>( mut self, auth: &'a Authentication, + timeout: std::time::Duration, ) -> BoxedFuture<'a, error::Result> where + R: Runtime, Self: Sized + 'a, { Box::pin(async move { - match auth { - Authentication::None => Ok(self), - Authentication::Secret(secret) => { - let client_secret: AuthWithSecret = self.recv_packet().await?.decode()?; - unimplemented!() - } - Authentication::Account(account) => { - let client_account: AuthWithAccount = self.recv_packet().await?.decode()?; - unimplemented!() + R::wait_for(timeout, async move { + match auth { + Authentication::None => Ok(self), + Authentication::Secret(secret) => { + log::debug!("using secret auth"); + + let client_secret: AuthWithSecret = self.recv_packet().await?.decode()?; + if client_secret.ne(&secret) { + self.send_packet(&AuthState::Reject.encode()?).await?; + Err(FusoError::AuthError) + } else { + self.send_packet(&AuthState::Ok.encode()?).await?; + Ok(self) + } + } + Authentication::Account(account) => { + log::debug!("using account auth"); + + let client_account: AuthWithAccount = self.recv_packet().await?.decode()?; + if client_account.ne(&account) { + self.send_packet(&AuthState::Reject.encode()?).await?; + Err(FusoError::AuthError) + } else { + self.send_packet(&AuthState::Ok.encode()?).await?; + Ok(self) + } + } } - } + }) + .await? }) } + + fn read_config<'a>(&'a mut self) -> BoxedFuture<'a, error::Result> + where + Self: Sized + 'a, + { + Box::pin(async move { Ok(self.recv_packet().await?.decode()?) }) + } + + fn write_config<'a, C>(&'a mut self, config: C) -> BoxedFuture<'a, error::Result<()>> + where + C: Into + Send, + Self: Sized + 'a, + { + let config: ClientConfig = config.into(); + + Box::pin(async move { self.send_packet(&config.encode()?).await }) + } } -impl Handshake for T where T: Stream + Send + Unpin {} +impl<'a> From<&'a WithForwardService> for ClientConfig { + fn from(value: &'a WithForwardService) -> Self { + Self::Forward(ForwardConfig { + exposes: value.exposes.clone(), + channel: value.channel.clone(), + cryptos: value.crypto.clone(), + compress: value.compress.clone(), + }) + } +} diff --git a/src/core/stream/mod.rs b/src/core/stream/mod.rs index aa3d1ff..2ef6881 100644 --- a/src/core/stream/mod.rs +++ b/src/core/stream/mod.rs @@ -1,3 +1,5 @@ +use crate::config::{Compress, Crypto}; + use self::{compress::CompressedStream, crypto::EncryptedStream}; use super::io::{AsyncRead, AsyncWrite}; @@ -9,17 +11,19 @@ pub mod fallback; pub mod handshake; pub trait UseCrypto<'a>: AsyncRead + AsyncWrite + Send + Unpin { - fn use_crypto(self, cryptos: Vec) -> EncryptedStream<'a> + fn use_crypto<'crypto, C>(self, cryptos: C) -> EncryptedStream<'a> where Self: Sized + 'a, + C: Iterator, { crypto::encrypt_stream(self, cryptos) } } pub trait UseCompress<'a>: AsyncRead + AsyncWrite + Send + Unpin { - fn use_compress(self, compress: Vec) -> CompressedStream<'a> + fn use_compress<'compress, C>(self, compress: C) -> CompressedStream<'a> where + C: Iterator, Self: Sized + 'a, { compress::compress_stream(self, compress) diff --git a/src/core/transfer.rs b/src/core/transfer.rs new file mode 100644 index 0000000..87ae6b1 --- /dev/null +++ b/src/core/transfer.rs @@ -0,0 +1,24 @@ +use std::{ + pin::Pin, + task::{Context, Poll}, +}; + +use crate::error; + +pub trait TransSender { + fn poll_send( + self: Pin<&mut Self>, + ctx: &mut Context<'_>, + buf: &[u8], + ) -> Poll>; +} + +pub trait TransReceiver { + fn poll_recv( + self: Pin<&mut Self>, + ctx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll>; +} + + diff --git a/src/error/mod.rs b/src/error/mod.rs index ad89de7..2d2390c 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -1,26 +1,39 @@ -pub type Result = std::result::Result; +use std::fmt::Display; +pub type Result = std::result::Result; #[derive(Debug)] -pub enum FusoError{ - BadMagic, - Timeout, - Abort, - BadRpcCall(u64), - TomlDeError(toml::de::Error), - StdIo(std::io::Error), +pub enum FusoError { + BadMagic, + Timeout, + Abort, + AuthError, + BadRpcCall(u64), + Bincode(bincode::Error), + TomlDeError(toml::de::Error), + StdIo(std::io::Error), } +impl From for FusoError { + fn from(value: std::io::Error) -> Self { + Self::StdIo(value) + } +} +impl From for FusoError { + fn from(value: toml::de::Error) -> Self { + Self::TomlDeError(value) + } +} -impl From for FusoError{ - fn from(value: std::io::Error) -> Self { - Self::StdIo(value) - } +impl From for FusoError{ + fn from(value: bincode::Error) -> Self { + Self::Bincode(value) + } } -impl From for FusoError{ - fn from(value: toml::de::Error) -> Self { - Self::TomlDeError(value) - } -} \ No newline at end of file +impl Display for FusoError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} diff --git a/src/runner/mod.rs b/src/runner/mod.rs index 2bfc0cb..79fad1b 100644 --- a/src/runner/mod.rs +++ b/src/runner/mod.rs @@ -124,13 +124,29 @@ where ) -> std::task::Poll { loop { match Pin::new(&mut self.poller).poll(cx) { - Poll::Pending => return Poll::Pending, + Poll::Pending => { + return if self.poller.has_more() { + Poll::Pending + } else { + log::debug!("ready .."); + Poll::Ready(Ok(())) + } + } Poll::Ready((mut runnable, result)) => { - log::debug!("{} stooped", runnable.name()); match runnable.restartable() { true => match result { - Ok(Rise::Fatal | Rise::Exit) => {} - _ => { + Ok(Rise::Fatal | Rise::Exit) => { + log::debug!("{} stooped", runnable.name()); + } + result => { + if let Err(e) = result { + log::warn!( + "failed to start `{}` {}, restart it", + runnable.name(), + e + ) + } + let fut = runnable.call(); log::debug!("restart {}", runnable.name()); self.poller.add(Runnable::Pause(runnable, { @@ -142,7 +158,7 @@ where } }, false => { - log::debug!("finished task") + log::debug!("{} stooped", runnable.name()) } }; } diff --git a/src/runtime/tokio/kcp.rs b/src/runtime/tokio/kcp.rs index 7cbed67..0ef8ed3 100644 --- a/src/runtime/tokio/kcp.rs +++ b/src/runtime/tokio/kcp.rs @@ -4,7 +4,7 @@ use tokio::io::ReadBuf; use crate::{ core::{ - future::StoredFuture, + future::LazyFuture, net::{BoxedDatagram, KcpListener}, BoxedFuture, }, @@ -27,7 +27,7 @@ impl KcpListener { let listener = kcp_rust::KcpListener::new::(udp, conf)?; Ok(Self { inner: Arc::new(listener), - stored: StoredFuture::new(), + stored: LazyFuture::new(), }) } } diff --git a/src/runtime/tokio/port_forward/mod.rs b/src/runtime/tokio/port_forward/mod.rs index f66562f..bb69b84 100644 --- a/src/runtime/tokio/port_forward/mod.rs +++ b/src/runtime/tokio/port_forward/mod.rs @@ -2,13 +2,11 @@ use std::net::SocketAddr; use crate::{ core::{ - accepter::Accepter, - processor::Preprocessor, - rpc::structs::port_forward::VisitorProtocol, + accepter::Accepter, processor::Preprocessor, rpc::structs::port_forward::VisitorProtocol, BoxedStream, Connection, Stream, }, error, - server::port_forward::{PortForwarder, MuxAccepter, Whence}, + server::port_forward::{MuxAccepter, Whence}, }; use super::TokioRuntime; @@ -18,11 +16,11 @@ where A: Accepter)> + Unpin + Send, { pub fn new(magic: u32, secret: [u8; 16], accepter: A) -> Self { - MuxAccepter::::new_runtime(accepter, magic, secret) + Self::new_runtime(accepter, magic, secret) } } -impl PortForwarder +impl crate::server::port_forward::PortForwarder where A: Accepter + Unpin + 'static, T: Stream + Send + Unpin + 'static, @@ -34,6 +32,15 @@ where M: Preprocessor, Output = error::Result>>, M: Send + Sync + 'static, { - PortForwarder::::new_with_runtime(stream, accepter, prepvis, prepmap) + Self::new_with_runtime(stream, accepter, prepvis, prepmap) + } +} + +impl crate::client::port_forward::PortForwarder { + pub fn new(transport: S, connector: C) -> Self + where + S: Stream + Unpin + Send + 'static, + { + Self::new_with_runtime(transport, connector) } } diff --git a/src/server/manager/mod.rs b/src/server/manager/mod.rs deleted file mode 100644 index 427b389..0000000 --- a/src/server/manager/mod.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::future::Future; - -use crate::error; - -pub trait Service { - fn run(&self) -> error::Result<()>; - - fn name(&self) -> String; -} - -pub trait Manager { - fn manage(&mut self, service: S) -> error::Result<()> - where - S: Into, - S1: Service + 'static; -} - -pub struct MultiServiceManager {} - -impl Manager for MultiServiceManager { - fn manage(&mut self, service: S) -> error::Result<()> - where - S: Into, - S1: Service + 'static, - { - unimplemented!() - } -} - -// impl Service for T -// where -// T: FnOnce() -> F, -// F: Future> + Send, -// { -// fn run(&self) -> error::Result<()> { -// todo!() -// } - -// fn name(&self) -> String { -// todo!() -// } -// } diff --git a/src/server/mod.rs b/src/server/mod.rs index 9772553..8745499 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -4,7 +4,7 @@ use crate::{ mod handshake; -pub mod manager; + pub mod port_forward; pub mod proxy_tunnel; diff --git a/src/server/port_forward/accepter.rs b/src/server/port_forward/accepter.rs index 81070fb..2e2a7f4 100644 --- a/src/server/port_forward/accepter.rs +++ b/src/server/port_forward/accepter.rs @@ -10,6 +10,7 @@ use rc4::{KeyInit, Rc4, StreamCipher}; use super::{Connection, Rc4MagicHandshake, Whence}; use crate::core::future::Poller; use crate::core::io::AsyncReadExt; +use crate::core::Stream; use crate::core::{accepter::Accepter, BoxedStream}; use crate::error; use crate::runtime::Runtime; @@ -21,6 +22,10 @@ pub struct MuxAccepter { _marked: PhantomData, } +pub struct ForwardAccepter { + accepter: A, +} + impl Accepter for MuxAccepter where A: Accepter)> + Unpin + 'static, @@ -111,3 +116,34 @@ where } } } + +impl ForwardAccepter +where + A: Accepter + Unpin + Send, + T: Stream + Unpin + 'static, +{ + pub fn new(accepter: A) -> Self { + Self { accepter } + } +} + +impl Accepter for ForwardAccepter +where + A: Accepter + Unpin + Send, + T: Stream + Unpin + Send + 'static, +{ + type Output = Whence; + + fn poll_accept( + mut self: Pin<&mut Self>, + ctx: &mut std::task::Context<'_>, + ) -> Poll> { + match Pin::new(&mut self.accepter).poll_accept(ctx)? { + Poll::Pending => Poll::Pending, + Poll::Ready((addr, stream)) => Poll::Ready(Ok(Whence::Mapping(Connection::new( + addr, + BoxedStream::new(stream), + )))), + } + } +} diff --git a/src/server/port_forward/mod.rs b/src/server/port_forward/mod.rs index d52bbd6..897a229 100644 --- a/src/server/port_forward/mod.rs +++ b/src/server/port_forward/mod.rs @@ -1,12 +1,10 @@ mod accepter; mod handshake; mod preprocessor; -mod transport; pub use accepter::*; pub use handshake::*; pub use preprocessor::*; -pub use transport::*; use parking_lot::Mutex; use std::future::Future; @@ -16,7 +14,9 @@ use std::{collections::HashMap, pin::Pin, sync::Arc, task::Poll}; use crate::core::future::Poller; use crate::core::io::AsyncReadExt; +use crate::core::port_forward::Transport; use crate::core::rpc::structs::port_forward; +use crate::core::rpc::ICallExt; use crate::core::token::IncToken; use crate::core::Stream; use crate::runtime::Runtime; @@ -25,17 +25,14 @@ use crate::{ accepter::Accepter, io::{AsyncRead, AsyncWrite}, processor::{Preprocessor, WrappedPreprocessor}, - rpc::{ - structs::port_forward::{Request, VisitorProtocol}, - AsyncCall, - }, + rpc::structs::port_forward::{Request, VisitorProtocol}, }, error, }; type Connection = crate::core::Connection<'static>; -pub enum Outcome { +enum Outcome { Ready(u64, Connection), Pending(u64), Timeout(u64), @@ -57,7 +54,7 @@ pub struct PortForwarder { poller: Poller<'static, error::Result>, accepter: A, visitors: Visitors, - transport: Transport<'static, T>, + transport: Transport, prepmap: WrappedPreprocessor<'static, Connection, error::Result>, prepvis: WrappedPreprocessor<'static, Connection, error::Result>, _marked: PhantomData, @@ -160,6 +157,7 @@ impl PortForwarder where A: Accepter + Unpin + 'static, T: Stream + Unpin + Send + 'static, + R: Runtime + 'static, { pub fn new_with_runtime(stream: T, accepter: A, prepvis: P, prepmap: M) -> Self where @@ -170,9 +168,14 @@ where { let mut poller = Poller::new(); - let (transport, hold) = Transport::new(stream); + let (transport, hold) = Transport::new::(std::time::Duration::from_secs(10), stream); - poller.add(hold); + poller.add(async move { + match hold.await { + Ok(()) => Ok(Outcome::Stopped(None)), + Err(e) => Ok(Outcome::Stopped(Some(e))), + } + }); Self { poller, @@ -200,7 +203,7 @@ where async fn do_prepare_visitor( conn: Connection, visitors: Visitors, - transport: Transport<'_, T>, + transport: Transport, preprocessor: WrappedPreprocessor<'_, Connection, error::Result>, ) -> error::Result { let mut transport = transport; @@ -230,7 +233,7 @@ where async fn do_prepare_mapping( conn: Connection, - _: Transport<'_, T>, + _: Transport, preprocessor: WrappedPreprocessor<'_, Connection, error::Result>, ) -> error::Result { let mut conn = preprocessor.prepare(conn).await?; diff --git a/src/server/port_forward/transport.rs b/src/server/port_forward/transport.rs deleted file mode 100644 index 33c343d..0000000 --- a/src/server/port_forward/transport.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::pin::Pin; -use std::task::Poll; - -use super::Outcome; - -use crate::core::rpc::caller::{Caller, Reactor}; -use crate::core::rpc::{self, Decoder}; - -use crate::{ - core::{ - rpc::{ - structs::port_forward::{Request, Response}, - AsyncCall, - }, - BoxedFuture, Stream, - }, - error, -}; - -pub struct Transport<'a, T> { - caller: Caller<'a, T>, -} - -pub struct TransportHold(Reactor<'static>); - -impl<'a, T> Transport<'a, T> -where - T: Stream + Send + Unpin + 'static, -{ - pub fn new(stream: T) -> (Self, TransportHold) { - let (reactor, caller) = rpc::caller::Caller::new(stream); - (Self { caller }, TransportHold(reactor)) - } -} - -impl<'transport, T> AsyncCall for Transport<'transport, T> -where - T: Stream + Send + Unpin + 'transport, -{ - type Output = error::Result; - - fn call<'a>(&'a mut self, data: Request) -> BoxedFuture<'a, Self::Output> { - Box::pin(async move { self.caller.call(data).await?.decode() }) - } -} - -impl<'transport, T> Clone for Transport<'transport, T> { - fn clone(&self) -> Self { - Self { - caller: self.caller.clone(), - } - } -} - -impl std::future::Future for TransportHold { - type Output = error::Result; - fn poll( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - match Pin::new(&mut self.0).poll(cx) { - std::task::Poll::Pending => Poll::Pending, - std::task::Poll::Ready(e) => match e { - Err(e) => Poll::Ready(Ok(Outcome::Stopped(Some(e)))), - Ok(()) => Poll::Ready(Ok(Outcome::Stopped(None))), - }, - } - } -} From 353877b33e1d9694afaf8abbb82f92f869b440e9 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Sat, 6 Apr 2024 14:53:33 +0000 Subject: [PATCH 10/29] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/client.toml | 16 +-- src/bin/client.rs | 9 +- src/bin/server.rs | 71 +++++++++---- src/client/port_forward/mod.rs | 44 +++++---- src/client/port_forward/transport.rs | 30 ++++++ src/config/client.rs | 31 ++++-- src/config/mod.rs | 99 ++++++++++++++++--- src/core/mod.rs | 1 - src/core/net/kcp.rs | 2 +- src/core/rpc/callee.rs | 26 +++++ src/core/rpc/caller.rs | 18 ++-- src/core/rpc/mod.rs | 24 +++++ src/core/rpc/structs.rs | 12 ++- src/core/stream/handshake.rs | 10 +- src/error/mod.rs | 2 + src/runtime/tokio/kcp.rs | 6 +- src/runtime/tokio/port_forward/mod.rs | 13 +-- src/server/port_forward/accepter.rs | 34 +++++-- src/server/port_forward/mod.rs | 15 ++- .../port_forward/transport.rs} | 4 +- src/server/proxy_tunnel/mod.rs | 2 +- 21 files changed, 355 insertions(+), 114 deletions(-) create mode 100644 src/client/port_forward/transport.rs rename src/{core/port_forward.rs => server/port_forward/transport.rs} (95%) diff --git a/config/client.toml b/config/client.toml index caa862e..15e5c34 100644 --- a/config/client.toml +++ b/config/client.toml @@ -12,20 +12,20 @@ auth = { type = "secret", secret = "12345678" } [ssh1] type = "forward" boot = "fork" -exposes = [8082] -channel = 6777 +exposes = [ "8080/tcp" ] +channel = [ "8002/tcp", "8002/kcp" ] restart = "always" target = { type = "static", port = 2222, addr = ["127.0.0.1"] } keep_alive = { interval = 100 } -[ssh2] + +[executable] type = "forward" boot = "fork" -exposes = [8081] -channel = 6771 -restart = "never" -target = { type = "static", port = 2222, addr = ["127.0.0.1"] } -keep_alive = { interval = 100 } +exposes = [ "7888/tcp" ] +restart = "always" +target = { type = "executable", path = "/bin/bash" } + [bridge1] type = "bridge" diff --git a/src/bin/client.rs b/src/bin/client.rs index f14d978..d159ed6 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -21,7 +21,7 @@ use fuso::{ }, error, runner::{FnRunnable, NamedRunnable, Rise, ServiceRunner}, - runtime::tokio::TokioRuntime, + runtime::{tokio::TokioRuntime, Runtime}, }; fn main() { @@ -118,7 +118,7 @@ async fn enter_forward_service_main( let mut connector = MultiConnector::::new(); if let Some(channel) = service.channel.as_ref() { - // connector.add(); + // connector.add(connector) } let mut forwarder = PortForwarder::new(stream, connector); @@ -127,15 +127,16 @@ async fn enter_forward_service_main( let (linker, target) = forwarder.accept().await?; tokio::spawn(async move { + + let a = linker.link().await; }); } - - } + async fn enter_proxy_service_main( config: Stateful, service: WithProxyService, diff --git a/src/bin/server.rs b/src/bin/server.rs index f0cfc0e..b9fe081 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -1,18 +1,18 @@ use std::{ - net::{IpAddr, SocketAddr}, + net::{IpAddr, Ipv4Addr, SocketAddr}, str::FromStr, time::Duration, }; use bincode::de; use fuso::{ - config::{client::WithForwardService, server::Config, Stateful}, + config::{client::WithForwardService, server::Config, Expose, Stateful}, core::{ accepter::{AccepterExt, MultiAccepter, StreamAccepter, TaggedAccepter}, future::Select, handshake::Handshaker, io::{AsyncReadExt, AsyncWriteExt, StreamExt}, - net::TcpListener, + net::{KcpListener, TcpListener}, processor::{IProcessor, Processor, StreamProcessor}, protocol::{AsyncPacketRead, AsyncPacketSend}, split::SplitStream, @@ -27,6 +27,7 @@ use fuso::{ runtime::tokio::TokioRuntime, server::port_forward::{ForwardAccepter, MuxAccepter, PortForwarder}, }; +use kcp_rust::KcpConfig; #[derive(Debug, Clone)] pub enum Tagged { @@ -202,26 +203,58 @@ where let mut accepter = MultiAccepter::new(); - if let Some(port) = conf.channel { - accepter.add(ForwardAccepter::new( - TcpListener::bind(format!("0:{port}")).await?, - )); + if let Some(ref channels) = conf.channel { + for expose in channels { + match expose { + Expose::Kcp(ip, port) => { + accepter.add(ForwardAccepter::new_visitor( + KcpListener::bind(Default::default(), ip.to_addr(*port)).await?, + )); + } + Expose::Tcp(ip, port) => accepter.add(ForwardAccepter::new_visitor( + TcpListener::bind(ip.to_addr(*port)).await?, + )), + } + } } - let mut multi_mux_accepter = MultiAccepter::new(); + if conf.channel.is_some() { + for expose in conf.exposes.iter() { + match expose { + Expose::Tcp(ip, port) => { + accepter.add(ForwardAccepter::new_visitor( + TcpListener::bind(ip.to_addr(*port)).await?, + )); + } + Expose::Kcp(ip, port) => { + accepter.add(ForwardAccepter::new_visitor({ + KcpListener::bind(Default::default(), ip.to_addr(*port)).await? + })); + } + } + } + } else { + accepter.add({ + let mut accepter = MultiAccepter::new(); + for expose in conf.exposes.iter() { + match expose { + Expose::Tcp(ip, port) => { + accepter.add(StreamAccepter::new( + TcpListener::bind(ip.to_addr(*port)).await?, + )); + } + Expose::Kcp(ip, port) => { + accepter.add(StreamAccepter::new({ + KcpListener::bind(Default::default(), ip.to_addr(*port)).await? + })); + } + } + } - for port in conf.exposes.iter() { - multi_mux_accepter.add(StreamAccepter::new( - TcpListener::bind(format!("0:{port}")).await?, - )); + MuxAccepter::new(1110, rand::random(), accepter) + }); } - let mux_accepter = MuxAccepter::new(1110, rand::random(), { - StreamAccepter::new(multi_mux_accepter) - }); - - accepter.add(mux_accepter); - let mut forwarder = PortForwarder::new(transport, accepter, (), None); log::debug!("forward started .."); @@ -230,6 +263,6 @@ where let (c1, c2) = forwarder.accept().await?; } - Ok(()) } + \ No newline at end of file diff --git a/src/client/port_forward/mod.rs b/src/client/port_forward/mod.rs index 42413a9..cc92409 100644 --- a/src/client/port_forward/mod.rs +++ b/src/client/port_forward/mod.rs @@ -1,48 +1,54 @@ mod linker; +mod transport; -use std::marker::PhantomData; use self::linker::Linker; +use crate::core::rpc::{structs::port_forward::Request, AsyncCallee}; +use std::{marker::PhantomData, pin::Pin, task::Poll}; use crate::{ + client::port_forward::transport::Transport, config::client::FinalTarget, - core::{ - accepter::Accepter, - port_forward::Transport, - rpc::{structs::port_forward::Request, AsyncCall}, - Connection, Stream, - }, + core::{accepter::Accepter, connector::Connector, Connection, Stream}, runtime::Runtime, }; - - -pub struct PortForwarder { +pub struct PortForwarder { + transport: Transport, _marked: PhantomData, } -impl PortForwarder +impl PortForwarder where R: Runtime + 'static, + S: Stream + Unpin + Send + 'static, { - pub fn new_with_runtime(transport: S, connector: C) -> Self + pub fn new_with_runtime(transport: S, connector: C) -> Self where - S: Stream + Unpin + Send + 'static, + C: Connector<(), Output = Connection<'static>>, { - let (transport, hold) = Transport::new::(std::time::Duration::from_secs(1), transport); + // let (transport, hold) = Transport::new::(std::time::Duration::from_secs(1), transport); unimplemented!() } } - - -impl Accepter for PortForwarder { +impl Accepter for PortForwarder +where + S: Unpin, +{ type Output = (Linker, FinalTarget); fn poll_accept( - self: std::pin::Pin<&mut Self>, + mut self: std::pin::Pin<&mut Self>, ctx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - todo!() + // match Pin::new(&mut self.transport).poll_next(ctx) { + // std::task::Poll::Pending => Poll::Pending, + // std::task::Poll::Ready(Request::New(addr, a)) => { + // unimplemented!() + // }, + // }; + + unimplemented!() } } diff --git a/src/client/port_forward/transport.rs b/src/client/port_forward/transport.rs new file mode 100644 index 0000000..6491fe7 --- /dev/null +++ b/src/client/port_forward/transport.rs @@ -0,0 +1,30 @@ +use std::marker::PhantomData; + +use crate::core::{ + rpc::{structs::port_forward::Request, AsyncCallee}, + Stream, +}; + +pub struct Transport { + _marked: PhantomData +} + +impl Transport { + pub fn new(heartbeat_delay: std::time::Duration, stream: S) -> Self + where + S: Stream + Send + Unpin, + { + unimplemented!() + } +} + + +impl AsyncCallee for Transport { + type Output = Request; + fn poll_next( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + unimplemented!() + } +} diff --git a/src/config/client.rs b/src/config/client.rs index 221e474..1be14f2 100644 --- a/src/config/client.rs +++ b/src/config/client.rs @@ -9,7 +9,10 @@ use crate::error; use serde::{Deserialize, Serialize}; -use super::{default_auth_timeout, Authentication, BootKind, Compress, Crypto, KeepAlive, RestartPolicy}; +use super::{ + default_auth_timeout, Authentication, BootKind, Compress, Crypto, Expose, KeepAlive, + RestartPolicy, +}; #[derive(Debug, Serialize, Deserialize)] pub struct Config { @@ -43,7 +46,7 @@ pub struct Server { pub authentication: Authentication, #[serde(rename = "auth_timeout")] #[serde(default = "default_auth_timeout")] - pub authentication_timeout: u32 + pub authentication_timeout: u32, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -76,12 +79,12 @@ pub struct WithForwardService { pub keep_alive: Option, /// 服务端对外暴露的端口, 可以填写多个, 确保端口没有被占用 /// 如果没有填写,那么随机分配,这时请确保防火墙中该随机端口被允许访问 - #[serde(default = "Default::default")] - pub exposes: Vec, + #[serde(default = "default_exposes")] + pub exposes: HashSet, /// 与服务器建立连接时的端口 /// 该字段是可选的, 如果没有填写, 将会使用`exposes`字段中的端口来建立连接 /// 如果填写了0, 那么会随机分配一个端口,这时请确保防火墙中该随机端口被允许访问 - pub channel: Option, + pub channel: Option>, /// 加密方式 #[serde(default = "Default::default")] pub crypto: HashSet, @@ -142,6 +145,11 @@ pub struct WithHttpHeaderRewrite { pub enum FinalTarget { /// 静态地址 Static { addr: ServerAddr, port: u16 }, + Executable { + path: String, + #[serde(default = "Default::default")] + args: Vec, + }, /// 当该地址是一个动态地址时, 代理模式将会变为socks5 Dynamic, } @@ -224,7 +232,7 @@ impl Default for Config { crypto: Default::default(), compress: Default::default(), authentication: Authentication::None, - authentication_timeout: default_auth_timeout() + authentication_timeout: default_auth_timeout(), }, features: Default::default(), services: Default::default(), @@ -238,7 +246,7 @@ impl Config { pub fn check(&self) -> error::Result<()> { let mut bind_ports = Vec::new(); - let mut remote_ports = self.server.ports.clone(); + let remote_ports = self.server.ports.clone(); for (name, service) in &self.services { match service { @@ -264,6 +272,15 @@ impl Config { } +/// 默认随机分配一个端口 +fn default_exposes() -> HashSet { + let mut exposes = HashSet::new(); + + exposes.insert(Expose::Tcp(super::IP::V4, 0)); + + exposes +} + #[cfg(test)] #[cfg(feature = "fuso-toml")] mod tests { diff --git a/src/config/mod.rs b/src/config/mod.rs index 8a543c3..9444b1c 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,7 +1,14 @@ -use std::{ops::Deref, sync::Arc}; +use std::{ + fmt::Display, + net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}, + ops::Deref, + sync::Arc, +}; use serde::{Deserialize, Serialize}; +use crate::error; + pub mod client; pub mod server; @@ -38,7 +45,7 @@ pub enum BootKind { #[serde(rename_all = "lowercase")] pub enum Crypto { Aes, - Rsa + Rsa, } #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] @@ -52,14 +59,26 @@ pub struct KeepAlive { interval: u32, } +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub enum IP { + V6, + V4, +} +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +#[serde(into = "String")] +#[serde(try_from = "String")] +pub enum Expose { + Kcp(IP, u16), + Tcp(IP, u16), +} #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] -pub enum RestartPolicy{ +pub enum RestartPolicy { Never, Always, - Counter + Counter, } impl Default for BootKind { @@ -68,23 +87,23 @@ impl Default for BootKind { } } -impl Default for RestartPolicy{ +impl Default for RestartPolicy { fn default() -> Self { RestartPolicy::Always } } -pub struct Stateful{ - pub conf: Arc +pub struct Stateful { + pub conf: Arc, } -impl Stateful{ - pub fn new(c: C) -> Self{ +impl Stateful { + pub fn new(c: C) -> Self { Self { conf: Arc::new(c) } } } -impl Deref for Stateful{ +impl Deref for Stateful { type Target = Arc; fn deref(&self) -> &Self::Target { @@ -92,13 +111,65 @@ impl Deref for Stateful{ } } -impl Clone for Stateful{ +impl Clone for Stateful { fn clone(&self) -> Self { - Stateful { conf: self.conf.clone() } + Stateful { + conf: self.conf.clone(), + } } } +impl TryFrom for Expose { + type Error = error::FusoError; + fn try_from(value: String) -> Result { + let lower = value.to_lowercase(); + + let (port, ty) = match lower.split_once("/") { + None => (value.as_str(), "tcp"), + Some(r) => r, + }; + + let port = u16::from_str_radix(port, 10).map_err(|_| error::FusoError::InvalidPort)?; + + Ok({ + match ty { + "kcp/v6" => Self::Kcp(IP::V6, port), + "tcp/v6" => Self::Tcp(IP::V6, port), + "kcp" | "kcp/v4" => Self::Kcp(IP::V4, port), + "tcp" | "tcp/v4" => Self::Tcp(IP::V4, port), + _ => return Err(error::FusoError::InvalidExposeType), + } + }) + } +} -pub(crate) fn default_auth_timeout() -> u32{ +impl From for String { + fn from(expose: Expose) -> Self { + match expose { + Expose::Kcp(ip, port) => format!("{port}/kcp/{ip}"), + Expose::Tcp(ip, tcp) => format!("{tcp}/tcp/{ip}"), + } + } +} + +impl Display for IP { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + IP::V6 => write!(f, "v6"), + IP::V4 => write!(f, "v4"), + } + } +} + +impl IP { + pub fn to_addr(&self, port: u16) -> SocketAddr { + match self { + IP::V4 => SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port)), + IP::V6 => SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, port, 0, 0)), + } + } +} + +pub(crate) fn default_auth_timeout() -> u32 { 60 -} \ No newline at end of file +} diff --git a/src/core/mod.rs b/src/core/mod.rs index 8bbfa38..87ac27b 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -15,7 +15,6 @@ pub mod future; pub mod handshake; pub mod io; pub mod net; -pub mod port_forward; pub mod processor; pub mod protocol; pub mod rpc; diff --git a/src/core/net/kcp.rs b/src/core/net/kcp.rs index db5446c..e7aadeb 100644 --- a/src/core/net/kcp.rs +++ b/src/core/net/kcp.rs @@ -119,4 +119,4 @@ impl AsyncWrite for KcpStream { } } } -} +} \ No newline at end of file diff --git a/src/core/rpc/callee.rs b/src/core/rpc/callee.rs index e69de29..73a60fd 100644 --- a/src/core/rpc/callee.rs +++ b/src/core/rpc/callee.rs @@ -0,0 +1,26 @@ +use std::marker::PhantomData; + +use crate::core::{channel, rpc::Looper}; + +use super::AsyncCallee; + +pub struct Callee { + _marked: PhantomData, +} + +impl Callee { + pub fn new(stream: S, heartbeat_delay: std::time::Duration) -> Self { + unimplemented!() + } +} + +impl AsyncCallee for Callee { + type Output = O; + + fn poll_next( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + unimplemented!() + } +} diff --git a/src/core/rpc/caller.rs b/src/core/rpc/caller.rs index 53489d9..7d011e2 100644 --- a/src/core/rpc/caller.rs +++ b/src/core/rpc/caller.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use crate::{ core::{ channel::{self, Receiver, Sender}, - future::{LazyFuture, Select}, + future::LazyFuture, protocol::{AsyncPacketRead, AsyncPacketSend}, split::{ReadHalf, WriteHalf}, task::{setter, Setter}, @@ -17,7 +17,7 @@ use crate::{ runtime::Runtime, }; -use super::{AsyncCall, Decoder}; +use super::{AsyncCall, Decoder, Looper}; use crate::core::rpc::Encoder; use crate::core::split::SplitStream; @@ -33,8 +33,6 @@ enum Response { Data { token: u64, data: Vec }, } -pub struct Looper<'a>(Select<'a, error::Result<()>>); - #[derive(Default, Clone)] pub struct Calls { call_list: Arc>>>>, @@ -63,20 +61,21 @@ where let (heart_rx, heart_ax) = channel::open::(); let calls = Calls::default(); - let mut select = Select::new(); - select.add(Looper::run_heartbeat::( + let mut looper = Looper::new(); + + looper.post(Looper::run_heartbeat::( heartbeat_delay, req_rx.clone(), heart_ax, )); - select.add(Looper::run_send_loop(calls.clone(), reader, heart_rx)); + looper.post(Looper::run_send_loop(calls.clone(), reader, heart_rx)); - select.add(Looper::run_recv_loop(calls.clone(), writer, req_ax)); + looper.post(Looper::run_recv_loop(calls.clone(), writer, req_ax)); ( - Looper(select), + looper, Self { calls, request: req_rx, @@ -87,6 +86,7 @@ where } } + impl<'caller, S, T> AsyncCall for Caller where T: serde::Serialize + Send + 'static, diff --git a/src/core/rpc/mod.rs b/src/core/rpc/mod.rs index 6461e19..0209019 100644 --- a/src/core/rpc/mod.rs +++ b/src/core/rpc/mod.rs @@ -4,6 +4,7 @@ mod caller; pub mod structs; use std::{ + future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, @@ -14,6 +15,8 @@ use crate::error; pub use callee::*; pub use caller::*; +use super::future::Select; + #[pin_project::pin_project] pub struct Call<'caller, C, A, O> { arg: A, @@ -28,6 +31,12 @@ pub trait AsyncCall { fn poll_call(self: Pin<&mut Self>, cx: &mut Context<'_>, arg: &T) -> Poll; } +pub trait AsyncCallee { + type Output; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll; +} + pub trait ICallExt: AsyncCall { fn call<'caller>(&'caller mut self, arg: A) -> Call<'caller, Self, A, O> where @@ -52,6 +61,8 @@ pub trait Decoder { impl ICallExt for T where T: AsyncCall {} +pub struct Looper<'looper>(Select<'looper, error::Result<()>>); + impl Encoder for T where T: serde::Serialize, @@ -86,3 +97,16 @@ where Pin::new(&mut **this.caller).poll_call(cx, &this.arg) } } + +impl<'looper> Looper<'looper> { + fn new() -> Self { + Self(Select::new()) + } + + fn post(&mut self, f: F) + where + F: Future> + Send + 'looper, + { + self.0.add(f); + } +} diff --git a/src/core/rpc/structs.rs b/src/core/rpc/structs.rs index 64f1b3c..e58af05 100644 --- a/src/core/rpc/structs.rs +++ b/src/core/rpc/structs.rs @@ -3,11 +3,15 @@ pub mod port_forward { use crate::{config::client::ServerAddr, core::Connection}; + #[derive(Debug, Serialize, Deserialize)] + pub enum Target { + Udp(ServerAddr), + Tcp(ServerAddr), + } + #[derive(Debug, Serialize, Deserialize)] pub enum Request { - Ping, - Pong, - New(u64, Option), + New(u64, Option), } #[derive(Debug, Serialize, Deserialize)] @@ -18,7 +22,7 @@ pub mod port_forward { pub enum WithSocks { Tcp(ServerAddr), - Udp(), + Udp(ServerAddr), } pub enum VisitorProtocol { diff --git a/src/core/stream/handshake.rs b/src/core/stream/handshake.rs index 6988e07..172ea34 100644 --- a/src/core/stream/handshake.rs +++ b/src/core/stream/handshake.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use crate::{ config::{ client::WithForwardService, AuthWithAccount, AuthWithSecret, Authentication, Compress, - Crypto, + Crypto, Expose, }, core::{ protocol::{AsyncPacketRead, AsyncPacketSend}, @@ -29,8 +29,8 @@ pub enum ClientConfig { #[derive(Debug, Serialize, Deserialize)] pub struct ForwardConfig { - pub exposes: Vec, - pub channel: Option, + pub exposes: HashSet, + pub channel: Option>, pub cryptos: HashSet, pub compress: HashSet, } @@ -84,7 +84,7 @@ pub trait Handshake: Stream + Send + Unpin { match auth { Authentication::None => Ok(self), Authentication::Secret(secret) => { - log::debug!("using secret auth"); + log::trace!("using secret auth"); let client_secret: AuthWithSecret = self.recv_packet().await?.decode()?; if client_secret.ne(&secret) { @@ -96,7 +96,7 @@ pub trait Handshake: Stream + Send + Unpin { } } Authentication::Account(account) => { - log::debug!("using account auth"); + log::trace!("using account auth"); let client_account: AuthWithAccount = self.recv_packet().await?.decode()?; if client_account.ne(&account) { diff --git a/src/error/mod.rs b/src/error/mod.rs index 2d2390c..957c4c1 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -9,6 +9,8 @@ pub enum FusoError { Abort, AuthError, BadRpcCall(u64), + InvalidPort, + InvalidExposeType, Bincode(bincode::Error), TomlDeError(toml::de::Error), StdIo(std::io::Error), diff --git a/src/runtime/tokio/kcp.rs b/src/runtime/tokio/kcp.rs index 0ef8ed3..cec9afb 100644 --- a/src/runtime/tokio/kcp.rs +++ b/src/runtime/tokio/kcp.rs @@ -70,10 +70,10 @@ impl kcp_rust::Runner for KcpWithTokioRuntime { type Err = error::FusoError; fn start(process: kcp_rust::Background) -> std::result::Result<(), Self::Err> { - log::debug!("starting {:?}", process.kind()); crate::runtime::tokio::TokioRuntime::spawn(async move{ - process.await.unwrap(); - log::debug!("kcp finished ....") + if let Err(e) = process.await { + log::warn!("{:?}", e); + } }); Ok(()) } diff --git a/src/runtime/tokio/port_forward/mod.rs b/src/runtime/tokio/port_forward/mod.rs index bb69b84..4bed4c8 100644 --- a/src/runtime/tokio/port_forward/mod.rs +++ b/src/runtime/tokio/port_forward/mod.rs @@ -36,11 +36,12 @@ where } } -impl crate::client::port_forward::PortForwarder { - pub fn new(transport: S, connector: C) -> Self - where - S: Stream + Unpin + Send + 'static, - { - Self::new_with_runtime(transport, connector) +impl crate::client::port_forward::PortForwarder +where + S: Stream + Send + Unpin + 'static, +{ + pub fn new(transport: S, connector: C) -> Self { + // Self::new_with_runtime(transport, connector) + unimplemented!() } } diff --git a/src/server/port_forward/accepter.rs b/src/server/port_forward/accepter.rs index 2e2a7f4..67c1c56 100644 --- a/src/server/port_forward/accepter.rs +++ b/src/server/port_forward/accepter.rs @@ -15,6 +15,11 @@ use crate::core::{accepter::Accepter, BoxedStream}; use crate::error; use crate::runtime::Runtime; +enum AcceptWhence { + Visitor, + Mapping, +} + pub struct MuxAccepter { accepter: A, handshaker: Arc, @@ -23,6 +28,7 @@ pub struct MuxAccepter { } pub struct ForwardAccepter { + whence: AcceptWhence, accepter: A, } @@ -122,8 +128,18 @@ where A: Accepter + Unpin + Send, T: Stream + Unpin + 'static, { - pub fn new(accepter: A) -> Self { - Self { accepter } + pub fn new_visitor(accepter: A) -> Self { + Self { + accepter, + whence: AcceptWhence::Visitor, + } + } + + pub fn new_mapping(accepter: A) -> Self { + Self { + accepter, + whence: AcceptWhence::Mapping, + } } } @@ -140,10 +156,16 @@ where ) -> Poll> { match Pin::new(&mut self.accepter).poll_accept(ctx)? { Poll::Pending => Poll::Pending, - Poll::Ready((addr, stream)) => Poll::Ready(Ok(Whence::Mapping(Connection::new( - addr, - BoxedStream::new(stream), - )))), + Poll::Ready((addr, stream)) => Poll::Ready(Ok({ + match self.whence { + AcceptWhence::Visitor => { + Whence::Visitor(Connection::new(addr, BoxedStream::new(stream))) + } + AcceptWhence::Mapping => { + Whence::Mapping(Connection::new(addr, BoxedStream::new(stream))) + } + } + })), } } } diff --git a/src/server/port_forward/mod.rs b/src/server/port_forward/mod.rs index 897a229..969f702 100644 --- a/src/server/port_forward/mod.rs +++ b/src/server/port_forward/mod.rs @@ -1,6 +1,7 @@ mod accepter; mod handshake; mod preprocessor; +mod transport; pub use accepter::*; pub use handshake::*; @@ -14,8 +15,7 @@ use std::{collections::HashMap, pin::Pin, sync::Arc, task::Poll}; use crate::core::future::Poller; use crate::core::io::AsyncReadExt; -use crate::core::port_forward::Transport; -use crate::core::rpc::structs::port_forward; +use crate::core::rpc::structs::port_forward::{self, Target}; use crate::core::rpc::ICallExt; use crate::core::token::IncToken; use crate::core::Stream; @@ -30,6 +30,8 @@ use crate::{ error, }; +use self::transport::Transport; + type Connection = crate::core::Connection<'static>; enum Outcome { @@ -209,10 +211,13 @@ where let mut transport = transport; let (conn, addr) = match preprocessor.prepare(conn).await? { - VisitorProtocol::Other(conn, addr) => (conn, addr), VisitorProtocol::Socks(conn, socks) => match socks { - port_forward::WithSocks::Tcp(addr) => (conn, Some(addr)), - port_forward::WithSocks::Udp() => todo!(), + port_forward::WithSocks::Tcp(addr) => (conn, Some(Target::Tcp(addr))), + port_forward::WithSocks::Udp(addr) => (conn, Some(Target::Udp(addr))), + }, + VisitorProtocol::Other(conn, addr) => match addr { + None => (conn, None), + Some(addr) => (conn, Some(Target::Tcp(addr))), }, }; diff --git a/src/core/port_forward.rs b/src/server/port_forward/transport.rs similarity index 95% rename from src/core/port_forward.rs rename to src/server/port_forward/transport.rs index ed5356f..5fb1bf0 100644 --- a/src/core/port_forward.rs +++ b/src/server/port_forward/transport.rs @@ -1,7 +1,7 @@ use std::pin::Pin; use std::task::Poll; -use crate::core::rpc::{self, Decoder, ICallExt}; +use crate::core::rpc::{self, Decoder}; use crate::core::rpc::{Caller, Looper}; use crate::runtime::Runtime; @@ -11,7 +11,7 @@ use crate::{ structs::port_forward::{Request, Response}, AsyncCall, }, - BoxedFuture, Stream, + Stream, }, error, }; diff --git a/src/server/proxy_tunnel/mod.rs b/src/server/proxy_tunnel/mod.rs index ddb4b52..11a07a6 100644 --- a/src/server/proxy_tunnel/mod.rs +++ b/src/server/proxy_tunnel/mod.rs @@ -7,7 +7,7 @@ impl ProxyTunnel { unimplemented!() } } - + impl Accepter for ProxyTunnel { type Output = error::Result<()>; fn poll_accept( From 44c51f27a5bb10e167e15761ca999415eaeabb3d Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Tue, 9 Apr 2024 00:05:46 +0800 Subject: [PATCH 11/29] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bin/client.rs | 2 +- src/client/port_forward/mod.rs | 8 ++- src/config/mod.rs | 2 +- src/core/channel.rs | 4 +- src/core/rpc/callee.rs | 51 ++++++++++++++++- src/core/rpc/caller.rs | 65 ++++----------------- src/core/rpc/keep.rs | 19 ++++++ src/core/rpc/lopper.rs | 86 ++++++++++++++++++++++++++++ src/core/rpc/mod.rs | 26 +++------ src/server/port_forward/mod.rs | 2 +- src/server/port_forward/transport.rs | 2 +- 11 files changed, 185 insertions(+), 82 deletions(-) create mode 100644 src/core/rpc/keep.rs create mode 100644 src/core/rpc/lopper.rs diff --git a/src/bin/client.rs b/src/bin/client.rs index d159ed6..98167e0 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -111,7 +111,7 @@ async fn enter_forward_service_main( log::error!("fail to handshake {e:?}"); return Ok(Rise::Restart); } - }; + }; stream.write_config(&service).await?; diff --git a/src/client/port_forward/mod.rs b/src/client/port_forward/mod.rs index cc92409..c5d6a1d 100644 --- a/src/client/port_forward/mod.rs +++ b/src/client/port_forward/mod.rs @@ -4,6 +4,7 @@ mod transport; use self::linker::Linker; use crate::core::rpc::{structs::port_forward::Request, AsyncCallee}; use std::{marker::PhantomData, pin::Pin, task::Poll}; +use crate::core::connector::ConnectExt; use crate::{ client::port_forward::transport::Transport, @@ -24,14 +25,19 @@ where { pub fn new_with_runtime(transport: S, connector: C) -> Self where - C: Connector<(), Output = Connection<'static>>, + C: Connector<(), Output = Connection<'static>> + Unpin, { + let mut connector = connector; + + // let (transport, hold) = Transport::new::(std::time::Duration::from_secs(1), transport); unimplemented!() } } + + impl Accepter for PortForwarder where S: Unpin, diff --git a/src/config/mod.rs b/src/config/mod.rs index 9444b1c..19e9089 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -147,7 +147,7 @@ impl From for String { fn from(expose: Expose) -> Self { match expose { Expose::Kcp(ip, port) => format!("{port}/kcp/{ip}"), - Expose::Tcp(ip, tcp) => format!("{tcp}/tcp/{ip}"), + Expose::Tcp(ip, port) => format!("{port}/tcp/{ip}"), } } } diff --git a/src/core/channel.rs b/src/core/channel.rs index 67130b0..0780cc6 100644 --- a/src/core/channel.rs +++ b/src/core/channel.rs @@ -84,9 +84,7 @@ pub fn open() -> (Sender, Receiver) { Sender { container: container.clone(), }, - Receiver { - container: container, - }, + Receiver { container }, ) } diff --git a/src/core/rpc/callee.rs b/src/core/rpc/callee.rs index 73a60fd..a291b65 100644 --- a/src/core/rpc/callee.rs +++ b/src/core/rpc/callee.rs @@ -1,19 +1,36 @@ use std::marker::PhantomData; -use crate::core::{channel, rpc::Looper}; +use crate::{ + core::{ + channel::{self, Receiver, Sender}, + protocol::AsyncPacketRead, + rpc::{lopper::Looper, Looper}, + split::{ReadHalf, WriteHalf}, + Stream, + }, + error, +}; -use super::AsyncCallee; +use super::{structs::port_forward::Request, AsyncCallee, Decoder}; +use crate::core::split::SplitStream; pub struct Callee { _marked: PhantomData, } -impl Callee { +impl Callee +where + S: Stream + Send + Unpin, +{ pub fn new(stream: S, heartbeat_delay: std::time::Duration) -> Self { + let (looper, rx, ax) = Looper::new(stream); + unimplemented!() } } + + impl AsyncCallee for Callee { type Output = O; @@ -24,3 +41,31 @@ impl AsyncCallee for Callee { unimplemented!() } } + +impl Looper<'_> { + async fn run_callee_receiver( + reader: ReadHalf, + sender: Sender, + ) -> error::Result<()> + where + S: Stream + Unpin, + { + let mut reader = reader; + loop { + let pkt = reader.recv_packet().await?; + sender.send(pkt.decode()?).await?; + } + } + + async fn run_callee_sender( + writer: WriteHalf, + receiver: Receiver, + ) -> error::Result<()> + where + S: Stream + Unpin, + { + loop { + let data = receiver.recv().await?; + } + } +} diff --git a/src/core/rpc/caller.rs b/src/core/rpc/caller.rs index 7d011e2..f3edb4c 100644 --- a/src/core/rpc/caller.rs +++ b/src/core/rpc/caller.rs @@ -17,22 +17,10 @@ use crate::{ runtime::Runtime, }; -use super::{AsyncCall, Decoder, Looper}; +use super::{lopper::Looper, AsyncCall, Cmd, Decoder}; use crate::core::rpc::Encoder; use crate::core::split::SplitStream; -#[derive(Debug, Serialize, Deserialize)] -enum Request { - Ping, - Data { token: u64, data: Vec }, -} - -#[derive(Debug, Serialize, Deserialize)] -enum Response { - Pong, - Data { token: u64, data: Vec }, -} - #[derive(Default, Clone)] pub struct Calls { call_list: Arc>>>>, @@ -42,7 +30,7 @@ pub struct Calls { #[pin_project::pin_project] pub struct Caller { calls: Calls, - request: Sender, + request: Sender, #[pin] fut_call: Arc>>>>, marked: PhantomData, @@ -57,7 +45,7 @@ where R: Runtime + 'a, { let (reader, writer) = stream.split(); - let (req_rx, req_ax) = channel::open::(); + let (req_rx, req_ax) = channel::open::(); let (heart_rx, heart_ax) = channel::open::(); let calls = Calls::default(); @@ -86,7 +74,6 @@ where } } - impl<'caller, S, T> AsyncCall for Caller where T: serde::Serialize + Send + 'static, @@ -109,8 +96,8 @@ where let token = this.calls.add(setter); let result = match data { - Ok(data) => { - this.request.send_sync(Request::Data { token, data }); + Ok(packet) => { + this.request.send_sync(Cmd::Transact { token, packet }); Ok(()) } Err(error) => { @@ -154,44 +141,14 @@ impl<'a> Looper<'a> { } } - async fn run_recv_loop( - calls: Calls, - writer: WriteHalf, - receiver: Receiver, - ) -> error::Result<()> - where - S: Stream + Unpin, - { - let mut writer = writer; - loop { - let pkt = receiver.recv().await?.encode()?; - if let Err(_) = writer.send_packet(&pkt).await { - calls.cancel_all(); - }; - } - } - - async fn run_send_loop( - calls: Calls, - reader: ReadHalf, - sender: Sender, - ) -> error::Result<()> - where - S: Stream + Unpin, - { - let mut reader = reader; - loop { - let data = reader.recv_packet().await?; - let response: Response = data.decode()?; + - match response { - Response::Pong => sender.send(Response::Pong).await, - response => calls.wake(response), - }?; - } - } + } + + + impl<'a> std::future::Future for Looper<'a> { type Output = error::Result<()>; fn poll( @@ -213,7 +170,7 @@ impl Calls { token } - fn wake(&self, resp: Response) -> error::Result<()> { + fn wake(&self, token: u64, packet: Vec) -> error::Result<()> { match resp { Response::Pong => unsafe { std::hint::unreachable_unchecked() }, Response::Data { token, data } => match self.call_list.lock().remove(&token) { diff --git a/src/core/rpc/keep.rs b/src/core/rpc/keep.rs new file mode 100644 index 0000000..febfeae --- /dev/null +++ b/src/core/rpc/keep.rs @@ -0,0 +1,19 @@ +use crate::{core::{channel::Sender, future::Poller}, error}; + +use super::Cmd; + +#[derive(Clone)] +pub struct Heartbeat {} + + +impl Heartbeat { + pub(super) fn new<'a>(sender: Sender) -> (Poller<'a, error::Result<()>>, Self) { + unimplemented!() + } + + pub(super) fn pong(&self) {} + + pub(super) fn ping(&self) {} + + pub(super) fn interrupt(&self) {} +} diff --git a/src/core/rpc/lopper.rs b/src/core/rpc/lopper.rs new file mode 100644 index 0000000..62e0ef5 --- /dev/null +++ b/src/core/rpc/lopper.rs @@ -0,0 +1,86 @@ +use std::future::Future; + +use crate::{ + core::{ + channel::{self, Receiver, Sender}, + future::Select, + protocol::{AsyncPacketRead, AsyncPacketSend}, + split::{ReadHalf, WriteHalf}, + Stream, + }, + error, +}; + +use super::{keep::Heartbeat, Cmd, Decoder, Encoder}; +use crate::core::split::SplitStream; + +pub struct Looper<'looper>(Select<'looper, error::Result<()>>); + +impl<'looper> Looper<'looper> { + pub(super) fn new(stream: S) -> (Self, Sender, Receiver<(u64, Vec)>) + where + S: Stream + Unpin + Send + 'looper, + { + let (prx, pax) = channel::open(); + let (psrx, psax) = channel::open(); + + let (poller, heartbeat) = Heartbeat::new(prx.clone()); + + let (reader, writer) = stream.split(); + + let mut select = Select::new(); + + select.add(poller); + select.add(Self::run_recv_loop(writer, pax)); + select.add(Self::run_send_loop(reader, psrx, heartbeat)); + + (Self(select), prx, psax) + } + + pub(super) fn post(&mut self, f: F) + where + F: Future> + Send + 'looper, + { + self.0.add(f); + } +} + +impl<'looper> Looper<'looper> { + pub(super) async fn run_send_loop( + reader: ReadHalf, + sender: Sender<(u64, Vec)>, + heartbeat: Heartbeat, + ) -> error::Result<()> + where + S: Stream + Unpin, + { + let mut reader = reader; + + loop { + let data = reader.recv_packet().await?; + let cmd: Cmd = data.decode()?; + match cmd { + Cmd::Pong => heartbeat.pong(), + Cmd::Ping => heartbeat.ping(), + Cmd::Transact { token, packet } => { + heartbeat.interrupt(); + sender.send((token, packet)).await?; + } + }; + } + } + + pub(super) async fn run_recv_loop( + writer: WriteHalf, + receiver: Receiver, + ) -> error::Result<()> + where + S: Stream + Unpin, + { + let mut writer = writer; + loop { + let pkt = receiver.recv().await?.encode()?; + writer.send_packet(&pkt).await?; + } + } +} diff --git a/src/core/rpc/mod.rs b/src/core/rpc/mod.rs index 0209019..1c9e08c 100644 --- a/src/core/rpc/mod.rs +++ b/src/core/rpc/mod.rs @@ -1,10 +1,11 @@ mod callee; mod caller; +mod keep; +mod lopper; pub mod structs; use std::{ - future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, @@ -14,8 +15,14 @@ use crate::error; pub use callee::*; pub use caller::*; +use serde::{Deserialize, Serialize}; -use super::future::Select; +#[derive(Debug, Serialize, Deserialize)] +enum Cmd { + Ping, + Pong, + Transact { token: u64, packet: Vec }, +} #[pin_project::pin_project] pub struct Call<'caller, C, A, O> { @@ -61,8 +68,6 @@ pub trait Decoder { impl ICallExt for T where T: AsyncCall {} -pub struct Looper<'looper>(Select<'looper, error::Result<()>>); - impl Encoder for T where T: serde::Serialize, @@ -97,16 +102,3 @@ where Pin::new(&mut **this.caller).poll_call(cx, &this.arg) } } - -impl<'looper> Looper<'looper> { - fn new() -> Self { - Self(Select::new()) - } - - fn post(&mut self, f: F) - where - F: Future> + Send + 'looper, - { - self.0.add(f); - } -} diff --git a/src/server/port_forward/mod.rs b/src/server/port_forward/mod.rs index 969f702..e6f70d6 100644 --- a/src/server/port_forward/mod.rs +++ b/src/server/port_forward/mod.rs @@ -30,7 +30,7 @@ use crate::{ error, }; -use self::transport::Transport; +use transport::Transport; type Connection = crate::core::Connection<'static>; diff --git a/src/server/port_forward/transport.rs b/src/server/port_forward/transport.rs index 5fb1bf0..2b1d366 100644 --- a/src/server/port_forward/transport.rs +++ b/src/server/port_forward/transport.rs @@ -70,4 +70,4 @@ impl std::future::Future for TransportHold { ) -> std::task::Poll { Pin::new(&mut self.0).poll(cx) } -} +} \ No newline at end of file From 63249c515a12965c0781701784650d8d24bacf6b Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Tue, 23 Apr 2024 23:23:57 +0800 Subject: [PATCH 12/29] =?UTF-8?q?=E5=AE=8C=E5=96=84=E9=83=A8=E5=88=86?= =?UTF-8?q?=E7=AB=AF=E5=8F=A3=E8=BD=AC=E5=8F=91=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/client.toml | 2 +- src/bin/client.rs | 10 +--- src/client/port_forward/linker.rs | 24 ++++++-- src/client/port_forward/mod.rs | 50 +++++++++------- src/client/port_forward/transport.rs | 57 ++++++++++++------ src/config/server.rs | 1 - src/core/channel.rs | 9 ++- src/core/connector.rs | 9 +++ src/core/rpc/callee.rs | 81 ++++++++++++-------------- src/core/rpc/caller.rs | 82 ++++++-------------------- src/core/rpc/keep.rs | 8 ++- src/core/rpc/lopper.rs | 12 +++- src/core/rpc/mod.rs | 2 + src/core/rpc/structs.rs | 15 ++--- src/core/task/mod.rs | 83 +++++++++++++++++++++------ src/core/token.rs | 4 +- src/error/mod.rs | 11 +++- src/runtime/tokio/port_forward/mod.rs | 12 ++-- src/server/port_forward/mod.rs | 46 ++++++++++----- src/server/port_forward/transport.rs | 2 +- 20 files changed, 310 insertions(+), 210 deletions(-) diff --git a/config/client.toml b/config/client.toml index 15e5c34..d861204 100644 --- a/config/client.toml +++ b/config/client.toml @@ -13,7 +13,7 @@ auth = { type = "secret", secret = "12345678" } type = "forward" boot = "fork" exposes = [ "8080/tcp" ] -channel = [ "8002/tcp", "8002/kcp" ] +channel = [ "8002/tcp" ] restart = "always" target = { type = "static", port = 2222, addr = ["127.0.0.1"] } keep_alive = { interval = 100 } diff --git a/src/bin/client.rs b/src/bin/client.rs index 98167e0..4c39e4e 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -17,7 +17,7 @@ use fuso::{ net::{TcpListener, TcpStream}, protocol::{AsyncPacketRead, AsyncPacketSend}, rpc::{AsyncCall, Caller}, - stream::{handshake::Handshake, UseCompress, UseCrypto}, + stream::{handshake::Handshake, UseCompress, UseCrypto}, Connection, }, error, runner::{FnRunnable, NamedRunnable, Rise, ServiceRunner}, @@ -115,7 +115,7 @@ async fn enter_forward_service_main( stream.write_config(&service).await?; - let mut connector = MultiConnector::::new(); + let mut connector = MultiConnector::<(), Connection<'static>>::new(); if let Some(channel) = service.channel.as_ref() { // connector.add(connector) @@ -126,11 +126,7 @@ async fn enter_forward_service_main( loop { let (linker, target) = forwarder.accept().await?; - tokio::spawn(async move { - - - let a = linker.link().await; - }); + log::debug!("connect to {}", linker); } } diff --git a/src/client/port_forward/linker.rs b/src/client/port_forward/linker.rs index 4397f68..5402ae2 100644 --- a/src/client/port_forward/linker.rs +++ b/src/client/port_forward/linker.rs @@ -1,11 +1,20 @@ -use crate::{core::Connection, error}; - -pub struct Linker {} - +use std::fmt::Display; +use crate::{ + core::{rpc::Responder, Connection}, + error, +}; +pub struct Linker { + token: u64, + responder: Responder, +} impl Linker { + pub fn new(token: u64, responder: Responder) -> Self { + Self { token, responder } + } + pub async fn link(self) -> error::Result<()> { unimplemented!() } @@ -14,3 +23,10 @@ impl Linker { unimplemented!() } } + + +impl Display for Linker{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "token={}", self.token) + } +} \ No newline at end of file diff --git a/src/client/port_forward/mod.rs b/src/client/port_forward/mod.rs index c5d6a1d..44886de 100644 --- a/src/client/port_forward/mod.rs +++ b/src/client/port_forward/mod.rs @@ -2,9 +2,15 @@ mod linker; mod transport; use self::linker::Linker; -use crate::core::rpc::{structs::port_forward::Request, AsyncCallee}; + +use crate::{ + core::{ + connector::BoxedConnector, + rpc::{structs::port_forward::Request, AsyncCallee}, + }, + error, +}; use std::{marker::PhantomData, pin::Pin, task::Poll}; -use crate::core::connector::ConnectExt; use crate::{ client::port_forward::transport::Transport, @@ -14,7 +20,8 @@ use crate::{ }; pub struct PortForwarder { - transport: Transport, + transport: Transport<'static, S>, + connector: BoxedConnector<'static, (), Connection<'static>>, _marked: PhantomData, } @@ -25,22 +32,22 @@ where { pub fn new_with_runtime(transport: S, connector: C) -> Self where - C: Connector<(), Output = Connection<'static>> + Unpin, + C: Connector<(), Output = Connection<'static>> + Send + Unpin + 'static, { - let mut connector = connector; - + let transport = Transport::new::(std::time::Duration::from_secs(1), transport); - // let (transport, hold) = Transport::new::(std::time::Duration::from_secs(1), transport); - - unimplemented!() + Self { + transport, + connector: BoxedConnector::new(connector), + _marked: PhantomData, + } } } - - impl Accepter for PortForwarder where - S: Unpin, + R: Unpin, + S: Stream + Send + Unpin, { type Output = (Linker, FinalTarget); @@ -48,13 +55,16 @@ where mut self: std::pin::Pin<&mut Self>, ctx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - // match Pin::new(&mut self.transport).poll_next(ctx) { - // std::task::Poll::Pending => Poll::Pending, - // std::task::Poll::Ready(Request::New(addr, a)) => { - // unimplemented!() - // }, - // }; - - unimplemented!() + match Pin::new(&mut self.transport).poll_next(ctx)? { + Poll::Pending => Poll::Pending, + Poll::Ready((request, responder)) => match request { + Request::New(token, target) => Poll::Ready(Ok((Linker::new(token, responder), { + match target { + None => FinalTarget::Dynamic, + Some(target) => FinalTarget::Dynamic, + } + }))), + }, + } } } diff --git a/src/client/port_forward/transport.rs b/src/client/port_forward/transport.rs index 6491fe7..3347057 100644 --- a/src/client/port_forward/transport.rs +++ b/src/client/port_forward/transport.rs @@ -1,30 +1,55 @@ -use std::marker::PhantomData; +use std::{future::Future, marker::PhantomData, pin::Pin}; -use crate::core::{ - rpc::{structs::port_forward::Request, AsyncCallee}, - Stream, +use crate::{ + core::{ + rpc::{ + self, structs::port_forward::Request, AsyncCallee, Callee, Caller, Decoder, Looper, + Responder, + }, + Stream, + }, + error, }; -pub struct Transport { - _marked: PhantomData +pub struct Transport<'transport, S> { + callee: Callee<'transport>, + lopper: Looper<'transport>, + _marked: PhantomData, } -impl Transport { - pub fn new(heartbeat_delay: std::time::Duration, stream: S) -> Self - where - S: Stream + Send + Unpin, - { - unimplemented!() +impl<'transport, S> Transport<'transport, S> +where + S: Stream + Send + Unpin + 'transport, +{ + pub fn new(heartbeat_delay: std::time::Duration, stream: S) -> Self { + let (lopper, callee) = rpc::Callee::new(stream, heartbeat_delay); + + Self { + callee, + lopper, + _marked: PhantomData, + } } } +impl<'transport, S> AsyncCallee for Transport<'transport, S> +where + S: Stream + Send + Unpin, +{ + type Output = error::Result<(Request, Responder)>; -impl AsyncCallee for Transport { - type Output = Request; fn poll_next( - self: std::pin::Pin<&mut Self>, + mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll { - unimplemented!() + match Pin::new(&mut self.lopper).poll(cx)? { + std::task::Poll::Ready(()) => std::task::Poll::Ready(Err(error::FusoError::Abort)), + std::task::Poll::Pending => match Pin::new(&mut self.callee).poll_next(cx)? { + std::task::Poll::Pending => std::task::Poll::Pending, + std::task::Poll::Ready((packet, responder)) => { + std::task::Poll::Ready(Ok((packet.decode()?, responder))) + } + }, + } } } diff --git a/src/config/server.rs b/src/config/server.rs index c97a920..9539d8d 100644 --- a/src/config/server.rs +++ b/src/config/server.rs @@ -71,7 +71,6 @@ pub struct KcpListenMetadata { impl ListenMetadata { pub fn as_socket_addr(self) -> SocketAddr { - log::debug!("{}:{}", self.bind, self.port); SocketAddr::new(self.bind, self.port) } } diff --git a/src/core/channel.rs b/src/core/channel.rs index 0780cc6..06795d4 100644 --- a/src/core/channel.rs +++ b/src/core/channel.rs @@ -1,7 +1,7 @@ use std::{ collections::VecDeque, sync::Arc, - task::{Poll, Waker}, + task::{Context, Poll, Waker}, }; use parking_lot::Mutex; @@ -36,6 +36,13 @@ impl Receiver { receiver: &self.container, } } + + pub fn poll_recv(&self, cx: &mut Context<'_>) -> Poll> { + match self.container.take(cx.waker()) { + None => Poll::Pending, + Some(t) => Poll::Ready(Ok(t)), + } + } } impl Sender { diff --git a/src/core/connector.rs b/src/core/connector.rs index 53745c8..ab4dd84 100644 --- a/src/core/connector.rs +++ b/src/core/connector.rs @@ -44,6 +44,15 @@ pub struct MultiConnector<'a, T, O> { connectors: Vec>, } +impl<'connector, T, O> BoxedConnector<'connector, T, O> { + pub fn new(connector: C) -> Self + where + C: Connector + Send + Unpin + 'connector, + { + Self(Box::new(connector)) + } +} + impl<'a, T, O> MultiConnector<'a, T, O> { pub fn new() -> Self { Self { diff --git a/src/core/rpc/callee.rs b/src/core/rpc/callee.rs index a291b65..51082cc 100644 --- a/src/core/rpc/callee.rs +++ b/src/core/rpc/callee.rs @@ -1,71 +1,64 @@ -use std::marker::PhantomData; +use std::{marker::PhantomData, task::Poll}; use crate::{ core::{ channel::{self, Receiver, Sender}, protocol::AsyncPacketRead, - rpc::{lopper::Looper, Looper}, + rpc::lopper::Looper, split::{ReadHalf, WriteHalf}, Stream, }, error, }; -use super::{structs::port_forward::Request, AsyncCallee, Decoder}; +use super::{structs::port_forward::Request, AsyncCallee, Cmd, Decoder}; use crate::core::split::SplitStream; -pub struct Callee { - _marked: PhantomData, +pub struct Callee<'looper> { + requester: Receiver<(u64, Vec)>, + responder: Sender, + _marked: PhantomData<&'looper ()>, } -impl Callee -where - S: Stream + Send + Unpin, -{ - pub fn new(stream: S, heartbeat_delay: std::time::Duration) -> Self { +pub struct Responder { + token: u64, + responder: Sender, +} + +impl<'looper> Callee<'looper> { + pub fn new(stream: S, heartbeat_delay: std::time::Duration) -> (Looper<'looper>, Self) + where + S: Stream + Send + Unpin + 'looper, + { let (looper, rx, ax) = Looper::new(stream); - unimplemented!() + ( + looper, + Self { + requester: ax, + responder: rx, + _marked: PhantomData, + }, + ) } } - - -impl AsyncCallee for Callee { - type Output = O; +impl AsyncCallee for Callee<'_> { + type Output = error::Result<(Vec, Responder)>; fn poll_next( self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - unimplemented!() - } -} - -impl Looper<'_> { - async fn run_callee_receiver( - reader: ReadHalf, - sender: Sender, - ) -> error::Result<()> - where - S: Stream + Unpin, - { - let mut reader = reader; - loop { - let pkt = reader.recv_packet().await?; - sender.send(pkt.decode()?).await?; - } - } - - async fn run_callee_sender( - writer: WriteHalf, - receiver: Receiver, - ) -> error::Result<()> - where - S: Stream + Unpin, - { - loop { - let data = receiver.recv().await?; + ) -> Poll { + match self.requester.poll_recv(cx)? { + Poll::Pending => Poll::Pending, + Poll::Ready((token, packet)) => Poll::Ready(Ok(( + packet, + Responder { + token, + responder: self.responder.clone(), + }, + ))), } } } diff --git a/src/core/rpc/caller.rs b/src/core/rpc/caller.rs index f3edb4c..ee8c2aa 100644 --- a/src/core/rpc/caller.rs +++ b/src/core/rpc/caller.rs @@ -1,14 +1,11 @@ use std::{collections::HashMap, marker::PhantomData, pin::Pin, sync::Arc}; use parking_lot::Mutex; -use serde::{Deserialize, Serialize}; use crate::{ core::{ - channel::{self, Receiver, Sender}, + channel::{Receiver, Sender}, future::LazyFuture, - protocol::{AsyncPacketRead, AsyncPacketSend}, - split::{ReadHalf, WriteHalf}, task::{setter, Setter}, token::IncToken, Stream, @@ -17,7 +14,7 @@ use crate::{ runtime::Runtime, }; -use super::{lopper::Looper, AsyncCall, Cmd, Decoder}; +use super::{lopper::Looper, AsyncCall, Cmd}; use crate::core::rpc::Encoder; use crate::core::split::SplitStream; @@ -32,7 +29,7 @@ pub struct Caller { calls: Calls, request: Sender, #[pin] - fut_call: Arc>>>>, + caller: Arc>>>>, marked: PhantomData, } @@ -44,30 +41,18 @@ where where R: Runtime + 'a, { - let (reader, writer) = stream.split(); - let (req_rx, req_ax) = channel::open::(); - let (heart_rx, heart_ax) = channel::open::(); - let calls = Calls::default(); - let mut looper = Looper::new(); - - looper.post(Looper::run_heartbeat::( - heartbeat_delay, - req_rx.clone(), - heart_ax, - )); - - looper.post(Looper::run_send_loop(calls.clone(), reader, heart_rx)); + let (mut looper, sender, receiver) = Looper::new(stream); - looper.post(Looper::run_recv_loop(calls.clone(), writer, req_ax)); + looper.post(Looper::run_command_consumer::(receiver, calls.clone())); ( looper, Self { calls, - request: req_rx, - fut_call: Arc::new(Mutex::new(LazyFuture::new())), + request: sender, + caller: Arc::new(Mutex::new(LazyFuture::new())), marked: PhantomData, }, ) @@ -88,9 +73,9 @@ where ) -> std::task::Poll { let this = self.project(); - let mut fut_call = this.fut_call.lock(); + let mut caller = this.caller.lock(); - Pin::new(&mut fut_call).poll(cx, move || { + Pin::new(&mut caller).poll(cx, move || { let data = arg.encode(); let (setter, getter) = setter(); let token = this.calls.add(setter); @@ -117,46 +102,18 @@ where } impl<'a> Looper<'a> { - async fn run_heartbeat( - delay: std::time::Duration, - sender: Sender, - receiver: Receiver, + async fn run_command_consumer( + receiver: Receiver<(u64, Vec)>, + calls: Calls, ) -> error::Result<()> where R: Runtime, { - let mut last = std::time::Instant::now(); - loop { - sender.send(Request::Ping).await?; - - match R::wait_for(delay, receiver.recv()).await?? { - Response::Pong => { - last = std::time::Instant::now(); - } - _ => unsafe { std::hint::unreachable_unchecked() }, - } - - R::sleep(delay).await; + let (token, packet) = receiver.recv().await?; + calls.wake(token, packet)?; } } - - - - -} - - - - -impl<'a> std::future::Future for Looper<'a> { - type Output = error::Result<()>; - fn poll( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - Pin::new(&mut self.0).poll(cx) - } } impl Calls { @@ -171,12 +128,9 @@ impl Calls { } fn wake(&self, token: u64, packet: Vec) -> error::Result<()> { - match resp { - Response::Pong => unsafe { std::hint::unreachable_unchecked() }, - Response::Data { token, data } => match self.call_list.lock().remove(&token) { - None => Err(error::FusoError::BadRpcCall(token)), - Some(setter) => setter.set(data), - }, + match self.call_list.lock().remove(&token) { + None => Err(error::FusoError::BadRpcCall(token)), + Some(setter) => setter.set(packet), } } @@ -195,7 +149,7 @@ impl Clone for Caller { calls: self.calls.clone(), request: self.request.clone(), marked: PhantomData, - fut_call: self.fut_call.clone(), + caller: self.caller.clone(), } } } diff --git a/src/core/rpc/keep.rs b/src/core/rpc/keep.rs index febfeae..1426473 100644 --- a/src/core/rpc/keep.rs +++ b/src/core/rpc/keep.rs @@ -1,14 +1,16 @@ -use crate::{core::{channel::Sender, future::Poller}, error}; +use crate::{ + core::{channel::Sender, future::Poller}, + error, +}; use super::Cmd; #[derive(Clone)] pub struct Heartbeat {} - impl Heartbeat { pub(super) fn new<'a>(sender: Sender) -> (Poller<'a, error::Result<()>>, Self) { - unimplemented!() + (Poller::new(), Self {}) } pub(super) fn pong(&self) {} diff --git a/src/core/rpc/lopper.rs b/src/core/rpc/lopper.rs index 62e0ef5..5a0b507 100644 --- a/src/core/rpc/lopper.rs +++ b/src/core/rpc/lopper.rs @@ -1,4 +1,4 @@ -use std::future::Future; +use std::{future::Future, pin::Pin}; use crate::{ core::{ @@ -45,6 +45,16 @@ impl<'looper> Looper<'looper> { } } +impl<'a> std::future::Future for Looper<'a> { + type Output = error::Result<()>; + fn poll( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + Pin::new(&mut self.0).poll(cx) + } +} + impl<'looper> Looper<'looper> { pub(super) async fn run_send_loop( reader: ReadHalf, diff --git a/src/core/rpc/mod.rs b/src/core/rpc/mod.rs index 1c9e08c..3d0a678 100644 --- a/src/core/rpc/mod.rs +++ b/src/core/rpc/mod.rs @@ -15,6 +15,8 @@ use crate::error; pub use callee::*; pub use caller::*; +pub use lopper::*; + use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] diff --git a/src/core/rpc/structs.rs b/src/core/rpc/structs.rs index e58af05..55ea329 100644 --- a/src/core/rpc/structs.rs +++ b/src/core/rpc/structs.rs @@ -5,8 +5,8 @@ pub mod port_forward { #[derive(Debug, Serialize, Deserialize)] pub enum Target { - Udp(ServerAddr), - Tcp(ServerAddr), + Udp(ServerAddr, u16), + Tcp(ServerAddr, u16), } #[derive(Debug, Serialize, Deserialize)] @@ -17,16 +17,11 @@ pub mod port_forward { #[derive(Debug, Serialize, Deserialize)] pub enum Response { Ok, - Error(), - } - - pub enum WithSocks { - Tcp(ServerAddr), - Udp(ServerAddr), + Error(String), } pub enum VisitorProtocol { - Socks(Connection<'static>, WithSocks), - Other(Connection<'static>, Option), + Socks(Connection<'static>, Target), + Other(Connection<'static>, Option), } } diff --git a/src/core/task/mod.rs b/src/core/task/mod.rs index a2f0986..23c4b9b 100644 --- a/src/core/task/mod.rs +++ b/src/core/task/mod.rs @@ -1,4 +1,7 @@ -use std::{sync::Arc, task::Waker}; +use std::{ + sync::Arc, + task::{Poll, Waker}, +}; use parking_lot::Mutex; @@ -7,27 +10,44 @@ use crate::error; mod pool; pub enum ValState { - Ok(V), - Nil, - Invalid + Ok(V), + Nil, + Invalid, } pub struct Getter { - val: Arc>>>, - waker: Arc>, + val: Arc>>, + waker: Arc>>, } pub struct Setter { - val: Arc>>>, - waker: Arc>, + val: Arc>>, + waker: Arc>>, } impl Setter { - pub fn set(self, val: V) -> error::Result<()> { - unimplemented!() + pub fn set(mut self, val: V) -> error::Result<()> { + drop(self.val.lock().replace(val)); + self.try_wake(); + Ok(()) } - pub fn invalid(self) {} + pub fn invalid(&mut self) { + self.val.lock().invalid(); + self.try_wake(); + } + + fn try_wake(&mut self) { + if let Some(waker) = self.waker.lock().take() { + waker.wake(); + }; + } +} + +impl Drop for Setter { + fn drop(&mut self) { + self.invalid(); + } } impl std::future::Future for Getter { @@ -36,15 +56,46 @@ impl std::future::Future for Getter { self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll { - let val = self.val.lock(); + let mut val = self.val.lock(); + match val.take()? { + Some(v) => Poll::Ready(Ok(v)), + None => { + self.waker.lock().replace(cx.waker().clone()); + Poll::Pending + } + } + } +} - +impl ValState { + fn take(&mut self) -> error::Result> { + match std::mem::replace(self, Self::Nil) { + ValState::Nil => Ok(None), + ValState::Ok(v) => Ok(Some(v)), + ValState::Invalid => Err(error::FusoError::InvaledSetter), + } + } - unimplemented!() + fn replace(&mut self, val: V) -> Self { + std::mem::replace(self, Self::Ok(val)) } -} + fn invalid(&mut self) { + drop(std::mem::replace(self, Self::Invalid)); + } +} pub fn setter() -> (Setter, Getter) { - unimplemented!() + let val = Arc::new(Mutex::new(ValState::Nil)); + + ( + Setter { + val: val.clone(), + waker: Default::default(), + }, + Getter { + val, + waker: Default::default(), + }, + ) } diff --git a/src/core/token.rs b/src/core/token.rs index 81a1ad1..143f1ff 100644 --- a/src/core/token.rs +++ b/src/core/token.rs @@ -22,9 +22,9 @@ impl IncToken { let cur_tok = *inc_token; let (cur_tok, overflow) = if f(cur_tok) { - cur_tok.overflowing_add(1) - } else { break cur_tok; + } else { + cur_tok.overflowing_add(1) }; if overflow { diff --git a/src/error/mod.rs b/src/error/mod.rs index 957c4c1..8addd22 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -8,9 +8,12 @@ pub enum FusoError { Timeout, Abort, AuthError, + InvaledSetter, BadRpcCall(u64), InvalidPort, InvalidExposeType, + NotResponse, + Custom(String), Bincode(bincode::Error), TomlDeError(toml::de::Error), StdIo(std::io::Error), @@ -28,12 +31,18 @@ impl From for FusoError { } } -impl From for FusoError{ +impl From for FusoError { fn from(value: bincode::Error) -> Self { Self::Bincode(value) } } +impl From for FusoError { + fn from(value: String) -> Self { + Self::Custom(value) + } +} + impl Display for FusoError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self) diff --git a/src/runtime/tokio/port_forward/mod.rs b/src/runtime/tokio/port_forward/mod.rs index 4bed4c8..0730f22 100644 --- a/src/runtime/tokio/port_forward/mod.rs +++ b/src/runtime/tokio/port_forward/mod.rs @@ -2,8 +2,8 @@ use std::net::SocketAddr; use crate::{ core::{ - accepter::Accepter, processor::Preprocessor, rpc::structs::port_forward::VisitorProtocol, - BoxedStream, Connection, Stream, + accepter::Accepter, connector::Connector, processor::Preprocessor, + rpc::structs::port_forward::VisitorProtocol, BoxedStream, Connection, Stream, }, error, server::port_forward::{MuxAccepter, Whence}, @@ -40,8 +40,10 @@ impl crate::client::port_forward::PortForwarder where S: Stream + Send + Unpin + 'static, { - pub fn new(transport: S, connector: C) -> Self { - // Self::new_with_runtime(transport, connector) - unimplemented!() + pub fn new(transport: S, connector: C) -> Self + where + C: Connector<(), Output = Connection<'static>> + Send + Unpin + 'static, + { + Self::new_with_runtime(transport, connector) } } diff --git a/src/server/port_forward/mod.rs b/src/server/port_forward/mod.rs index e6f70d6..90b1e76 100644 --- a/src/server/port_forward/mod.rs +++ b/src/server/port_forward/mod.rs @@ -38,6 +38,7 @@ enum Outcome { Ready(u64, Connection), Pending(u64), Timeout(u64), + Transport(u64, error::FusoError), Stopped(Option), } @@ -112,7 +113,10 @@ where let (need_poll, outcome) = match Pin::new(&mut self.poller).poll(ctx) { Poll::Pending => continue, Poll::Ready(Ok(o)) => (false, o), - Poll::Ready(Err(_)) => continue, + Poll::Ready(Err(e)) => { + log::error!("{:?}", e); + continue; + } }; polled = need_poll; @@ -147,6 +151,16 @@ where } } } + Outcome::Transport(token, transport) => { + if let Some(conn) = self.visitors.take(token) { + log::warn!( + "failed to create mapping {{ token={}, addr={}, msg='{}' }}", + token, + conn.addr(), + transport + ); + } + } }; } @@ -211,13 +225,10 @@ where let mut transport = transport; let (conn, addr) = match preprocessor.prepare(conn).await? { - VisitorProtocol::Socks(conn, socks) => match socks { - port_forward::WithSocks::Tcp(addr) => (conn, Some(Target::Tcp(addr))), - port_forward::WithSocks::Udp(addr) => (conn, Some(Target::Udp(addr))), - }, + VisitorProtocol::Socks(conn, socks) => (conn, Some(socks)), VisitorProtocol::Other(conn, addr) => match addr { None => (conn, None), - Some(addr) => (conn, Some(Target::Tcp(addr))), + Some(target) => (conn, Some(target)), }, }; @@ -225,15 +236,24 @@ where let token = visitors.store(conn); - match transport.call(Request::New(token, addr)).await { - Err(_) => {} - Ok(resp) => match resp { - port_forward::Response::Ok => {} - port_forward::Response::Error() => todo!(), + let result = R::wait_for(std::time::Duration::from_secs(10), async move { + match transport.call(Request::New(token, addr)).await { + Err(e) => Err(e), + Ok(resp) => match resp { + port_forward::Response::Ok => Ok(()), + port_forward::Response::Error(msg) => Err(msg.into()), + }, + } + }) + .await; + + match result { + Err(_) => Ok(Outcome::Transport(token, error::FusoError::NotResponse)), + Ok(r) => match r { + Ok(_) => Ok(Outcome::Pending(token)), + Err(e) => Ok(Outcome::Transport(token, e)), }, } - - Ok(Outcome::Pending(token)) } async fn do_prepare_mapping( diff --git a/src/server/port_forward/transport.rs b/src/server/port_forward/transport.rs index 2b1d366..5fb1bf0 100644 --- a/src/server/port_forward/transport.rs +++ b/src/server/port_forward/transport.rs @@ -70,4 +70,4 @@ impl std::future::Future for TransportHold { ) -> std::task::Poll { Pin::new(&mut self.0).poll(cx) } -} \ No newline at end of file +} From 6baae7dfc958b9a38662504969836d0162d18e66 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Tue, 23 Apr 2024 23:44:04 +0800 Subject: [PATCH 13/29] =?UTF-8?q?=E9=87=8D=E5=91=BD=E5=90=8D=E7=AC=A6?= =?UTF-8?q?=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bin/server.rs | 4 +-- src/client/port_forward/mod.rs | 6 ++--- src/core/accepter.rs | 20 +++++++-------- src/core/connector.rs | 10 ++++---- src/core/mod.rs | 16 ++++++------ src/core/net/tcp.rs | 12 ++++----- src/core/net/udp.rs | 8 +++--- src/core/processor.rs | 8 +++--- src/core/stream/codec.rs | 36 +++++++++++++-------------- src/core/stream/compress/lz4/mod.rs | 4 +-- src/core/stream/compress/mod.rs | 20 +++++++-------- src/core/stream/crypto/aes/mod.rs | 4 +-- src/core/stream/crypto/mod.rs | 30 +++++++++++----------- src/core/stream/crypto/rsa/mod.rs | 4 +-- src/runtime/tokio/kcp.rs | 8 +++--- src/runtime/tokio/port_forward/mod.rs | 4 +-- src/runtime/tokio/tcp.rs | 24 +++++++++--------- src/server/port_forward/accepter.rs | 10 ++++---- 18 files changed, 114 insertions(+), 114 deletions(-) diff --git a/src/bin/server.rs b/src/bin/server.rs index b9fe081..032464e 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -21,7 +21,7 @@ use fuso::{ handshake::{ClientConfig, ForwardConfig, Handshake}, UseCompress, UseCrypto, }, - BoxedStream, Stream, + AbstractStream, Stream, }, error, runtime::tokio::TokioRuntime, @@ -174,7 +174,7 @@ async fn enter_fuso_serve(conf: Stateful) -> error::Result<()> { pub async fn handle_connection( conf: Stateful, - stream: BoxedStream<'static>, + stream: AbstractStream<'static>, ) -> error::Result<()> { let crypto = &conf.crypto; let compress = &conf.compress; diff --git a/src/client/port_forward/mod.rs b/src/client/port_forward/mod.rs index 44886de..ca457d5 100644 --- a/src/client/port_forward/mod.rs +++ b/src/client/port_forward/mod.rs @@ -5,7 +5,7 @@ use self::linker::Linker; use crate::{ core::{ - connector::BoxedConnector, + connector::AbstractConnector, rpc::{structs::port_forward::Request, AsyncCallee}, }, error, @@ -21,7 +21,7 @@ use crate::{ pub struct PortForwarder { transport: Transport<'static, S>, - connector: BoxedConnector<'static, (), Connection<'static>>, + connector: AbstractConnector<'static, (), Connection<'static>>, _marked: PhantomData, } @@ -38,7 +38,7 @@ where Self { transport, - connector: BoxedConnector::new(connector), + connector: AbstractConnector::new(connector), _marked: PhantomData, } } diff --git a/src/core/accepter.rs b/src/core/accepter.rs index cfd7ed2..20b97c6 100644 --- a/src/core/accepter.rs +++ b/src/core/accepter.rs @@ -6,7 +6,7 @@ use std::{ use crate::error; -use super::{BoxedStream, Stream}; +use super::{AbstractStream, Stream}; pub trait Accepter { type Output; @@ -30,20 +30,20 @@ pub struct Accept<'a, A: Unpin> { accepter: &'a mut A, } -pub struct BoxedAccepter<'a, O>(Box + Unpin + Send + 'a>); +pub struct AbstractAccepter<'a, O>(Box + Unpin + Send + 'a>); pub struct StreamAccepter<'a, O>(Box + Unpin + Send + 'a>); pub struct TaggedAccepter<'a, T: Clone, O> { tag: T, - accepter: BoxedAccepter<'a, O>, + accepter: AbstractAccepter<'a, O>, } pub struct MultiAccepter<'a, O> { - accepter_list: Vec>, + accepter_list: Vec>, } -impl<'a, O> BoxedAccepter<'a, O> { +impl<'a, O> AbstractAccepter<'a, O> { pub fn new(accepter: A) -> Self where A: Accepter + Unpin + Send + 'a, @@ -62,7 +62,7 @@ where { Self { tag, - accepter: BoxedAccepter::new(accepter), + accepter: AbstractAccepter::new(accepter), } } } @@ -78,7 +78,7 @@ impl<'a, O> MultiAccepter<'a, O> { where A: Accepter + Unpin + Send + 'static, { - self.accepter_list.push(BoxedAccepter::new(accepter)) + self.accepter_list.push(AbstractAccepter::new(accepter)) } } @@ -100,7 +100,7 @@ impl<'a, O> Accepter for MultiAccepter<'a, O> { } } -impl<'a, O> Accepter for BoxedAccepter<'a, O> { +impl<'a, O> Accepter for AbstractAccepter<'a, O> { type Output = O; fn poll_accept( mut self: Pin<&mut Self>, @@ -154,14 +154,14 @@ impl<'a, O> Accepter for StreamAccepter<'a, (SocketAddr, O)> where O: Stream + Send + Unpin + 'static, { - type Output = (SocketAddr, BoxedStream<'a>); + type Output = (SocketAddr, AbstractStream<'a>); fn poll_accept( mut self: Pin<&mut Self>, ctx: &mut Context<'_>, ) -> Poll> { match Pin::new(&mut *self.0).poll_accept(ctx)? { Poll::Pending => Poll::Pending, - Poll::Ready((addr, stream)) => Poll::Ready(Ok((addr, BoxedStream::new(stream)))), + Poll::Ready((addr, stream)) => Poll::Ready(Ok((addr, AbstractStream::new(stream)))), } } } diff --git a/src/core/connector.rs b/src/core/connector.rs index ab4dd84..6610532 100644 --- a/src/core/connector.rs +++ b/src/core/connector.rs @@ -36,15 +36,15 @@ pub trait ConnectExt: Connector { impl ConnectExt for C where C: Connector {} -pub struct BoxedConnector<'connector, T, O>( +pub struct AbstractConnector<'connector, T, O>( Box + Send + Unpin + 'connector>, ); pub struct MultiConnector<'a, T, O> { - connectors: Vec>, + connectors: Vec>, } -impl<'connector, T, O> BoxedConnector<'connector, T, O> { +impl<'connector, T, O> AbstractConnector<'connector, T, O> { pub fn new(connector: C) -> Self where C: Connector + Send + Unpin + 'connector, @@ -64,7 +64,7 @@ impl<'a, T, O> MultiConnector<'a, T, O> { where C: Connector + Send + Unpin + 'a, { - self.connectors.push(BoxedConnector(Box::new(connector))); + self.connectors.push(AbstractConnector(Box::new(connector))); } } @@ -80,7 +80,7 @@ where } } -impl Connector for BoxedConnector<'_, T, O> { +impl Connector for AbstractConnector<'_, T, O> { type Output = O; fn poll_connect( mut self: Pin<&mut Self>, diff --git a/src/core/mod.rs b/src/core/mod.rs index 87ac27b..7d69980 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -36,14 +36,14 @@ pub trait Stream: io::AsyncRead + io::AsyncWrite {} pub struct Connection<'a> { addr: SocketAddr, - stream: BoxedStream<'a>, + stream: AbstractStream<'a>, cursor: Option>>, marked: Option>>, } -pub struct BoxedStream<'a>(Box); +pub struct AbstractStream<'a>(Box); -impl<'a> BoxedStream<'a> { +impl<'a> AbstractStream<'a> { pub fn new(stream: S) -> Self where S: Stream + Unpin + Send + 'a, @@ -54,7 +54,7 @@ impl<'a> BoxedStream<'a> { impl Stream for T where T: io::AsyncRead + io::AsyncWrite + Unpin {} -impl<'a> io::AsyncRead for BoxedStream<'a> { +impl<'a> io::AsyncRead for AbstractStream<'a> { fn poll_read( mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, @@ -64,7 +64,7 @@ impl<'a> io::AsyncRead for BoxedStream<'a> { } } -impl<'a> io::AsyncWrite for BoxedStream<'a> { +impl<'a> io::AsyncWrite for AbstractStream<'a> { fn poll_write( mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, @@ -81,8 +81,8 @@ impl<'a> io::AsyncWrite for BoxedStream<'a> { } } -impl<'a> From<(SocketAddr, BoxedStream<'a>)> for Connection<'a> { - fn from(value: (SocketAddr, BoxedStream<'a>)) -> Self { +impl<'a> From<(SocketAddr, AbstractStream<'a>)> for Connection<'a> { + fn from(value: (SocketAddr, AbstractStream<'a>)) -> Self { Self { addr: value.0, stream: value.1, @@ -95,7 +95,7 @@ impl<'a> From<(SocketAddr, BoxedStream<'a>)> for Connection<'a> { impl<'a> Connection<'a> { pub fn new(addr: SocketAddr, stream: S) -> Self where - S: Into>, + S: Into>, { Self { addr, diff --git a/src/core/net/tcp.rs b/src/core/net/tcp.rs index 0b333bd..7fb0f33 100644 --- a/src/core/net/tcp.rs +++ b/src/core/net/tcp.rs @@ -3,32 +3,32 @@ use std::{net::SocketAddr, pin::Pin, task::Poll}; use crate::error; use crate::core::{ - accepter::{Accepter, BoxedAccepter}, + accepter::{Accepter, AbstractAccepter}, io::{AsyncRead, AsyncWrite}, - BoxedFuture, BoxedStream, Provider, + BoxedFuture, AbstractStream, Provider, }; pub trait TcpProvider { type Connector: Provider< - BoxedFuture<'static, error::Result>>, + BoxedFuture<'static, error::Result>>, Arg = SocketAddr, >; type Listener: Provider< BoxedFuture< 'static, - error::Result)>>, + error::Result)>>, >, Arg = SocketAddr, >; } pub struct TcpListener { - pub(crate) accepter: BoxedAccepter<'static, (SocketAddr, BoxedStream<'static>)>, + pub(crate) accepter: AbstractAccepter<'static, (SocketAddr, AbstractStream<'static>)>, } pub struct TcpStream { - pub(crate) stream: BoxedStream<'static>, + pub(crate) stream: AbstractStream<'static>, } impl TcpStream { diff --git a/src/core/net/udp.rs b/src/core/net/udp.rs index 3f6bab7..0ab2bff 100644 --- a/src/core/net/udp.rs +++ b/src/core/net/udp.rs @@ -2,15 +2,15 @@ use std::{net::SocketAddr, pin::Pin}; use crate::{core::{BoxedFuture, Provider}, error}; -pub type BoxedDatagram<'a> = Box; +pub type AbstractDatagram<'a> = Box; pub trait UdpProvider { - type Binder: Provider>>, Arg = SocketAddr>; - type Connect: Provider>>, Arg = SocketAddr>; + type Binder: Provider>>, Arg = SocketAddr>; + type Connect: Provider>>, Arg = SocketAddr>; } pub struct UdpSocket<'a> { - inner: BoxedDatagram<'a>, + inner: AbstractDatagram<'a>, } pub trait AsyncRecvfrom { diff --git a/src/core/processor.rs b/src/core/processor.rs index e24fe89..07d28a2 100644 --- a/src/core/processor.rs +++ b/src/core/processor.rs @@ -33,17 +33,17 @@ pub trait StreamProcessor { impl<'a, T> StreamProcessor for T where T: AsyncRead + AsyncWrite {} -pub struct BoxedProcessor<'a, A, R>(Box + Send + Unpin + 'a>); +pub struct AbstractProcessor<'a, A, R>(Box + Send + Unpin + 'a>); pub struct Processor<'a, A, R> { - processors: Vec>, + processors: Vec>, } pub struct WrappedPreprocessor<'a, In, Out>( pub(crate) Arc + Sync + Send + 'a>, ); -impl IProcessor for BoxedProcessor<'_, A, R> +impl IProcessor for AbstractProcessor<'_, A, R> where A: Send, { @@ -83,7 +83,7 @@ impl<'a, A, R> Processor<'a, A, R> { where P: IProcessor + Unpin + Send + 'a, { - self.processors.push(BoxedProcessor(Box::new(p))) + self.processors.push(AbstractProcessor(Box::new(p))) } } diff --git a/src/core/stream/codec.rs b/src/core/stream/codec.rs index a518c3d..de06f13 100644 --- a/src/core/stream/codec.rs +++ b/src/core/stream/codec.rs @@ -6,7 +6,7 @@ use std::{ use crate::{ core::{ io::{AsyncRead, AsyncWrite}, - BoxedStream, + AbstractStream, }, error, }; @@ -15,7 +15,7 @@ pub trait AsyncEncoder { fn poll_encode( self: Pin<&mut Self>, cx: &mut Context<'_>, - stream: &mut BoxedStream<'_>, + stream: &mut AbstractStream<'_>, buf: &[u8], ) -> Poll>; } @@ -24,20 +24,20 @@ pub trait AsyncDecoder { fn poll_decode( self: Pin<&mut Self>, cx: &mut Context<'_>, - stream: &mut BoxedStream<'_>, + stream: &mut AbstractStream<'_>, buf: &mut [u8], ) -> Poll>; } pub trait Codec: AsyncDecoder + AsyncEncoder {} -pub struct BoxedCodec<'a>(pub(crate) Box); +pub struct AbstractCodec<'a>(pub(crate) Box); #[pin_project::pin_project] pub struct PairCodec<'a> { #[pin] - pub(crate) first: BoxedCodec<'a>, - pub(crate) second: BoxedCodec<'a>, + pub(crate) first: AbstractCodec<'a>, + pub(crate) second: AbstractCodec<'a>, } #[pin_project::pin_project] @@ -51,24 +51,24 @@ pub struct EmptyCodec; impl Codec for T where T: AsyncDecoder + AsyncEncoder + Unpin {} -impl<'a> AsyncEncoder for BoxedCodec<'a> { +impl<'a> AsyncEncoder for AbstractCodec<'a> { #[inline] fn poll_encode( mut self: Pin<&mut Self>, cx: &mut Context<'_>, - stream: &mut BoxedStream<'_>, + stream: &mut AbstractStream<'_>, buf: &[u8], ) -> Poll> { Pin::new(&mut *self.0).poll_encode(cx, stream, buf) } } -impl<'a> AsyncDecoder for BoxedCodec<'a> { +impl<'a> AsyncDecoder for AbstractCodec<'a> { #[inline] fn poll_decode( mut self: Pin<&mut Self>, cx: &mut Context<'_>, - stream: &mut BoxedStream<'_>, + stream: &mut AbstractStream<'_>, buf: &mut [u8], ) -> Poll> { Pin::new(&mut *self.0).poll_decode(cx, stream, buf) @@ -80,12 +80,12 @@ impl<'a> AsyncEncoder for PairCodec<'a> { fn poll_encode( self: Pin<&mut Self>, cx: &mut Context<'_>, - stream: &mut BoxedStream<'_>, + stream: &mut AbstractStream<'_>, buf: &[u8], ) -> Poll> { let mut this = self.project(); - let mut stream = BoxedStream::new(CodecStream { + let mut stream = AbstractStream::new(CodecStream { stream, codec: &mut *this.first, }); @@ -99,12 +99,12 @@ impl<'a> AsyncDecoder for PairCodec<'a> { fn poll_decode( self: Pin<&mut Self>, cx: &mut Context<'_>, - stream: &mut BoxedStream<'_>, + stream: &mut AbstractStream<'_>, buf: &mut [u8], ) -> Poll> { let mut this = self.project(); - let mut stream = BoxedStream::new(CodecStream { + let mut stream = AbstractStream::new(CodecStream { stream, codec: &mut *this.first, }); @@ -113,7 +113,7 @@ impl<'a> AsyncDecoder for PairCodec<'a> { } } -impl<'s, 'c> AsyncWrite for CodecStream<&mut BoxedStream<'s>, &mut BoxedCodec<'c>> { +impl<'s, 'c> AsyncWrite for CodecStream<&mut AbstractStream<'s>, &mut AbstractCodec<'c>> { #[inline] fn poll_write( self: Pin<&mut Self>, @@ -134,7 +134,7 @@ impl<'s, 'c> AsyncWrite for CodecStream<&mut BoxedStream<'s>, &mut BoxedCodec<'c } } -impl<'s, 'c> AsyncRead for CodecStream<&mut BoxedStream<'s>, &mut BoxedCodec<'c>> { +impl<'s, 'c> AsyncRead for CodecStream<&mut AbstractStream<'s>, &mut AbstractCodec<'c>> { #[inline] fn poll_read( self: Pin<&mut Self>, @@ -151,7 +151,7 @@ impl AsyncEncoder for EmptyCodec { fn poll_encode( self: Pin<&mut Self>, cx: &mut Context<'_>, - stream: &mut BoxedStream<'_>, + stream: &mut AbstractStream<'_>, buf: &[u8], ) -> Poll> { Pin::new(stream).poll_write(cx, buf) @@ -163,7 +163,7 @@ impl AsyncDecoder for EmptyCodec { fn poll_decode( self: Pin<&mut Self>, cx: &mut Context<'_>, - stream: &mut BoxedStream<'_>, + stream: &mut AbstractStream<'_>, buf: &mut [u8], ) -> Poll> { Pin::new(stream).poll_read(cx, buf) diff --git a/src/core/stream/compress/lz4/mod.rs b/src/core/stream/compress/lz4/mod.rs index 54b8420..605380b 100644 --- a/src/core/stream/compress/lz4/mod.rs +++ b/src/core/stream/compress/lz4/mod.rs @@ -11,7 +11,7 @@ impl AsyncEncoder for Lz4Compressor { fn poll_encode( self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, - stream: &mut crate::core::BoxedStream<'_>, + stream: &mut crate::core::AbstractStream<'_>, buf: &[u8], ) -> std::task::Poll> { Pin::new(stream).poll_write(cx, buf) @@ -22,7 +22,7 @@ impl AsyncDecoder for Lz4Compressor { fn poll_decode( self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, - stream: &mut crate::core::BoxedStream<'_>, + stream: &mut crate::core::AbstractStream<'_>, buf: &mut [u8], ) -> std::task::Poll> { Pin::new(stream).poll_read(cx, buf) diff --git a/src/core/stream/compress/mod.rs b/src/core/stream/compress/mod.rs index 69db706..b1af6af 100644 --- a/src/core/stream/compress/mod.rs +++ b/src/core/stream/compress/mod.rs @@ -7,16 +7,16 @@ use crate::{config::Compress, error}; use crate::core::{ io::{AsyncRead, AsyncWrite}, - BoxedStream, + AbstractStream, }; -use super::codec::{AsyncDecoder, AsyncEncoder, BoxedCodec}; +use super::codec::{AsyncDecoder, AsyncEncoder, AbstractCodec}; #[pin_project::pin_project] pub struct CompressedStream<'a> { #[pin] - stream: BoxedStream<'a>, - compressor: BoxedCodec<'a>, + stream: AbstractStream<'a>, + compressor: AbstractCodec<'a>, } pub fn compress_stream<'a, 'compress, C, S>(stream: S, mut compress: C) -> CompressedStream<'a> @@ -24,17 +24,17 @@ where C: Iterator, S: AsyncRead + AsyncWrite + Send + Unpin + 'a, { - fn use_compress<'a>(compress: &Compress) -> BoxedCodec<'a> { + fn use_compress<'a>(compress: &Compress) -> AbstractCodec<'a> { match compress { - Compress::Lz4 => BoxedCodec(Box::new(lz4::Lz4Compressor {})), + Compress::Lz4 => AbstractCodec(Box::new(lz4::Lz4Compressor {})), } } let mut compressor = match compress.next() { - None => BoxedCodec(Box::new(EmptyCodec)), + None => AbstractCodec(Box::new(EmptyCodec)), Some(comp) => match compress.next() { None => use_compress(comp), - Some(next) => BoxedCodec(Box::new({ + Some(next) => AbstractCodec(Box::new({ PairCodec { first: use_compress(comp), second: use_compress(next), @@ -44,14 +44,14 @@ where }; for crypto_type in compress { - compressor = BoxedCodec(Box::new(PairCodec { + compressor = AbstractCodec(Box::new(PairCodec { first: compressor, second: use_compress(crypto_type), })) } CompressedStream { - stream: BoxedStream::new(stream), + stream: AbstractStream::new(stream), compressor, } } diff --git a/src/core/stream/crypto/aes/mod.rs b/src/core/stream/crypto/aes/mod.rs index 36b595c..ff8a7be 100644 --- a/src/core/stream/crypto/aes/mod.rs +++ b/src/core/stream/crypto/aes/mod.rs @@ -10,7 +10,7 @@ impl AsyncEncrypt for AesCrypto { fn poll_encrypt( self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, - stream: &mut crate::core::BoxedStream<'_>, + stream: &mut crate::core::AbstractStream<'_>, buf: &[u8], ) -> std::task::Poll> { Pin::new(stream).poll_write(cx, buf) @@ -21,7 +21,7 @@ impl AsyncDecrypt for AesCrypto { fn poll_decrypt( self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, - stream: &mut crate::core::BoxedStream<'_>, + stream: &mut crate::core::AbstractStream<'_>, buf: &mut [u8], ) -> std::task::Poll> { Pin::new(stream).poll_read(cx, buf) diff --git a/src/core/stream/crypto/mod.rs b/src/core/stream/crypto/mod.rs index 6b6ca65..f0914dc 100644 --- a/src/core/stream/crypto/mod.rs +++ b/src/core/stream/crypto/mod.rs @@ -10,15 +10,15 @@ use crate::core::{ io::{AsyncRead, AsyncWrite}, stream::codec::{EmptyCodec, PairCodec}, }; -use crate::{config::Crypto, core::BoxedStream, error}; +use crate::{config::Crypto, core::AbstractStream, error}; -use super::codec::{AsyncDecoder, AsyncEncoder, BoxedCodec}; +use super::codec::{AsyncDecoder, AsyncEncoder, AbstractCodec}; pub trait AsyncEncrypt { fn poll_encrypt( self: Pin<&mut Self>, cx: &mut Context<'_>, - stream: &mut BoxedStream<'_>, + stream: &mut AbstractStream<'_>, buf: &[u8], ) -> Poll>; } @@ -27,7 +27,7 @@ pub trait AsyncDecrypt { fn poll_decrypt( self: Pin<&mut Self>, cx: &mut Context<'_>, - stream: &mut BoxedStream<'_>, + stream: &mut AbstractStream<'_>, buf: &mut [u8], ) -> Poll>; } @@ -37,8 +37,8 @@ pub trait AsyncCrypto: AsyncEncrypt + AsyncDecrypt {} #[pin_project::pin_project] pub struct EncryptedStream<'a> { #[pin] - stream: BoxedStream<'a>, - crypto: BoxedCodec<'a>, + stream: AbstractStream<'a>, + crypto: AbstractCodec<'a>, } pub fn encrypt_stream<'a, 'crypto, C, S>(stream: S, mut cryptos: C) -> EncryptedStream<'a> @@ -46,18 +46,18 @@ where C: Iterator, S: AsyncRead + AsyncWrite + Send + Unpin + 'a, { - fn use_crypto<'a>(crypto: &Crypto) -> BoxedCodec<'a> { + fn use_crypto<'a>(crypto: &Crypto) -> AbstractCodec<'a> { match crypto { - Crypto::Rsa => BoxedCodec(Box::new(rsa::RsaCrypto {})), - Crypto::Aes => BoxedCodec(Box::new(aes::AesCrypto {})), + Crypto::Rsa => AbstractCodec(Box::new(rsa::RsaCrypto {})), + Crypto::Aes => AbstractCodec(Box::new(aes::AesCrypto {})), } } let mut crypto = match cryptos.next() { - None => BoxedCodec(Box::new(EmptyCodec)), + None => AbstractCodec(Box::new(EmptyCodec)), Some(crypto) => match cryptos.next() { None => use_crypto(&crypto), - Some(next) => BoxedCodec(Box::new({ + Some(next) => AbstractCodec(Box::new({ PairCodec { first: use_crypto(&crypto), second: use_crypto(&next), @@ -67,7 +67,7 @@ where }; for crypto_type in cryptos { - crypto = BoxedCodec(Box::new(PairCodec { + crypto = AbstractCodec(Box::new(PairCodec { first: crypto, second: use_crypto(&crypto_type), })) @@ -75,7 +75,7 @@ where EncryptedStream { crypto, - stream: BoxedStream::new(stream), + stream: AbstractStream::new(stream), } } @@ -88,7 +88,7 @@ where fn poll_decode( mut self: Pin<&mut Self>, cx: &mut Context<'_>, - stream: &mut BoxedStream<'_>, + stream: &mut AbstractStream<'_>, buf: &mut [u8], ) -> Poll> { Pin::new(&mut *self).poll_decrypt(cx, stream, buf) @@ -102,7 +102,7 @@ where fn poll_encode( mut self: Pin<&mut Self>, cx: &mut Context<'_>, - stream: &mut BoxedStream<'_>, + stream: &mut AbstractStream<'_>, buf: &[u8], ) -> Poll> { Pin::new(&mut *self).poll_encrypt(cx, stream, buf) diff --git a/src/core/stream/crypto/rsa/mod.rs b/src/core/stream/crypto/rsa/mod.rs index f7e6976..87d9e7b 100644 --- a/src/core/stream/crypto/rsa/mod.rs +++ b/src/core/stream/crypto/rsa/mod.rs @@ -10,7 +10,7 @@ impl AsyncEncrypt for RsaCrypto { fn poll_encrypt( self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, - stream: &mut crate::core::BoxedStream<'_>, + stream: &mut crate::core::AbstractStream<'_>, buf: &[u8], ) -> std::task::Poll> { Pin::new(stream).poll_write(cx, buf) @@ -21,7 +21,7 @@ impl AsyncDecrypt for RsaCrypto { fn poll_decrypt( self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, - stream: &mut crate::core::BoxedStream<'_>, + stream: &mut crate::core::AbstractStream<'_>, buf: &mut [u8], ) -> std::task::Poll> { Pin::new(stream).poll_read(cx, buf) diff --git a/src/runtime/tokio/kcp.rs b/src/runtime/tokio/kcp.rs index cec9afb..0974eda 100644 --- a/src/runtime/tokio/kcp.rs +++ b/src/runtime/tokio/kcp.rs @@ -5,7 +5,7 @@ use tokio::io::ReadBuf; use crate::{ core::{ future::LazyFuture, - net::{BoxedDatagram, KcpListener}, + net::{AbstractDatagram, KcpListener}, BoxedFuture, }, error, @@ -39,14 +39,14 @@ impl crate::core::net::UdpProvider for KcpWithTokioRuntime { type Connect = Self; } -impl crate::core::Provider>>> +impl crate::core::Provider>>> for KcpWithTokioRuntime { type Arg = SocketAddr; - fn call(addr: Self::Arg) -> BoxedFuture<'static, error::Result>> { + fn call(addr: Self::Arg) -> BoxedFuture<'static, error::Result>> { Box::pin(async move { - let boxed: BoxedDatagram<'_> = Box::new(TokioUdpSocket(Arc::new({ + let boxed: AbstractDatagram<'_> = Box::new(TokioUdpSocket(Arc::new({ tokio::net::UdpSocket::bind(addr).await? }))); Ok(boxed) diff --git a/src/runtime/tokio/port_forward/mod.rs b/src/runtime/tokio/port_forward/mod.rs index 0730f22..f7b13e0 100644 --- a/src/runtime/tokio/port_forward/mod.rs +++ b/src/runtime/tokio/port_forward/mod.rs @@ -3,7 +3,7 @@ use std::net::SocketAddr; use crate::{ core::{ accepter::Accepter, connector::Connector, processor::Preprocessor, - rpc::structs::port_forward::VisitorProtocol, BoxedStream, Connection, Stream, + rpc::structs::port_forward::VisitorProtocol, AbstractStream, Connection, Stream, }, error, server::port_forward::{MuxAccepter, Whence}, @@ -13,7 +13,7 @@ use super::TokioRuntime; impl MuxAccepter where - A: Accepter)> + Unpin + Send, + A: Accepter)> + Unpin + Send, { pub fn new(magic: u32, secret: [u8; 16], accepter: A) -> Self { Self::new_runtime(accepter, magic, secret) diff --git a/src/runtime/tokio/tcp.rs b/src/runtime/tokio/tcp.rs index db04a17..8a2ba93 100644 --- a/src/runtime/tokio/tcp.rs +++ b/src/runtime/tokio/tcp.rs @@ -4,10 +4,10 @@ use tokio::io::ReadBuf; use crate::{ core::{ - accepter::{Accepter, BoxedAccepter}, + accepter::{Accepter, AbstractAccepter}, io, net::{TcpListener, TcpProvider, TcpStream}, - BoxedFuture, BoxedStream, Provider, + BoxedFuture, AbstractStream, Provider, }, error, }; @@ -50,7 +50,7 @@ impl TcpStream { { let stream = tokio::net::TcpStream::connect(addr).await?; Ok(TcpStream { - stream: BoxedStream::new(stream), + stream: AbstractStream::new(stream), }) } } @@ -63,7 +63,7 @@ impl TcpListener { { let listener = tokio::net::TcpListener::bind(addr).await?; Ok(TcpListener { - accepter: BoxedAccepter::new(listener), + accepter: AbstractAccepter::new(listener), }) } } @@ -76,12 +76,12 @@ impl TcpProvider for TokioTcpProver { type Connector = Self; } -impl Provider>>> for TokioTcpProver { +impl Provider>>> for TokioTcpProver { type Arg = SocketAddr; - fn call(addr: Self::Arg) -> BoxedFuture<'static, error::Result>> { + fn call(addr: Self::Arg) -> BoxedFuture<'static, error::Result>> { Box::pin(async move { - Ok(BoxedStream::new( + Ok(AbstractStream::new( tokio::net::TcpStream::connect(addr).await?, )) }) @@ -92,7 +92,7 @@ impl Provider< BoxedFuture< 'static, - error::Result)>>, + error::Result)>>, >, > for TokioTcpProver { @@ -102,10 +102,10 @@ impl addr: Self::Arg, ) -> BoxedFuture< 'static, - error::Result)>>, + error::Result)>>, > { Box::pin(async move { - Ok(BoxedAccepter::new( + Ok(AbstractAccepter::new( tokio::net::TcpListener::bind(addr).await?, )) }) @@ -113,7 +113,7 @@ impl } impl Accepter for tokio::net::TcpListener { - type Output = (std::net::SocketAddr, BoxedStream<'static>); + type Output = (std::net::SocketAddr, AbstractStream<'static>); fn poll_accept( self: std::pin::Pin<&mut Self>, ctx: &mut std::task::Context<'_>, @@ -121,7 +121,7 @@ impl Accepter for tokio::net::TcpListener { match tokio::net::TcpListener::poll_accept(&*self, ctx)? { std::task::Poll::Pending => Poll::Pending, std::task::Poll::Ready((stream, addr)) => { - Poll::Ready(Ok((addr, BoxedStream::new(stream)))) + Poll::Ready(Ok((addr, AbstractStream::new(stream)))) } } } diff --git a/src/server/port_forward/accepter.rs b/src/server/port_forward/accepter.rs index 67c1c56..b244dc4 100644 --- a/src/server/port_forward/accepter.rs +++ b/src/server/port_forward/accepter.rs @@ -11,7 +11,7 @@ use super::{Connection, Rc4MagicHandshake, Whence}; use crate::core::future::Poller; use crate::core::io::AsyncReadExt; use crate::core::Stream; -use crate::core::{accepter::Accepter, BoxedStream}; +use crate::core::{accepter::Accepter, AbstractStream}; use crate::error; use crate::runtime::Runtime; @@ -34,7 +34,7 @@ pub struct ForwardAccepter { impl Accepter for MuxAccepter where - A: Accepter)> + Unpin + 'static, + A: Accepter)> + Unpin + 'static, R: Runtime + Unpin + 'static, { type Output = Whence; @@ -108,7 +108,7 @@ where #[cfg(feature = "fuso-runtime")] impl MuxAccepter where - A: Accepter)> + Unpin + Send, + A: Accepter)> + Unpin + Send, { pub fn new_runtime(accepter: A, magic: u32, secret: [u8; 16]) -> Self { Self { @@ -159,10 +159,10 @@ where Poll::Ready((addr, stream)) => Poll::Ready(Ok({ match self.whence { AcceptWhence::Visitor => { - Whence::Visitor(Connection::new(addr, BoxedStream::new(stream))) + Whence::Visitor(Connection::new(addr, AbstractStream::new(stream))) } AcceptWhence::Mapping => { - Whence::Mapping(Connection::new(addr, BoxedStream::new(stream))) + Whence::Mapping(Connection::new(addr, AbstractStream::new(stream))) } } })), From 943945d9aabe618b64f761402c71580f12abfb75 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Thu, 25 Apr 2024 00:14:49 +0800 Subject: [PATCH 14/29] =?UTF-8?q?=E5=AE=8C=E5=96=84=E9=83=A8=E5=88=86?= =?UTF-8?q?=E7=AB=AF=E5=8F=A3=E8=BD=AC=E5=8F=91=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bin/client.rs | 17 +++-- src/client/port_forward/linker.rs | 5 +- src/client/port_forward/transport.rs | 8 ++- src/core/channel.rs | 8 ++- src/core/rpc/callee.rs | 94 ++++++++++++++++++++++---- src/core/rpc/caller.rs | 55 +++++++-------- src/core/rpc/mod.rs | 51 +++----------- src/core/rpc/{lopper.rs => polling.rs} | 12 ++-- src/error/mod.rs | 1 + src/server/port_forward/mod.rs | 4 +- src/server/port_forward/transport.rs | 14 ++-- 11 files changed, 156 insertions(+), 113 deletions(-) rename src/core/rpc/{lopper.rs => polling.rs} (86%) diff --git a/src/bin/client.rs b/src/bin/client.rs index 4c39e4e..66fa81c 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -5,7 +5,7 @@ use fuso::{ client::port_forward::PortForwarder, config::{ client::{ - Config, Host, ServerAddr, Service, WithBridgeService, WithForwardService, + Config, FinalTarget, Host, ServerAddr, Service, WithBridgeService, WithForwardService, WithProxyService, }, Stateful, @@ -17,7 +17,8 @@ use fuso::{ net::{TcpListener, TcpStream}, protocol::{AsyncPacketRead, AsyncPacketSend}, rpc::{AsyncCall, Caller}, - stream::{handshake::Handshake, UseCompress, UseCrypto}, Connection, + stream::{handshake::Handshake, UseCompress, UseCrypto}, + Connection, }, error, runner::{FnRunnable, NamedRunnable, Rise, ServiceRunner}, @@ -111,7 +112,7 @@ async fn enter_forward_service_main( log::error!("fail to handshake {e:?}"); return Ok(Rise::Restart); } - }; + }; stream.write_config(&service).await?; @@ -126,11 +127,15 @@ async fn enter_forward_service_main( loop { let (linker, target) = forwarder.accept().await?; - log::debug!("connect to {}", linker); - } -} + log::debug!("start forward ...."); + tokio::spawn(async move{ + tokio::time::sleep(std::time::Duration::from_secs(5)).await; + drop(linker); + }); + } +} async fn enter_proxy_service_main( diff --git a/src/client/port_forward/linker.rs b/src/client/port_forward/linker.rs index 5402ae2..dae3b75 100644 --- a/src/client/port_forward/linker.rs +++ b/src/client/port_forward/linker.rs @@ -24,9 +24,8 @@ impl Linker { } } - -impl Display for Linker{ +impl Display for Linker { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "token={}", self.token) } -} \ No newline at end of file +} diff --git a/src/client/port_forward/transport.rs b/src/client/port_forward/transport.rs index 3347057..044a1e1 100644 --- a/src/client/port_forward/transport.rs +++ b/src/client/port_forward/transport.rs @@ -3,12 +3,13 @@ use std::{future::Future, marker::PhantomData, pin::Pin}; use crate::{ core::{ rpc::{ - self, structs::port_forward::Request, AsyncCallee, Callee, Caller, Decoder, Looper, + self, structs::port_forward::Request, AsyncCallee, Callee, Decoder, Looper, Responder, }, Stream, }, error, + runtime::Runtime, }; pub struct Transport<'transport, S> { @@ -21,7 +22,10 @@ impl<'transport, S> Transport<'transport, S> where S: Stream + Send + Unpin + 'transport, { - pub fn new(heartbeat_delay: std::time::Duration, stream: S) -> Self { + pub fn new(heartbeat_delay: std::time::Duration, stream: S) -> Self + where + R: Runtime, + { let (lopper, callee) = rpc::Callee::new(stream, heartbeat_delay); Self { diff --git a/src/core/channel.rs b/src/core/channel.rs index 06795d4..abcb9bd 100644 --- a/src/core/channel.rs +++ b/src/core/channel.rs @@ -79,7 +79,7 @@ impl Clone for Container { fn clone(&self) -> Self { Self { buffer: self.buffer.clone(), - waker: Default::default(), + waker: self.waker.clone(), } } } @@ -103,7 +103,7 @@ where fn poll( mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, + _: &mut std::task::Context<'_>, ) -> std::task::Poll { let data = self.data.take().expect("invalid state"); self.sender.push(data); @@ -137,7 +137,9 @@ impl Container { match self.buffer.lock().pop_front() { Some(data) => Some(data), None => { - self.waker.lock().replace(waker.clone()); + if let Some(waker) = self.waker.lock().replace(waker.clone()) { + waker.wake(); + } None } } diff --git a/src/core/rpc/callee.rs b/src/core/rpc/callee.rs index 51082cc..c01ce66 100644 --- a/src/core/rpc/callee.rs +++ b/src/core/rpc/callee.rs @@ -1,27 +1,31 @@ -use std::{marker::PhantomData, task::Poll}; +use std::{future::Future, marker::PhantomData, pin::Pin, task::Poll}; use crate::{ core::{ channel::{self, Receiver, Sender}, + future::{Poller, Select}, protocol::AsyncPacketRead, - rpc::lopper::Looper, + rpc::polling::Looper, split::{ReadHalf, WriteHalf}, + task::{setter, Getter, Setter}, Stream, }, error, }; -use super::{structs::port_forward::Request, AsyncCallee, Cmd, Decoder}; +use super::{structs::port_forward::Request, AsyncCallee, Cmd, Decoder, Transact}; use crate::core::split::SplitStream; pub struct Callee<'looper> { - requester: Receiver<(u64, Vec)>, + guarder: Sender<(u64, Getter<()>)>, + requester: Receiver, responder: Sender, _marked: PhantomData<&'looper ()>, } pub struct Responder { token: u64, + guard: Setter<()>, responder: Sender, } @@ -30,11 +34,16 @@ impl<'looper> Callee<'looper> { where S: Stream + Send + Unpin + 'looper, { - let (looper, rx, ax) = Looper::new(stream); + let (mut looper, rx, ax) = Looper::new(stream); + + let (sender, receiver) = channel::open(); + + looper.post(Responder::run_responder_guarder(receiver, rx.clone())); ( looper, Self { + guarder: sender, requester: ax, responder: rx, _marked: PhantomData, @@ -43,6 +52,41 @@ impl<'looper> Callee<'looper> { } } +impl Responder { + async fn run_responder_guarder( + rx: Receiver<(u64, Getter<()>)>, + sender: Sender, + ) -> error::Result<()> { + let mut calls = Vec::new(); + std::future::poll_fn(|cx| { + let mut polled = false; + + while !polled { + polled = match rx.poll_recv(cx)? { + Poll::Pending => true, + Poll::Ready((token, getter)) => { + calls.push((token, getter)); + false + } + }; + + calls.retain_mut(|(token, getter)| match Pin::new(getter).poll(cx) { + Poll::Pending => true, + Poll::Ready(Ok(())) => false, + Poll::Ready(Err(_)) => { + sender.send_sync(Cmd::Cancel(*token)); + false + } + }); + } + + Poll::Pending + }) + .await + } +} + + impl AsyncCallee for Callee<'_> { type Output = error::Result<(Vec, Responder)>; @@ -50,15 +94,37 @@ impl AsyncCallee for Callee<'_> { self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> Poll { - match self.requester.poll_recv(cx)? { - Poll::Pending => Poll::Pending, - Poll::Ready((token, packet)) => Poll::Ready(Ok(( - packet, - Responder { - token, - responder: self.responder.clone(), - }, - ))), + loop { + break match self.requester.poll_recv(cx)? { + Poll::Pending => Poll::Pending, + Poll::Ready(Transact::Cancel(token)) => { + log::debug!("cancel {token}"); + // TODO + continue; + } + Poll::Ready(Transact::Request(token, packet)) => Poll::Ready(Ok({ + let (setter, getter) = setter(); + + self.guarder.send_sync((token, getter)); + + ( + packet, + Responder { + token, + guard: setter, + responder: self.responder.clone(), + }, + ) + })), + }; } } } + + +impl Drop for Responder { + fn drop(&mut self) { + self.guard.invalid(); + log::debug!("cleaned callee {}", self.token); + } +} diff --git a/src/core/rpc/caller.rs b/src/core/rpc/caller.rs index ee8c2aa..2ca9415 100644 --- a/src/core/rpc/caller.rs +++ b/src/core/rpc/caller.rs @@ -8,13 +8,13 @@ use crate::{ future::LazyFuture, task::{setter, Setter}, token::IncToken, - Stream, + BoxedFuture, Stream, }, - error, + error::{self, FusoError}, runtime::Runtime, }; -use super::{lopper::Looper, AsyncCall, Cmd}; +use super::{polling::Looper, AsyncCall, Cmd, Transact}; use crate::core::rpc::Encoder; use crate::core::split::SplitStream; @@ -64,38 +64,29 @@ where T: serde::Serialize + Send + 'static, S: Send + Unpin + 'caller, { - type Output = error::Result>; + type Output<'a> = BoxedFuture<'a, error::Result>> where S: 'a; - fn poll_call( - self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - arg: &T, - ) -> std::task::Poll { - let this = self.project(); - - let mut caller = this.caller.lock(); - - Pin::new(&mut caller).poll(cx, move || { - let data = arg.encode(); - let (setter, getter) = setter(); - let token = this.calls.add(setter); + fn call<'a>(&'a mut self, arg: T) -> Self::Output<'a> { + let data = arg.encode(); + let (setter, getter) = setter(); + let token = self.calls.add(setter); + Box::pin(async move { let result = match data { - Ok(packet) => { - this.request.send_sync(Cmd::Transact { token, packet }); - Ok(()) - } + Ok(packet) => self.request.send(Cmd::Transact { token, packet }).await, Err(error) => { - this.calls.cancel(token); + self.calls.cancel(token); Err(error) } }; - async move { - match result { - Ok(_) => getter.await, + match result { + Err(e) => Err(e), + Ok(_) => match getter.await { + Ok(o) => Ok(o), + Err(FusoError::InvaledSetter) => Err(FusoError::Cancel), Err(e) => Err(e), - } + }, } }) } @@ -103,15 +94,21 @@ where impl<'a> Looper<'a> { async fn run_command_consumer( - receiver: Receiver<(u64, Vec)>, + receiver: Receiver, calls: Calls, ) -> error::Result<()> where R: Runtime, { loop { - let (token, packet) = receiver.recv().await?; - calls.wake(token, packet)?; + match receiver.recv().await? { + Transact::Cancel(token) => { + calls.cancel(token); + } + Transact::Request(token, packet) => { + calls.wake(token, packet)?; + } + } } } } diff --git a/src/core/rpc/mod.rs b/src/core/rpc/mod.rs index 3d0a678..aa1bc9a 100644 --- a/src/core/rpc/mod.rs +++ b/src/core/rpc/mod.rs @@ -1,7 +1,7 @@ mod callee; mod caller; mod keep; -mod lopper; +mod polling; pub mod structs; @@ -15,7 +15,7 @@ use crate::error; pub use callee::*; pub use caller::*; -pub use lopper::*; +pub use polling::*; use serde::{Deserialize, Serialize}; @@ -23,21 +23,22 @@ use serde::{Deserialize, Serialize}; enum Cmd { Ping, Pong, + Cancel(u64), Transact { token: u64, packet: Vec }, } -#[pin_project::pin_project] -pub struct Call<'caller, C, A, O> { - arg: A, - #[pin] - caller: &'caller mut C, - _marked: PhantomData, +#[derive(Debug, Serialize, Deserialize)] +enum Transact { + Cancel(u64), + Request(u64, Vec), } pub trait AsyncCall { - type Output; + type Output<'a> + where + Self: 'a; - fn poll_call(self: Pin<&mut Self>, cx: &mut Context<'_>, arg: &T) -> Poll; + fn call<'a>(&'a mut self, arg: T) -> Self::Output<'a>; } pub trait AsyncCallee { @@ -46,19 +47,6 @@ pub trait AsyncCallee { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll; } -pub trait ICallExt: AsyncCall { - fn call<'caller>(&'caller mut self, arg: A) -> Call<'caller, Self, A, O> - where - Self: Sized + Unpin, - { - Call { - caller: self, - arg, - _marked: PhantomData, - } - } -} - pub trait Encoder { fn encode(self) -> error::Result>; } @@ -68,8 +56,6 @@ pub trait Decoder { fn decode(self) -> error::Result; } -impl ICallExt for T where T: AsyncCall {} - impl Encoder for T where T: serde::Serialize, @@ -89,18 +75,3 @@ where bincode::deserialize(&self).map_err(Into::into) } } - -impl<'caller, C, A, O> std::future::Future for Call<'caller, C, A, O> -where - C: AsyncCall + Unpin, -{ - type Output = O; - - fn poll( - self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - let mut this = self.project(); - Pin::new(&mut **this.caller).poll_call(cx, &this.arg) - } -} diff --git a/src/core/rpc/lopper.rs b/src/core/rpc/polling.rs similarity index 86% rename from src/core/rpc/lopper.rs rename to src/core/rpc/polling.rs index 5a0b507..b8f2b74 100644 --- a/src/core/rpc/lopper.rs +++ b/src/core/rpc/polling.rs @@ -11,13 +11,13 @@ use crate::{ error, }; -use super::{keep::Heartbeat, Cmd, Decoder, Encoder}; +use super::{keep::Heartbeat, Cmd, Decoder, Encoder, Transact}; use crate::core::split::SplitStream; pub struct Looper<'looper>(Select<'looper, error::Result<()>>); impl<'looper> Looper<'looper> { - pub(super) fn new(stream: S) -> (Self, Sender, Receiver<(u64, Vec)>) + pub(super) fn new(stream: S) -> (Self, Sender, Receiver) where S: Stream + Unpin + Send + 'looper, { @@ -58,7 +58,7 @@ impl<'a> std::future::Future for Looper<'a> { impl<'looper> Looper<'looper> { pub(super) async fn run_send_loop( reader: ReadHalf, - sender: Sender<(u64, Vec)>, + sender: Sender, heartbeat: Heartbeat, ) -> error::Result<()> where @@ -72,9 +72,12 @@ impl<'looper> Looper<'looper> { match cmd { Cmd::Pong => heartbeat.pong(), Cmd::Ping => heartbeat.ping(), + Cmd::Cancel(token) => { + sender.send(Transact::Cancel(token)).await?; + } Cmd::Transact { token, packet } => { heartbeat.interrupt(); - sender.send((token, packet)).await?; + sender.send(Transact::Request(token, packet)).await?; } }; } @@ -90,6 +93,7 @@ impl<'looper> Looper<'looper> { let mut writer = writer; loop { let pkt = receiver.recv().await?.encode()?; + log::debug!("send to transport {}bytes", pkt.len()); writer.send_packet(&pkt).await?; } } diff --git a/src/error/mod.rs b/src/error/mod.rs index 8addd22..6f2c42e 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -8,6 +8,7 @@ pub enum FusoError { Timeout, Abort, AuthError, + Cancel, InvaledSetter, BadRpcCall(u64), InvalidPort, diff --git a/src/server/port_forward/mod.rs b/src/server/port_forward/mod.rs index 90b1e76..f8b156e 100644 --- a/src/server/port_forward/mod.rs +++ b/src/server/port_forward/mod.rs @@ -15,8 +15,8 @@ use std::{collections::HashMap, pin::Pin, sync::Arc, task::Poll}; use crate::core::future::Poller; use crate::core::io::AsyncReadExt; -use crate::core::rpc::structs::port_forward::{self, Target}; -use crate::core::rpc::ICallExt; +use crate::core::rpc::structs::port_forward::{self}; +use crate::core::rpc::AsyncCall; use crate::core::token::IncToken; use crate::core::Stream; use crate::runtime::Runtime; diff --git a/src/server/port_forward/transport.rs b/src/server/port_forward/transport.rs index 5fb1bf0..7210a1c 100644 --- a/src/server/port_forward/transport.rs +++ b/src/server/port_forward/transport.rs @@ -4,6 +4,7 @@ use std::task::Poll; use crate::core::rpc::{self, Decoder}; use crate::core::rpc::{Caller, Looper}; +use crate::core::BoxedFuture; use crate::runtime::Runtime; use crate::{ core::{ @@ -40,17 +41,10 @@ impl AsyncCall for Transport where T: Stream + Send + Unpin, { - type Output = error::Result; + type Output<'a> = BoxedFuture<'a, error::Result> where T: 'a; - fn poll_call( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - arg: &Request, - ) -> std::task::Poll { - match Pin::new(&mut self.caller).poll_call(cx, arg)? { - Poll::Pending => Poll::Pending, - Poll::Ready(data) => Poll::Ready(Ok(data.decode()?)), - } + fn call<'a>(&'a mut self, arg: Request) -> Self::Output<'a> { + Box::pin(async move { Ok(self.caller.call(arg).await?.decode()?) }) } } From 5a52363e5a82d75dfb1ad625704d368d86e0b688 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Sat, 27 Apr 2024 23:58:45 +0800 Subject: [PATCH 15/29] =?UTF-8?q?=E5=AE=8C=E5=96=84=E9=83=A8=E5=88=86?= =?UTF-8?q?=E7=AB=AF=E5=8F=A3=E8=BD=AC=E5=8F=91=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/client.toml | 58 +++++------ src/bin/client.rs | 97 ++++++++++++++---- src/bin/server.rs | 37 +++---- src/client/port_forward/connector.rs | 40 ++++++++ src/client/port_forward/linker.rs | 61 +++++++++-- src/client/port_forward/mod.rs | 29 ++++-- src/core/channel.rs | 30 +++--- src/core/connector.rs | 139 ++++++++++++++------------ src/core/rpc/callee.rs | 39 ++++++-- src/core/rpc/caller.rs | 9 +- src/core/rpc/polling.rs | 4 +- src/core/stream/handshake.rs | 5 +- src/core/task/mod.rs | 15 +-- src/core/transfer.rs | 138 ++++++++++++++++++++++++- src/error/mod.rs | 1 + src/runtime/tokio/port_forward/mod.rs | 11 +- src/server/port_forward/mod.rs | 66 +++++++++--- src/server/port_forward/transport.rs | 1 - 18 files changed, 570 insertions(+), 210 deletions(-) create mode 100644 src/client/port_forward/connector.rs diff --git a/config/client.toml b/config/client.toml index d861204..dc0a5d7 100644 --- a/config/client.toml +++ b/config/client.toml @@ -19,32 +19,32 @@ target = { type = "static", port = 2222, addr = ["127.0.0.1"] } keep_alive = { interval = 100 } -[executable] -type = "forward" -boot = "fork" -exposes = [ "7888/tcp" ] -restart = "always" -target = { type = "executable", path = "/bin/bash" } - - -[bridge1] -type = "bridge" -bind = "0.0.0.0" -port = 9999 - -[proxy1] -type = "proxy" -bind = "0.0.0.0" -port = 8081 -restart = 'never' -crypto = ["rsa", "aes"] -target = { type = "static", addr = ["baidu.com"], port = 443 } -rewrite = { with = "http_header", host = "https://baidu.com" } - -[proxy2] -type = "proxy" -bind = "0.0.0.0" -port = 7890 -restart = 'never' -crypto = ["rsa", "aes"] -target = { type = "dynamic" } \ No newline at end of file +# [executable] +# type = "forward" +# boot = "fork" +# exposes = [ "7888/tcp" ] +# restart = "always" +# target = { type = "executable", path = "/bin/bash" } + + +# [bridge1] +# type = "bridge" +# bind = "0.0.0.0" +# port = 9999 + +# [proxy1] +# type = "proxy" +# bind = "0.0.0.0" +# port = 8081 +# restart = 'never' +# crypto = ["rsa", "aes"] +# target = { type = "static", addr = ["baidu.com"], port = 443 } +# rewrite = { with = "http_header", host = "https://baidu.com" } + +# [proxy2] +# type = "proxy" +# bind = "0.0.0.0" +# port = 7890 +# restart = 'never' +# crypto = ["rsa", "aes"] +# target = { type = "dynamic" } \ No newline at end of file diff --git a/src/bin/client.rs b/src/bin/client.rs index 66fa81c..cc7d617 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -2,13 +2,13 @@ use std::{net::SocketAddr, time::Duration}; use fuso::{ cli, - client::port_forward::PortForwarder, + client::port_forward::{MuxConnector, PortForwarder, Protocol}, config::{ client::{ Config, FinalTarget, Host, ServerAddr, Service, WithBridgeService, WithForwardService, WithProxyService, }, - Stateful, + Expose, Stateful, }, core::{ accepter::AccepterExt, @@ -18,7 +18,8 @@ use fuso::{ protocol::{AsyncPacketRead, AsyncPacketSend}, rpc::{AsyncCall, Caller}, stream::{handshake::Handshake, UseCompress, UseCrypto}, - Connection, + transfer::AbstractTransmitter, + AbstractStream, Connection, }, error, runner::{FnRunnable, NamedRunnable, Rise, ServiceRunner}, @@ -91,11 +92,16 @@ async fn enter_forward_service_main( let result = server .try_connect(|host, port| async move { - log::debug!("connect to server {host}:{port}"); - match host { + log::trace!("connect to server {host}:{port}"); + let connection = match host { Host::Ip(ip) => TcpStream::connect(SocketAddr::new(*ip, port)).await, Host::Domain(domain) => TcpStream::connect(format!("{domain}:{port}")).await, - } + }; + + connection.map(|c| { + log::debug!("connection established: {host}:{port}"); + c + }) }) .await? .use_crypto(crypto.iter()) @@ -116,28 +122,85 @@ async fn enter_forward_service_main( stream.write_config(&service).await?; - let mut connector = MultiConnector::<(), Connection<'static>>::new(); + let mut connector = MultiConnector::>::new(); - if let Some(channel) = service.channel.as_ref() { - // connector.add(connector) - } + if let Some(channels) = service.channel.as_ref() { + for expose in channels.iter() { + match expose { + Expose::Kcp(_, _) => todo!(), + Expose::Tcp(_, port) => { + let port = *port; + let addr = server.addr.clone(); + connector.add(move |proto| { + let addr = addr.clone(); + async move { + addr.try_connect(port, |host, port| async move { + let connection = match host { + Host::Ip(ip) => { + TcpStream::connect(SocketAddr::new(*ip, port)).await + } + Host::Domain(domain) => { + TcpStream::connect(format!("{domain}:{port}")).await + } + }?; - let mut forwarder = PortForwarder::new(stream, connector); + Ok(AbstractStream::new(connection).into()) + }) + .await + } + }) + } + } + } + } else { + for expose in service.exposes.clone() { + let addr = server.addr.clone(); + match expose { + Expose::Kcp(_, port) => todo!(), + Expose::Tcp(_, port) => connector.add(MuxConnector::new(move |proto| { + let addr = addr.clone(); + async move { + addr.try_connect(port, |host, port| async move { + let connection = match host { + Host::Ip(ip) => { + TcpStream::connect(SocketAddr::new(*ip, port)).await + } + Host::Domain(domain) => { + TcpStream::connect(format!("{domain}:{port}")).await + } + }?; - loop { - let (linker, target) = forwarder.accept().await?; + Ok(AbstractStream::new(connection).into()) + }) + .await + } + })), + } + } + } - log::debug!("start forward ...."); + let mut forwarder = PortForwarder::new(stream, connector); - tokio::spawn(async move{ + log::debug!("forward started ."); + loop { + let (mut linker, target) = forwarder.accept().await?; + tokio::spawn(async move { tokio::time::sleep(std::time::Duration::from_secs(5)).await; - drop(linker); + match linker.link(Protocol::Tcp).await { + Ok(transmitter) => { + log::debug!("conneced ..."); + tokio::time::sleep(std::time::Duration::from_secs(5)).await; + + } + Err(e) => { + log::debug!("{:?}", e); + } + } }); } } - async fn enter_proxy_service_main( config: Stateful, service: WithProxyService, diff --git a/src/bin/server.rs b/src/bin/server.rs index 032464e..2c7665d 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -203,31 +203,29 @@ where let mut accepter = MultiAccepter::new(); - if let Some(ref channels) = conf.channel { - for expose in channels { - match expose { - Expose::Kcp(ip, port) => { - accepter.add(ForwardAccepter::new_visitor( - KcpListener::bind(Default::default(), ip.to_addr(*port)).await?, - )); - } - Expose::Tcp(ip, port) => accepter.add(ForwardAccepter::new_visitor( - TcpListener::bind(ip.to_addr(*port)).await?, - )), + for expose in conf.exposes.iter() { + match expose { + Expose::Kcp(ip, port) => { + accepter.add(ForwardAccepter::new_visitor( + KcpListener::bind(Default::default(), ip.to_addr(*port)).await?, + )); } + Expose::Tcp(ip, port) => accepter.add(ForwardAccepter::new_visitor( + TcpListener::bind(ip.to_addr(*port)).await?, + )), } } - if conf.channel.is_some() { - for expose in conf.exposes.iter() { + if let Some(ref channels) = conf.channel { + for expose in channels.iter() { match expose { Expose::Tcp(ip, port) => { - accepter.add(ForwardAccepter::new_visitor( + accepter.add(ForwardAccepter::new_mapping( TcpListener::bind(ip.to_addr(*port)).await?, )); } Expose::Kcp(ip, port) => { - accepter.add(ForwardAccepter::new_visitor({ + accepter.add(ForwardAccepter::new_mapping({ KcpListener::bind(Default::default(), ip.to_addr(*port)).await? })); } @@ -261,8 +259,11 @@ where loop { let (c1, c2) = forwarder.accept().await?; + log::debug!("start forward ................"); + tokio::spawn(async move { + if let Err(e) = c1.transfer(c2).await { + log::warn!("{e}") + }; + }); } - - Ok(()) } - \ No newline at end of file diff --git a/src/client/port_forward/connector.rs b/src/client/port_forward/connector.rs new file mode 100644 index 0000000..46fb760 --- /dev/null +++ b/src/client/port_forward/connector.rs @@ -0,0 +1,40 @@ +use std::marker::PhantomData; + +use crate::core::{ + connector::{AbstractConnector, Connector, IntoConnector}, + io::AsyncWriteExt, + protocol::AsyncPacketSend, + transfer::AbstractTransmitter, +}; + +use super::Protocol; + +pub struct MuxConnector<'connector> { + inner: AbstractConnector<'connector, Protocol, AbstractTransmitter<'connector>>, +} + +impl<'connector> MuxConnector<'connector> { + pub fn new(connector: I) -> Self + where + I: IntoConnector<'connector, C, Protocol, AbstractTransmitter<'connector>>, + { + Self { + inner: connector.into(), + } + } +} + +impl<'connector> Connector for MuxConnector<'connector> { + type Output = AbstractTransmitter<'connector>; + + fn connect<'conn>( + &'conn self, + target: Protocol, + ) -> crate::core::BoxedFuture<'conn, crate::error::Result> { + Box::pin(async move { + let transmitter = self.inner.connect(target).await?; + + unimplemented!() + }) + } +} diff --git a/src/client/port_forward/linker.rs b/src/client/port_forward/linker.rs index dae3b75..29c012d 100644 --- a/src/client/port_forward/linker.rs +++ b/src/client/port_forward/linker.rs @@ -1,26 +1,73 @@ use std::fmt::Display; use crate::{ - core::{rpc::Responder, Connection}, + core::{ + connector::{Connector, ReplicableConnector}, + io::AsyncWriteExt, + rpc::{ + structs::port_forward::{Request, Response}, + Responder, + }, + transfer::{AbstractTransmitter, TransmitterExt}, + Connection, + }, error, }; pub struct Linker { token: u64, + connector: ReplicableConnector<'static, Protocol, AbstractTransmitter<'static>>, responder: Responder, } +pub enum Reason { + Cancel, +} + +#[derive(Debug, Clone)] +pub enum Protocol { + Tcp, + Udp, + Kcp, +} + impl Linker { - pub fn new(token: u64, responder: Responder) -> Self { - Self { token, responder } + pub fn new( + token: u64, + connector: ReplicableConnector<'static, Protocol, AbstractTransmitter<'static>>, + responder: Responder, + ) -> Self { + Self { + token, + connector, + responder, + } } - pub async fn link(self) -> error::Result<()> { - unimplemented!() + pub async fn link(self, poto: Protocol) -> error::Result> { + self.responder.replay(Response::Ok).await?; + + let mut transmitter = self.connector.connect(poto).await?; + let token = self.token.to_be_bytes(); + + transmitter.send_all(&token).await?; + + Ok(transmitter) } - pub async fn reject(self) -> error::Result<()> { - unimplemented!() + pub async fn cancel(self, reason: R) -> error::Result<()> + where + R: Into, + { + let reason = reason.into(); + + Ok(()) + } +} + +impl From<()> for Reason { + fn from(value: ()) -> Self { + Self::Cancel } } diff --git a/src/client/port_forward/mod.rs b/src/client/port_forward/mod.rs index ca457d5..68d019a 100644 --- a/src/client/port_forward/mod.rs +++ b/src/client/port_forward/mod.rs @@ -1,8 +1,13 @@ +mod connector; mod linker; mod transport; -use self::linker::Linker; +pub use linker::*; +pub use connector::*; + +use crate::core::connector::ReplicableConnector; +use crate::core::transfer::AbstractTransmitter; use crate::{ core::{ connector::AbstractConnector, @@ -10,6 +15,7 @@ use crate::{ }, error, }; +use std::sync::Arc; use std::{marker::PhantomData, pin::Pin, task::Poll}; use crate::{ @@ -21,7 +27,7 @@ use crate::{ pub struct PortForwarder { transport: Transport<'static, S>, - connector: AbstractConnector<'static, (), Connection<'static>>, + connector: ReplicableConnector<'static, Protocol, AbstractTransmitter<'static>>, _marked: PhantomData, } @@ -32,13 +38,13 @@ where { pub fn new_with_runtime(transport: S, connector: C) -> Self where - C: Connector<(), Output = Connection<'static>> + Send + Unpin + 'static, + C: Connector> + Sync + Send + Unpin + 'static, { let transport = Transport::new::(std::time::Duration::from_secs(1), transport); Self { transport, - connector: AbstractConnector::new(connector), + connector: ReplicableConnector(Arc::new(AbstractConnector::new(connector))), _marked: PhantomData, } } @@ -58,12 +64,15 @@ where match Pin::new(&mut self.transport).poll_next(ctx)? { Poll::Pending => Poll::Pending, Poll::Ready((request, responder)) => match request { - Request::New(token, target) => Poll::Ready(Ok((Linker::new(token, responder), { - match target { - None => FinalTarget::Dynamic, - Some(target) => FinalTarget::Dynamic, - } - }))), + Request::New(token, target) => Poll::Ready(Ok({ + let linker = Linker::new(token, self.connector.clone(), responder); + (linker, { + match target { + None => FinalTarget::Dynamic, + Some(target) => FinalTarget::Dynamic, + } + }) + })), }, } } diff --git a/src/core/channel.rs b/src/core/channel.rs index abcb9bd..1c96ec7 100644 --- a/src/core/channel.rs +++ b/src/core/channel.rs @@ -22,7 +22,7 @@ pub struct Receiver { } pub struct Recv<'a, T> { - receiver: &'a Container, + container: &'a Container, } pub struct Send<'a, T> { @@ -33,15 +33,12 @@ pub struct Send<'a, T> { impl Receiver { pub fn recv<'a>(&'a self) -> Recv<'a, T> { Recv { - receiver: &self.container, + container: &self.container, } } pub fn poll_recv(&self, cx: &mut Context<'_>) -> Poll> { - match self.container.take(cx.waker()) { - None => Poll::Pending, - Some(t) => Poll::Ready(Ok(t)), - } + self.container.poll_take(cx) } } @@ -118,16 +115,16 @@ impl<'a, T> std::future::Future for Recv<'a, T> { self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll { - match self.receiver.take(cx.waker()) { - None => Poll::Pending, - Some(data) => Poll::Ready(Ok(data)), - } + self.container.poll_take(cx) } } impl Container { fn push(&self, data: T) { - self.buffer.lock().push_back(data); + let mut buf = self.buffer.lock(); + + buf.push_back(data); + if let Some(waker) = self.waker.lock().take() { waker.wake(); } @@ -137,11 +134,16 @@ impl Container { match self.buffer.lock().pop_front() { Some(data) => Some(data), None => { - if let Some(waker) = self.waker.lock().replace(waker.clone()) { - waker.wake(); - } + drop(self.waker.lock().replace(waker.clone())); None } } } + + pub fn poll_take(&self, cx: &mut Context<'_>) -> Poll> { + match self.take(cx.waker()) { + None => Poll::Pending, + Some(t) => Poll::Ready(Ok(t)), + } + } } diff --git a/src/core/connector.rs b/src/core/connector.rs index 6610532..b835e53 100644 --- a/src/core/connector.rs +++ b/src/core/connector.rs @@ -1,49 +1,43 @@ use std::{ + future::Future, pin::Pin, + sync::Arc, task::{Context, Poll}, }; use crate::error; -pub trait Connector { - type Output; +use super::BoxedFuture; - fn poll_connect( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - target: &Target, - ) -> Poll>; +pub trait IntoConnector<'a, C, T, O> { + fn into(self) -> AbstractConnector<'a, T, O>; } -#[pin_project::pin_project] -pub struct Connect<'a, C, T> { - target: T, - #[pin] - connector: &'a mut C, -} +pub trait Connector { + type Output; -pub trait ConnectExt: Connector { - fn connect<'a>(&'a mut self, target: T) -> Connect<'a, Self, T> - where - Self: Sized, - { - Connect { - target, - connector: self, - } - } + fn connect<'conn>( + &'conn self, + target: Target, + ) -> BoxedFuture<'conn, error::Result>; } -impl ConnectExt for C where C: Connector {} - pub struct AbstractConnector<'connector, T, O>( Box + Send + Unpin + 'connector>, ); +pub struct ReplicableConnector<'connector, T, O>(pub Arc>); + +pub struct ConnectorWithFn<'connector, T, O> { + f: Box BoxedFuture<'connector, error::Result> + Send + Sync>, +} + pub struct MultiConnector<'a, T, O> { connectors: Vec>, } +unsafe impl Sync for AbstractConnector<'_, T, O> {} + impl<'connector, T, O> AbstractConnector<'connector, T, O> { pub fn new(connector: C) -> Self where @@ -60,60 +54,79 @@ impl<'a, T, O> MultiConnector<'a, T, O> { } } - pub fn add(&mut self, connector: C) + pub fn add(&mut self, connector: I) where - C: Connector + Send + Unpin + 'a, + I: IntoConnector<'a, C, T, O>, { - self.connectors.push(AbstractConnector(Box::new(connector))); + self.connectors.push(connector.into()); } } -impl std::future::Future for Connect<'_, C, T> -where - C: Connector + Unpin, -{ - type Output = error::Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut this = self.project(); - Pin::new(&mut **this.connector).poll_connect(cx, &this.target) +impl Connector for AbstractConnector<'_, T, O> { + type Output = O; + fn connect<'conn>(&'conn self, target: T) -> BoxedFuture<'conn, error::Result> { + self.0.connect(target) } } -impl Connector for AbstractConnector<'_, T, O> { +impl<'connector, T, O> Connector for ReplicableConnector<'connector, T, O> { type Output = O; - fn poll_connect( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - target: &T, - ) -> Poll> { - Pin::new(&mut *self.0).poll_connect(cx, target) + fn connect<'conn>(&'conn self, target: T) -> BoxedFuture<'conn, error::Result> { + self.0.connect(target) } } -impl Connector for MultiConnector<'_, T, O> { +impl Connector for MultiConnector<'_, T, O> +where + T: Clone + Send, +{ type Output = O; - fn poll_connect( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - target: &T, - ) -> Poll> { - let mut error = Option::None; - - for connector in self.connectors.iter_mut() { - match Pin::new(connector).poll_connect(cx, target) { - Poll::Pending => continue, - Poll::Ready(Ok(o)) => return Poll::Ready(Ok(o)), - Poll::Ready(Err(e)) => { - error.replace(e); + fn connect<'conn>(&'conn self, target: T) -> BoxedFuture<'conn, error::Result> { + Box::pin(async move { + for connector in self.connectors.iter() { + if let Ok(o) = connector.connect(target.clone()).await { + return Ok(o); } } - } - match error { - None => Poll::Pending, - Some(e) => Poll::Ready(Err(e)), - } + Err(error::FusoError::InvalidConnection) + }) + } +} + +impl<'a, C, T, O> IntoConnector<'a, C, T, O> for C +where + C: Connector + Send + Unpin + 'a, +{ + fn into(self) -> AbstractConnector<'a, T, O> { + AbstractConnector::new(self) + } +} + +impl<'a, F, Fut, T, O> IntoConnector<'a, ConnectorWithFn<'a, T, O>, T, O> for F +where + F: Fn(T) -> Fut + Send + Sync + 'static, + Fut: Future> + Send + 'a, + T: 'a, + O: 'static, +{ + fn into(self) -> AbstractConnector<'a, T, O> { + AbstractConnector::new(ConnectorWithFn { + f: Box::new(move |t| Box::pin((self)(t))), + }) + } +} + +impl<'a, T, O> Connector for ConnectorWithFn<'a, T, O> { + type Output = O; + fn connect<'conn>(&'conn self, target: T) -> BoxedFuture<'conn, error::Result> { + (self.f)(target) + } +} + +impl<'connector, T, O> Clone for ReplicableConnector<'connector, T, O> { + fn clone(&self) -> Self { + Self(self.0.clone()) } } diff --git a/src/core/rpc/callee.rs b/src/core/rpc/callee.rs index c01ce66..168b785 100644 --- a/src/core/rpc/callee.rs +++ b/src/core/rpc/callee.rs @@ -1,5 +1,7 @@ use std::{future::Future, marker::PhantomData, pin::Pin, task::Poll}; +use serde::Serialize; + use crate::{ core::{ channel::{self, Receiver, Sender}, @@ -14,6 +16,7 @@ use crate::{ }; use super::{structs::port_forward::Request, AsyncCallee, Cmd, Decoder, Transact}; +use crate::core::rpc::Encoder; use crate::core::split::SplitStream; pub struct Callee<'looper> { @@ -25,7 +28,7 @@ pub struct Callee<'looper> { pub struct Responder { token: u64, - guard: Setter<()>, + guard: Option>, responder: Sender, } @@ -58,7 +61,7 @@ impl Responder { sender: Sender, ) -> error::Result<()> { let mut calls = Vec::new(); - std::future::poll_fn(|cx| { + std::future::poll_fn(move |cx| { let mut polled = false; while !polled { @@ -73,7 +76,8 @@ impl Responder { calls.retain_mut(|(token, getter)| match Pin::new(getter).poll(cx) { Poll::Pending => true, Poll::Ready(Ok(())) => false, - Poll::Ready(Err(_)) => { + Poll::Ready(Err(e)) => { + log::debug!("{}", e); sender.send_sync(Cmd::Cancel(*token)); false } @@ -86,7 +90,6 @@ impl Responder { } } - impl AsyncCallee for Callee<'_> { type Output = error::Result<(Vec, Responder)>; @@ -111,7 +114,7 @@ impl AsyncCallee for Callee<'_> { packet, Responder { token, - guard: setter, + guard: Some(setter), responder: self.responder.clone(), }, ) @@ -121,10 +124,32 @@ impl AsyncCallee for Callee<'_> { } } +impl Responder { + pub async fn replay(mut self, result: T) -> error::Result<()> + where + T: Serialize, + { + let result = self + .responder + .send(Cmd::Transact { + token: self.token, + packet: result.encode()?, + }) + .await; + + if let Some(guard) = self.guard.take() { + let _ = guard.set(()); + } + + result + } +} impl Drop for Responder { fn drop(&mut self) { - self.guard.invalid(); - log::debug!("cleaned callee {}", self.token); + if let Some(mut guard) = self.guard.take() { + guard.invalid(); + log::trace!("cleaned rpc task. token={}", self.token); + } } } diff --git a/src/core/rpc/caller.rs b/src/core/rpc/caller.rs index 2ca9415..2781dd6 100644 --- a/src/core/rpc/caller.rs +++ b/src/core/rpc/caller.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, marker::PhantomData, pin::Pin, sync::Arc}; +use std::{collections::HashMap, marker::PhantomData, sync::Arc}; use parking_lot::Mutex; @@ -16,7 +16,6 @@ use crate::{ use super::{polling::Looper, AsyncCall, Cmd, Transact}; use crate::core::rpc::Encoder; -use crate::core::split::SplitStream; #[derive(Default, Clone)] pub struct Calls { @@ -24,11 +23,9 @@ pub struct Calls { inc_token: IncToken, } -#[pin_project::pin_project] pub struct Caller { calls: Calls, request: Sender, - #[pin] caller: Arc>>>>, marked: PhantomData, } @@ -134,10 +131,6 @@ impl Calls { fn cancel(&self, token: u64) { drop(self.call_list.lock().remove(&token)) } - - fn cancel_all(&self) { - self.call_list.lock().clear(); - } } impl Clone for Caller { diff --git a/src/core/rpc/polling.rs b/src/core/rpc/polling.rs index b8f2b74..5b6199f 100644 --- a/src/core/rpc/polling.rs +++ b/src/core/rpc/polling.rs @@ -92,8 +92,8 @@ impl<'looper> Looper<'looper> { { let mut writer = writer; loop { - let pkt = receiver.recv().await?.encode()?; - log::debug!("send to transport {}bytes", pkt.len()); + let pkt: Vec = receiver.recv().await?.encode()?; + log::trace!("send to transport {}bytes", pkt.len()); writer.send_packet(&pkt).await?; } } diff --git a/src/core/stream/handshake.rs b/src/core/stream/handshake.rs index 172ea34..43197c3 100644 --- a/src/core/stream/handshake.rs +++ b/src/core/stream/handshake.rs @@ -127,7 +127,10 @@ pub trait Handshake: Stream + Send + Unpin { { let config: ClientConfig = config.into(); - Box::pin(async move { self.send_packet(&config.encode()?).await }) + Box::pin(async move { + log::debug!("send configuration information"); + self.send_packet(&config.encode()?).await + }) } } diff --git a/src/core/task/mod.rs b/src/core/task/mod.rs index 23c4b9b..c0cf188 100644 --- a/src/core/task/mod.rs +++ b/src/core/task/mod.rs @@ -22,6 +22,7 @@ pub struct Getter { pub struct Setter { val: Arc>>, + setted: bool, waker: Arc>>, } @@ -29,6 +30,7 @@ impl Setter { pub fn set(mut self, val: V) -> error::Result<()> { drop(self.val.lock().replace(val)); self.try_wake(); + self.setted = true; Ok(()) } @@ -46,7 +48,9 @@ impl Setter { impl Drop for Setter { fn drop(&mut self) { - self.invalid(); + if !self.setted { + self.invalid(); + } } } @@ -87,15 +91,14 @@ impl ValState { pub fn setter() -> (Setter, Getter) { let val = Arc::new(Mutex::new(ValState::Nil)); + let waker = Arc::new(Mutex::new(None)); ( Setter { val: val.clone(), - waker: Default::default(), - }, - Getter { - val, - waker: Default::default(), + waker: waker.clone(), + setted: false, }, + Getter { val, waker }, ) } diff --git a/src/core/transfer.rs b/src/core/transfer.rs index 87ae6b1..ccfaeb2 100644 --- a/src/core/transfer.rs +++ b/src/core/transfer.rs @@ -1,19 +1,35 @@ use std::{ - pin::Pin, + io, + pin::{self, Pin}, task::{Context, Poll}, }; use crate::error; -pub trait TransSender { +use super::Stream; + +#[pin_project::pin_project] +pub struct TransmitSend<'t, T> { + data: &'t [u8], + #[pin] + transmitter: &'t mut T, +} + +#[pin_project::pin_project] +pub struct TransmitSendAll<'t, T> { + pos: usize, + data: &'t [u8], + #[pin] + transmitter: &'t mut T, +} + +pub trait Transmitter: Unpin { fn poll_send( self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &[u8], ) -> Poll>; -} -pub trait TransReceiver { fn poll_recv( self: Pin<&mut Self>, ctx: &mut Context<'_>, @@ -21,4 +37,118 @@ pub trait TransReceiver { ) -> Poll>; } +pub trait TransmitterExt: Transmitter { + fn send<'a>(&'a mut self, data: &'a [u8]) -> TransmitSend<'a, Self> + where + Self: Sized, + { + TransmitSend { + data, + transmitter: self, + } + } + + fn send_all<'a>(&'a mut self, data: &'a [u8]) -> TransmitSendAll<'a, Self> + where + Self: Sized, + { + TransmitSendAll { + pos: 0, + data, + transmitter: self, + } + } +} + +impl TransmitterExt for T where T: Transmitter {} + +pub struct AbstractTransmitter<'transmitter> { + inner: Box, +} + +impl<'transmitter> Transmitter for AbstractTransmitter<'transmitter> { + fn poll_send( + mut self: Pin<&mut Self>, + ctx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut *self.inner).poll_send(ctx, buf) + } + + fn poll_recv( + mut self: Pin<&mut Self>, + ctx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + Pin::new(&mut *self.inner).poll_recv(ctx, buf) + } +} + +impl Transmitter for T +where + T: Stream + Unpin, +{ + fn poll_send( + self: Pin<&mut Self>, + ctx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + T::poll_write(self, ctx, buf) + } + + fn poll_recv( + self: Pin<&mut Self>, + ctx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + T::poll_read(self, ctx, buf) + } +} + +impl<'transmitter, S> From for AbstractTransmitter<'transmitter> +where + S: Stream + Send + Unpin + 'transmitter, +{ + fn from(value: S) -> Self { + Self { + inner: Box::new(value), + } + } +} + +impl<'t, T> std::future::Future for TransmitSend<'t, T> +where + T: Transmitter, +{ + type Output = error::Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + Pin::new(&mut **this.transmitter).poll_send(cx, &this.data) + } +} + +impl<'t, T> std::future::Future for TransmitSendAll<'t, T> +where + T: Transmitter, +{ + type Output = error::Result<()>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + while *this.pos < this.data.len() { + match Pin::new(&mut **this.transmitter).poll_send(cx, &this.data[*this.pos..])? { + Poll::Pending => return Poll::Pending, + Poll::Ready(0) => { + return Poll::Ready(Err(io::Error::from(io::ErrorKind::BrokenPipe).into())) + } + Poll::Ready(n) => { + *this.pos += n; + } + }; + } + + Poll::Ready(Ok(())) + } +} diff --git a/src/error/mod.rs b/src/error/mod.rs index 6f2c42e..4e30917 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -9,6 +9,7 @@ pub enum FusoError { Abort, AuthError, Cancel, + InvalidConnection, InvaledSetter, BadRpcCall(u64), InvalidPort, diff --git a/src/runtime/tokio/port_forward/mod.rs b/src/runtime/tokio/port_forward/mod.rs index f7b13e0..eba89f4 100644 --- a/src/runtime/tokio/port_forward/mod.rs +++ b/src/runtime/tokio/port_forward/mod.rs @@ -1,12 +1,9 @@ use std::net::SocketAddr; use crate::{ - core::{ - accepter::Accepter, connector::Connector, processor::Preprocessor, - rpc::structs::port_forward::VisitorProtocol, AbstractStream, Connection, Stream, - }, - error, - server::port_forward::{MuxAccepter, Whence}, + client::port_forward::Protocol, core::{ + accepter::Accepter, connector::Connector, processor::Preprocessor, rpc::structs::port_forward::VisitorProtocol, transfer::AbstractTransmitter, AbstractStream, Connection, Stream + }, error, server::port_forward::{MuxAccepter, Whence} }; use super::TokioRuntime; @@ -42,7 +39,7 @@ where { pub fn new(transport: S, connector: C) -> Self where - C: Connector<(), Output = Connection<'static>> + Send + Unpin + 'static, + C: Connector> + Sync + Send + Unpin + 'static, { Self::new_with_runtime(transport, connector) } diff --git a/src/server/port_forward/mod.rs b/src/server/port_forward/mod.rs index f8b156e..fc99b36 100644 --- a/src/server/port_forward/mod.rs +++ b/src/server/port_forward/mod.rs @@ -13,10 +13,11 @@ use std::future::Future; use std::marker::PhantomData; use std::{collections::HashMap, pin::Pin, sync::Arc, task::Poll}; -use crate::core::future::Poller; +use crate::core::future::{Poller, Select}; use crate::core::io::AsyncReadExt; use crate::core::rpc::structs::port_forward::{self}; use crate::core::rpc::AsyncCall; +use crate::core::task::{setter, Getter, Setter}; use crate::core::token::IncToken; use crate::core::Stream; use crate::runtime::Runtime; @@ -36,7 +37,8 @@ type Connection = crate::core::Connection<'static>; enum Outcome { Ready(u64, Connection), - Pending(u64), + Mapped(u64), + Pending(u64, Getter<()>), Timeout(u64), Transport(u64, error::FusoError), Stopped(Option), @@ -50,7 +52,7 @@ pub enum Whence { #[derive(Clone)] pub struct Visitors { inc_token: IncToken, - connections: Arc>>, + connections: Arc)>>>, } pub struct PortForwarder { @@ -122,11 +124,22 @@ where polled = need_poll; match outcome { - Outcome::Pending(token) => { - self.poller.add(Self::wait_transport( - token, - std::time::Duration::from_secs(10), - )); + Outcome::Mapped(token) => match self.visitors.take(token) { + None => { + log::debug!("mapping successful {{ token={token} }}") + } + Some(_) => { + log::debug!("mapping error {{ token={token} }}") + } + }, + Outcome::Pending(token, getter) => { + if self.visitors.exists(token) { + self.poller.add(Self::wait_transport( + token, + getter, + std::time::Duration::from_secs(10), + )); + } } Outcome::Timeout(token) => { if let Some(conn) = self.visitors.take(token) { @@ -210,10 +223,25 @@ where R: Runtime, T: AsyncWrite + AsyncRead + Send + Unpin, { - async fn wait_transport(token: u64, timeout: std::time::Duration) -> error::Result { + async fn wait_transport( + token: u64, + getter: Getter<()>, + timeout: std::time::Duration, + ) -> error::Result { log::debug!("wait for mapping {{ token={token}, timeout={timeout:?} }}"); - R::sleep(timeout).await; - Ok(Outcome::Timeout(token)) + let mut select = Select::new(); + + select.add(async move { + R::sleep(timeout).await; + Ok(Outcome::Timeout(token)) + }); + + select.add(async move { + let _ = getter.await; + Ok(Outcome::Mapped(token)) + }); + + select.await } async fn do_prepare_visitor( @@ -234,7 +262,9 @@ where log::debug!("create mapping {} -- [T] -- {:?}", conn.addr(), addr); - let token = visitors.store(conn); + let (setter, getter) = setter(); + + let token = visitors.store(conn, setter); let result = R::wait_for(std::time::Duration::from_secs(10), async move { match transport.call(Request::New(token, addr)).await { @@ -250,7 +280,7 @@ where match result { Err(_) => Ok(Outcome::Transport(token, error::FusoError::NotResponse)), Ok(r) => match r { - Ok(_) => Ok(Outcome::Pending(token)), + Ok(_) => Ok(Outcome::Pending(token, getter)), Err(e) => Ok(Outcome::Transport(token, e)), }, } @@ -285,14 +315,18 @@ impl Default for Visitors { } impl Visitors { + fn exists(&self, token: u64) -> bool { + self.connections.lock().contains_key(&token) + } + fn take(&self, token: u64) -> Option { - self.connections.lock().remove(&token) + self.connections.lock().remove(&token).map(|(c, _)| c) } - fn store(&self, conn: Connection) -> u64 { + fn store(&self, conn: Connection, setter: Setter<()>) -> u64 { let token = self.next_token(); - self.connections.lock().insert(token, conn); + self.connections.lock().insert(token, (conn, setter)); token } diff --git a/src/server/port_forward/transport.rs b/src/server/port_forward/transport.rs index 7210a1c..6d1a674 100644 --- a/src/server/port_forward/transport.rs +++ b/src/server/port_forward/transport.rs @@ -1,5 +1,4 @@ use std::pin::Pin; -use std::task::Poll; use crate::core::rpc::{self, Decoder}; use crate::core::rpc::{Caller, Looper}; From bbbabe6190ab6f930a54ff253d9e32ad9cfeded9 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Mon, 29 Apr 2024 00:24:21 +0800 Subject: [PATCH 16/29] =?UTF-8?q?=E5=AE=8C=E5=96=84=E9=83=A8=E5=88=86?= =?UTF-8?q?=E7=AB=AF=E5=8F=A3=E8=BD=AC=E5=8F=91=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 4 ++ config/client.toml | 8 ++- config/fuso.toml | 5 ++ src/bin/client.rs | 95 +++++++++++++++++++-------- src/bin/main.rs | 3 + src/bin/server.rs | 22 +++---- src/client/port_forward/linker.rs | 22 +++++-- src/client/port_forward/mod.rs | 27 ++++++-- src/config/client.rs | 12 +++- src/core/io/mod.rs | 6 +- src/core/rpc/structs.rs | 1 + src/core/transfer.rs | 31 ++++++++- src/runtime/tokio/port_forward/mod.rs | 6 +- src/server/port_forward/mod.rs | 5 +- 14 files changed, 184 insertions(+), 63 deletions(-) create mode 100644 config/fuso.toml create mode 100644 src/bin/main.rs diff --git a/Cargo.toml b/Cargo.toml index f599656..664565c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,10 @@ path = "src/bin/client.rs" name = "fus" path = "src/bin/server.rs" +[[bin]] +name = "fuso" +path = "src/bin/main.rs" + [profile.release] lto = true debug = false diff --git a/config/client.toml b/config/client.toml index dc0a5d7..3d6e69a 100644 --- a/config/client.toml +++ b/config/client.toml @@ -13,9 +13,11 @@ auth = { type = "secret", secret = "12345678" } type = "forward" boot = "fork" exposes = [ "8080/tcp" ] -channel = [ "8002/tcp" ] +# channel = [ "8002/tcp" ] restart = "always" -target = { type = "static", port = 2222, addr = ["127.0.0.1"] } +cryptos = ["aes"] +compress = ["lz4"] +target = { type = "tcp", port = 9999, addr = ["127.0.0.1"] } keep_alive = { interval = 100 } @@ -24,7 +26,7 @@ keep_alive = { interval = 100 } # boot = "fork" # exposes = [ "7888/tcp" ] # restart = "always" -# target = { type = "executable", path = "/bin/bash" } +# target = { type = "shell", path = "/bin/bash" } # [bridge1] diff --git a/config/fuso.toml b/config/fuso.toml new file mode 100644 index 0000000..3b7157f --- /dev/null +++ b/config/fuso.toml @@ -0,0 +1,5 @@ + +[client_1.shell_1] +target = "executable" +channel = "8080/kcp" +web = { addr = "127.0.0.1", port = "8888" } \ No newline at end of file diff --git a/src/bin/client.rs b/src/bin/client.rs index cc7d617..b8b23c6 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -8,17 +8,17 @@ use fuso::{ Config, FinalTarget, Host, ServerAddr, Service, WithBridgeService, WithForwardService, WithProxyService, }, - Expose, Stateful, + Compress, Crypto, Expose, Stateful, }, core::{ accepter::AccepterExt, connector::MultiConnector, - io::{AsyncReadExt, StreamExt}, + io::{AsyncReadExt, AsyncWriteExt, StreamExt}, net::{TcpListener, TcpStream}, protocol::{AsyncPacketRead, AsyncPacketSend}, rpc::{AsyncCall, Caller}, - stream::{handshake::Handshake, UseCompress, UseCrypto}, - transfer::AbstractTransmitter, + stream::{compress::CompressedStream, handshake::Handshake, UseCompress, UseCrypto}, + transfer::{AbstractTransmitter, TransmitterExt}, AbstractStream, Connection, }, error, @@ -82,6 +82,26 @@ fn enter_fuso_main(mut conf: Config) -> ServiceRunner<'static, TokioRuntime> { sp.build() } +async fn try_tcp_connect<'a, Cr, Co>( + addr: ServerAddr, + port: u16, + cryptos: Cr, + compress: Co, +) -> error::Result> +where + Cr: Iterator, + Co: Iterator, +{ + addr.try_connect(port, |host, port| async move { + match host { + Host::Ip(ip) => TcpStream::connect(SocketAddr::new(*ip, port)).await, + Host::Domain(domain) => TcpStream::connect(format!("{domain}:{port}")).await, + } + }) + .await + .map(|stream| stream.use_crypto(cryptos).use_compress(compress)) +} + async fn enter_forward_service_main( config: Stateful, service: WithForwardService, @@ -130,23 +150,17 @@ async fn enter_forward_service_main( Expose::Kcp(_, _) => todo!(), Expose::Tcp(_, port) => { let port = *port; + let cryptos = service.crypto.clone(); + let compress = service.compress.clone(); let addr = server.addr.clone(); connector.add(move |proto| { let addr = addr.clone(); + let cryptos = cryptos.clone(); + let compress = compress.clone(); async move { - addr.try_connect(port, |host, port| async move { - let connection = match host { - Host::Ip(ip) => { - TcpStream::connect(SocketAddr::new(*ip, port)).await - } - Host::Domain(domain) => { - TcpStream::connect(format!("{domain}:{port}")).await - } - }?; - - Ok(AbstractStream::new(connection).into()) - }) - .await + try_tcp_connect(addr, port, cryptos.iter(), compress.iter()) + .await + .map(Into::into) } }) } @@ -155,6 +169,7 @@ async fn enter_forward_service_main( } else { for expose in service.exposes.clone() { let addr = server.addr.clone(); + match expose { Expose::Kcp(_, port) => todo!(), Expose::Tcp(_, port) => connector.add(MuxConnector::new(move |proto| { @@ -179,22 +194,48 @@ async fn enter_forward_service_main( } } - let mut forwarder = PortForwarder::new(stream, connector); + let mut forwarder = PortForwarder::new(stream, service.target, connector); log::debug!("forward started ."); loop { let (mut linker, target) = forwarder.accept().await?; tokio::spawn(async move { - tokio::time::sleep(std::time::Duration::from_secs(5)).await; - match linker.link(Protocol::Tcp).await { - Ok(transmitter) => { - log::debug!("conneced ..."); - tokio::time::sleep(std::time::Duration::from_secs(5)).await; - - } - Err(e) => { - log::debug!("{:?}", e); + log::debug!("target {:?}", target); + + match target { + FinalTarget::Udp { addr, port } => todo!(), + FinalTarget::Shell { path, args } => todo!(), + FinalTarget::Dynamic => todo!(), + FinalTarget::Tcp { addr, port } => { + let result = addr + .try_connect(port, |host, port| async move { + match host { + Host::Ip(ip) => { + TcpStream::connect(SocketAddr::new(*ip, port)).await + } + Host::Domain(domain) => { + TcpStream::connect(format!("{domain}:{port}")).await + } + } + }) + .await; + + match result { + Ok(mut stream) => match linker.link(Protocol::Tcp).await { + Ok(mut transmitter) => { + log::debug!("start forwarding ......"); + } + Err(e) => { + log::debug!("{:?}", e); + } + }, + Err(e) => { + if let Err(e) = linker.cancel(e).await { + log::warn!("{:?}", e); + }; + } + } } } }); diff --git a/src/bin/main.rs b/src/bin/main.rs new file mode 100644 index 0000000..cee5e36 --- /dev/null +++ b/src/bin/main.rs @@ -0,0 +1,3 @@ +fn main(){ + +} \ No newline at end of file diff --git a/src/bin/server.rs b/src/bin/server.rs index 2c7665d..b61ec94 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -203,20 +203,20 @@ where let mut accepter = MultiAccepter::new(); - for expose in conf.exposes.iter() { - match expose { - Expose::Kcp(ip, port) => { - accepter.add(ForwardAccepter::new_visitor( - KcpListener::bind(Default::default(), ip.to_addr(*port)).await?, - )); + if let Some(ref channels) = conf.channel { + for expose in conf.exposes.iter() { + match expose { + Expose::Kcp(ip, port) => { + accepter.add(ForwardAccepter::new_visitor( + KcpListener::bind(Default::default(), ip.to_addr(*port)).await?, + )); + } + Expose::Tcp(ip, port) => accepter.add(ForwardAccepter::new_visitor( + TcpListener::bind(ip.to_addr(*port)).await?, + )), } - Expose::Tcp(ip, port) => accepter.add(ForwardAccepter::new_visitor( - TcpListener::bind(ip.to_addr(*port)).await?, - )), } - } - if let Some(ref channels) = conf.channel { for expose in channels.iter() { match expose { Expose::Tcp(ip, port) => { diff --git a/src/client/port_forward/linker.rs b/src/client/port_forward/linker.rs index 29c012d..f70d36e 100644 --- a/src/client/port_forward/linker.rs +++ b/src/client/port_forward/linker.rs @@ -22,6 +22,7 @@ pub struct Linker { pub enum Reason { Cancel, + Error(error::FusoError), } #[derive(Debug, Clone)] @@ -45,9 +46,10 @@ impl Linker { } pub async fn link(self, poto: Protocol) -> error::Result> { + let mut transmitter = self.connector.connect(poto).await?; + self.responder.replay(Response::Ok).await?; - let mut transmitter = self.connector.connect(poto).await?; let token = self.token.to_be_bytes(); transmitter.send_all(&token).await?; @@ -60,17 +62,29 @@ impl Linker { R: Into, { let reason = reason.into(); - - Ok(()) + self.responder + .replay({ + match reason { + Reason::Cancel => Response::Cancel, + Reason::Error(e) => Response::Error(e.to_string()), + } + }) + .await } } impl From<()> for Reason { - fn from(value: ()) -> Self { + fn from(_: ()) -> Self { Self::Cancel } } +impl From for Reason { + fn from(e: error::FusoError) -> Self { + Self::Error(e) + } +} + impl Display for Linker { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "token={}", self.token) diff --git a/src/client/port_forward/mod.rs b/src/client/port_forward/mod.rs index 68d019a..2f5c136 100644 --- a/src/client/port_forward/mod.rs +++ b/src/client/port_forward/mod.rs @@ -2,11 +2,11 @@ mod connector; mod linker; mod transport; -pub use linker::*; pub use connector::*; - +pub use linker::*; use crate::core::connector::ReplicableConnector; +use crate::core::rpc::structs::port_forward::Target; use crate::core::transfer::AbstractTransmitter; use crate::{ core::{ @@ -26,6 +26,7 @@ use crate::{ }; pub struct PortForwarder { + target: FinalTarget, transport: Transport<'static, S>, connector: ReplicableConnector<'static, Protocol, AbstractTransmitter<'static>>, _marked: PhantomData, @@ -36,13 +37,18 @@ where R: Runtime + 'static, S: Stream + Unpin + Send + 'static, { - pub fn new_with_runtime(transport: S, connector: C) -> Self + pub fn new_with_runtime(transport: S, target: FinalTarget, connector: C) -> Self where - C: Connector> + Sync + Send + Unpin + 'static, + C: Connector> + + Sync + + Send + + Unpin + + 'static, { let transport = Transport::new::(std::time::Duration::from_secs(1), transport); Self { + target, transport, connector: ReplicableConnector(Arc::new(AbstractConnector::new(connector))), _marked: PhantomData, @@ -68,8 +74,8 @@ where let linker = Linker::new(token, self.connector.clone(), responder); (linker, { match target { - None => FinalTarget::Dynamic, - Some(target) => FinalTarget::Dynamic, + None => self.target.clone(), + Some(target) => target.into(), } }) })), @@ -77,3 +83,12 @@ where } } } + +impl From for FinalTarget { + fn from(value: Target) -> Self { + match value { + Target::Udp(addr, port) => FinalTarget::Udp { addr, port }, + Target::Tcp(addr, port) => FinalTarget::Tcp { addr, port }, + } + } +} diff --git a/src/config/client.rs b/src/config/client.rs index 1be14f2..98e0fee 100644 --- a/src/config/client.rs +++ b/src/config/client.rs @@ -144,8 +144,15 @@ pub struct WithHttpHeaderRewrite { #[serde(tag = "type", rename_all = "lowercase")] pub enum FinalTarget { /// 静态地址 - Static { addr: ServerAddr, port: u16 }, - Executable { + Tcp { + addr: ServerAddr, + port: u16, + }, + Udp { + addr: ServerAddr, + port: u16, + }, + Shell { path: String, #[serde(default = "Default::default")] args: Vec, @@ -271,7 +278,6 @@ impl Config { } } - /// 默认随机分配一个端口 fn default_exposes() -> HashSet { let mut exposes = HashSet::new(); diff --git a/src/core/io/mod.rs b/src/core/io/mod.rs index dc357d7..dfdd26a 100644 --- a/src/core/io/mod.rs +++ b/src/core/io/mod.rs @@ -6,7 +6,7 @@ use std::{ use crate::{error, select}; -use super::BoxedFuture; +use super::{BoxedFuture, Stream}; use crate::core::split::SplitStream; @@ -79,7 +79,7 @@ pub trait StreamExt { fn transfer<'a, T>(self, to: T) -> BoxedFuture<'a, error::Result<()>> where T: AsyncRead + AsyncWrite + Unpin + Send + 'a, - Self: Sized + AsyncRead + AsyncWrite + Unpin + Send + 'a, + Self: Sized + Stream + Unpin + Send + 'a, { Box::pin(async move { let (reader_2, writer_2) = to.split(); @@ -103,7 +103,7 @@ pub trait StreamExt { break Err(io::Error::from(io::ErrorKind::UnexpectedEof).into()); } - to.write_all(&buf).await?; + to.write_all(&buf[..n]).await?; } }) } diff --git a/src/core/rpc/structs.rs b/src/core/rpc/structs.rs index 55ea329..730de9e 100644 --- a/src/core/rpc/structs.rs +++ b/src/core/rpc/structs.rs @@ -17,6 +17,7 @@ pub mod port_forward { #[derive(Debug, Serialize, Deserialize)] pub enum Response { Ok, + Cancel, Error(String), } diff --git a/src/core/transfer.rs b/src/core/transfer.rs index ccfaeb2..c6b1948 100644 --- a/src/core/transfer.rs +++ b/src/core/transfer.rs @@ -1,6 +1,6 @@ use std::{ io, - pin::{self, Pin}, + pin::Pin, task::{Context, Poll}, }; @@ -23,6 +23,13 @@ pub struct TransmitSendAll<'t, T> { transmitter: &'t mut T, } +#[pin_project::pin_project] +pub struct TransmitRecv<'t, T> { + buf: &'t mut [u8], + #[pin] + transmitter: &'t mut T, +} + pub trait Transmitter: Unpin { fn poll_send( self: Pin<&mut Self>, @@ -38,6 +45,16 @@ pub trait Transmitter: Unpin { } pub trait TransmitterExt: Transmitter { + fn recv<'a>(&'a mut self, buf: &'a mut [u8]) -> TransmitRecv<'a, Self> + where + Self: Sized, + { + TransmitRecv { + buf, + transmitter: self, + } + } + fn send<'a>(&'a mut self, data: &'a [u8]) -> TransmitSend<'a, Self> where Self: Sized, @@ -152,3 +169,15 @@ where Poll::Ready(Ok(())) } } + +impl<'t, T> std::future::Future for TransmitRecv<'t, T> +where + T: Transmitter, +{ + type Output = error::Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + Pin::new(&mut **this.transmitter).poll_recv(cx, this.buf) + } +} diff --git a/src/runtime/tokio/port_forward/mod.rs b/src/runtime/tokio/port_forward/mod.rs index eba89f4..8b423e5 100644 --- a/src/runtime/tokio/port_forward/mod.rs +++ b/src/runtime/tokio/port_forward/mod.rs @@ -1,7 +1,7 @@ use std::net::SocketAddr; use crate::{ - client::port_forward::Protocol, core::{ + client::port_forward::Protocol, config::client::FinalTarget, core::{ accepter::Accepter, connector::Connector, processor::Preprocessor, rpc::structs::port_forward::VisitorProtocol, transfer::AbstractTransmitter, AbstractStream, Connection, Stream }, error, server::port_forward::{MuxAccepter, Whence} }; @@ -37,10 +37,10 @@ impl crate::client::port_forward::PortForwarder where S: Stream + Send + Unpin + 'static, { - pub fn new(transport: S, connector: C) -> Self + pub fn new(transport: S, target: FinalTarget, connector: C) -> Self where C: Connector> + Sync + Send + Unpin + 'static, { - Self::new_with_runtime(transport, connector) + Self::new_with_runtime(transport, target, connector) } } diff --git a/src/server/port_forward/mod.rs b/src/server/port_forward/mod.rs index fc99b36..ca5dae7 100644 --- a/src/server/port_forward/mod.rs +++ b/src/server/port_forward/mod.rs @@ -144,7 +144,7 @@ where Outcome::Timeout(token) => { if let Some(conn) = self.visitors.take(token) { log::warn!( - "failed to create mapping {{ token={}, addr={}, msg='timeout' }}", + "failed to create mapping {{ token={}, addr={}, reason='Timeout' }}", token, conn.addr() ); @@ -167,7 +167,7 @@ where Outcome::Transport(token, transport) => { if let Some(conn) = self.visitors.take(token) { log::warn!( - "failed to create mapping {{ token={}, addr={}, msg='{}' }}", + "failed to create mapping {{ token={}, addr={}, reason='{}' }}", token, conn.addr(), transport @@ -271,6 +271,7 @@ where Err(e) => Err(e), Ok(resp) => match resp { port_forward::Response::Ok => Ok(()), + port_forward::Response::Cancel => Err(error::FusoError::Cancel), port_forward::Response::Error(msg) => Err(msg.into()), }, } From 2e3888545b50cdaf4371a21dad11e17d8e4f1652 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Mon, 6 May 2024 00:45:27 +0800 Subject: [PATCH 17/29] =?UTF-8?q?=E5=AE=8C=E5=96=84=E9=83=A8=E5=88=86?= =?UTF-8?q?=E7=AB=AF=E5=8F=A3=E8=BD=AC=E5=8F=91=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 54 ++++++++++++++++++++----- Cargo.toml | 5 ++- config/client.toml | 3 ++ src/bin/client.rs | 51 ++++++++++++++--------- src/bin/server.rs | 17 +++++--- src/client/port_forward/connector.rs | 20 +++++---- src/config/client.rs | 21 ++++++++++ src/config/mod.rs | 6 +-- src/core/connection.rs | 0 src/core/connector.rs | 7 +--- src/core/mod.rs | 1 + src/core/processor.rs | 10 ++--- src/core/rpc/keep.rs | 12 ++++-- src/core/rpc/mod.rs | 11 +++-- src/core/stream/handshake.rs | 20 ++++++++- src/error/mod.rs | 20 +++++++-- src/runtime/tokio/port_forward/mod.rs | 10 ++--- src/server/port_forward/accepter.rs | 9 +++-- src/server/port_forward/mod.rs | 23 ++++++----- src/server/port_forward/preprocessor.rs | 14 +++++++ 20 files changed, 225 insertions(+), 89 deletions(-) create mode 100644 src/core/connection.rs diff --git a/Cargo.lock b/Cargo.lock index 6841b49..4070fc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -161,21 +161,18 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.5.0" @@ -309,7 +306,6 @@ name = "fuso" version = "1.0.5-beta" dependencies = [ "axum", - "bincode", "cc", "clap", "env_logger", @@ -320,6 +316,7 @@ dependencies = [ "pin-project", "rand", "rc4", + "rmp-serde", "serde", "tokio", "toml", @@ -672,6 +669,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.16.0" @@ -720,6 +726,12 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -859,6 +871,28 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "rmp" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "rustc-demangle" version = "0.1.23" diff --git a/Cargo.toml b/Cargo.toml index 664565c..cafb5bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,10 +37,11 @@ codegen-units = 1 panic = 'abort' [features] -default = ["fuso-config", "fuso-cli", "fuso-kcp", "fuso-rt-tokio", "fuso-manager"] +default = ["fuso-config", "fuso-cli", "fuso-kcp", "fuso-rt-tokio", "fuso-manager", "fuso-socks5"] fuso-kcp = ["kcp-rust"] fuso-cli = ["clap"] fuso-serde = ["serde"] +fuso-socks5 = [ "dep:fuso-socks" ] fuso-config = ["toml", "serde"] fuso-runtime = [] fuso-rt-tokio = ["dep:tokio", "fuso-runtime"] @@ -57,7 +58,7 @@ env_logger = "0.11.2" log = "0.4.16" pin-project = "1.1.4" parking_lot = "0.12.1" -bincode = "1.3.3" +rmp-serde = "1.3.0" rand = "0.8.5" [dependencies.kcp-rust] diff --git a/config/client.toml b/config/client.toml index 3d6e69a..cb1ac11 100644 --- a/config/client.toml +++ b/config/client.toml @@ -20,6 +20,9 @@ compress = ["lz4"] target = { type = "tcp", port = 9999, addr = ["127.0.0.1"] } keep_alive = { interval = 100 } +[ssh1.prepares.socks5] +auth = { type = "secret", secret = "1234" } + # [executable] # type = "forward" diff --git a/src/bin/client.rs b/src/bin/client.rs index b8b23c6..f80bd21 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -16,8 +16,12 @@ use fuso::{ io::{AsyncReadExt, AsyncWriteExt, StreamExt}, net::{TcpListener, TcpStream}, protocol::{AsyncPacketRead, AsyncPacketSend}, - rpc::{AsyncCall, Caller}, - stream::{compress::CompressedStream, handshake::Handshake, UseCompress, UseCrypto}, + rpc::{AsyncCall, Caller, Encoder}, + stream::{ + compress::CompressedStream, + handshake::{Handshake, MuxConfig}, + UseCompress, UseCrypto, + }, transfer::{AbstractTransmitter, TransmitterExt}, AbstractStream, Connection, }, @@ -167,29 +171,35 @@ async fn enter_forward_service_main( } } } else { + let mux: MuxConfig = MuxConfig::default(); + + stream.send_packet(&mux.borrow_encode()?).await?; + for expose in service.exposes.clone() { let addr = server.addr.clone(); match expose { Expose::Kcp(_, port) => todo!(), - Expose::Tcp(_, port) => connector.add(MuxConnector::new(move |proto| { - let addr = addr.clone(); - async move { - addr.try_connect(port, |host, port| async move { - let connection = match host { - Host::Ip(ip) => { - TcpStream::connect(SocketAddr::new(*ip, port)).await - } - Host::Domain(domain) => { - TcpStream::connect(format!("{domain}:{port}")).await - } - }?; - - Ok(AbstractStream::new(connection).into()) - }) - .await - } - })), + Expose::Tcp(_, port) => { + connector.add(MuxConnector::new(mux.clone(), move |proto| { + let addr = addr.clone(); + async move { + addr.try_connect(port, |host, port| async move { + let connection = match host { + Host::Ip(ip) => { + TcpStream::connect(SocketAddr::new(*ip, port)).await + } + Host::Domain(domain) => { + TcpStream::connect(format!("{domain}:{port}")).await + } + }?; + + Ok(AbstractStream::new(connection).into()) + }) + .await + } + })) + } } } } @@ -225,6 +235,7 @@ async fn enter_forward_service_main( Ok(mut stream) => match linker.link(Protocol::Tcp).await { Ok(mut transmitter) => { log::debug!("start forwarding ......"); + } Err(e) => { log::debug!("{:?}", e); diff --git a/src/bin/server.rs b/src/bin/server.rs index b61ec94..733e74f 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -4,7 +4,7 @@ use std::{ time::Duration, }; -use bincode::de; + use fuso::{ config::{client::WithForwardService, server::Config, Expose, Stateful}, core::{ @@ -15,10 +15,11 @@ use fuso::{ net::{KcpListener, TcpListener}, processor::{IProcessor, Processor, StreamProcessor}, protocol::{AsyncPacketRead, AsyncPacketSend}, + rpc::Decoder, split::SplitStream, stream::{ fallback::Fallback, - handshake::{ClientConfig, ForwardConfig, Handshake}, + handshake::{ClientConfig, ForwardConfig, Handshake, MuxConfig}, UseCompress, UseCrypto, }, AbstractStream, Stream, @@ -195,7 +196,7 @@ pub async fn handle_connection( Ok(()) } -pub async fn enter_forwarder(conf: ForwardConfig, transport: S) -> error::Result<()> +pub async fn enter_forwarder(conf: ForwardConfig, mut transport: S) -> error::Result<()> where S: Stream + Send + Unpin + 'static, { @@ -233,7 +234,13 @@ where } } else { accepter.add({ + + log::debug!("using mux accepter"); + + let mux: MuxConfig = transport.recv_packet().await?.decode()?; + let mut accepter = MultiAccepter::new(); + for expose in conf.exposes.iter() { match expose { Expose::Tcp(ip, port) => { @@ -249,13 +256,13 @@ where } } - MuxAccepter::new(1110, rand::random(), accepter) + MuxAccepter::new(mux, accepter) }); } let mut forwarder = PortForwarder::new(transport, accepter, (), None); - log::debug!("forward started .."); + log::debug!("port forwarder started ."); loop { let (c1, c2) = forwarder.accept().await?; diff --git a/src/client/port_forward/connector.rs b/src/client/port_forward/connector.rs index 46fb760..82068f2 100644 --- a/src/client/port_forward/connector.rs +++ b/src/client/port_forward/connector.rs @@ -1,24 +1,25 @@ -use std::marker::PhantomData; +use rc4::{KeyInit, Rc4, StreamCipher}; use crate::core::{ connector::{AbstractConnector, Connector, IntoConnector}, - io::AsyncWriteExt, - protocol::AsyncPacketSend, - transfer::AbstractTransmitter, + stream::handshake::MuxConfig, + transfer::{AbstractTransmitter, TransmitterExt}, }; use super::Protocol; pub struct MuxConnector<'connector> { + conf: MuxConfig, inner: AbstractConnector<'connector, Protocol, AbstractTransmitter<'connector>>, } impl<'connector> MuxConnector<'connector> { - pub fn new(connector: I) -> Self + pub fn new(conf: MuxConfig, connector: I) -> Self where I: IntoConnector<'connector, C, Protocol, AbstractTransmitter<'connector>>, { Self { + conf, inner: connector.into(), } } @@ -32,9 +33,14 @@ impl<'connector> Connector for MuxConnector<'connector> { target: Protocol, ) -> crate::core::BoxedFuture<'conn, crate::error::Result> { Box::pin(async move { - let transmitter = self.inner.connect(target).await?; + let mut transmitter = self.inner.connect(target).await?; + let mut magic = self.conf.magic.to_le_bytes(); - unimplemented!() + Rc4::new((&self.conf.secret).into()).apply_keystream(&mut magic); + + transmitter.send_all(&magic).await?; + + Ok(transmitter) }) } } diff --git a/src/config/client.rs b/src/config/client.rs index 98e0fee..778da66 100644 --- a/src/config/client.rs +++ b/src/config/client.rs @@ -91,6 +91,23 @@ pub struct WithForwardService { /// 压缩方式 #[serde(default = "Default::default")] pub compress: HashSet, + /// 访问端建立连接后的前置处理方式 + #[serde(default = "Default::default")] + pub prepares: PrepareForward, +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize, Hash, PartialEq, Eq)] +pub struct PrepareForward { + socks5: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize, Hash, PartialEq, Eq)] +pub struct WithSocks5Prepare { + /// 认证方式 + auth: Authentication, + /// 是否启动udp转发 + #[serde(default = "default_socks5_udp_forward")] + udp_forward: bool, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -287,6 +304,10 @@ fn default_exposes() -> HashSet { exposes } +fn default_socks5_udp_forward() -> bool { + true +} + #[cfg(test)] #[cfg(feature = "fuso-toml")] mod tests { diff --git a/src/config/mod.rs b/src/config/mod.rs index 19e9089..226a8aa 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -12,7 +12,7 @@ use crate::error; pub mod client; pub mod server; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)] #[serde(tag = "type", rename_all = "lowercase")] pub enum Authentication { @@ -21,12 +21,12 @@ pub enum Authentication { Account(AuthWithAccount), } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)] pub struct AuthWithSecret { secret: String, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)] pub struct AuthWithAccount { username: String, password: String, diff --git a/src/core/connection.rs b/src/core/connection.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/core/connector.rs b/src/core/connector.rs index b835e53..571fce4 100644 --- a/src/core/connector.rs +++ b/src/core/connector.rs @@ -1,9 +1,4 @@ -use std::{ - future::Future, - pin::Pin, - sync::Arc, - task::{Context, Poll}, -}; +use std::{future::Future, sync::Arc}; use crate::error; diff --git a/src/core/mod.rs b/src/core/mod.rs index 7d69980..7e8ea69 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -23,6 +23,7 @@ pub mod stream; pub mod task; pub mod token; pub mod transfer; +pub mod connection; pub type BoxedFuture<'a, O> = Pin + Send + 'a>>; diff --git a/src/core/processor.rs b/src/core/processor.rs index 07d28a2..ec36806 100644 --- a/src/core/processor.rs +++ b/src/core/processor.rs @@ -39,7 +39,7 @@ pub struct Processor<'a, A, R> { processors: Vec>, } -pub struct WrappedPreprocessor<'a, In, Out>( +pub struct AbstractPreprocessor<'a, In, Out>( pub(crate) Arc + Sync + Send + 'a>, ); @@ -47,8 +47,8 @@ impl IProcessor for AbstractProcessor<'_, A, R> where A: Send, { - fn process<'a>(&'a mut self, data: A) -> BoxedFuture<'a, Process> { - self.0.process(data) + fn process<'a>(&'a mut self, input: A) -> BoxedFuture<'a, Process> { + self.0.process(input) } } @@ -87,14 +87,14 @@ impl<'a, A, R> Processor<'a, A, R> { } } -impl Preprocessor for WrappedPreprocessor<'_, In, Out> { +impl Preprocessor for AbstractPreprocessor<'_, In, Out> { type Output = Out; fn prepare<'a>(&'a self, input: In) -> BoxedFuture<'a, Self::Output> { self.0.prepare(input) } } -impl Clone for WrappedPreprocessor<'_, In, Out> { +impl Clone for AbstractPreprocessor<'_, In, Out> { fn clone(&self) -> Self { Self(self.0.clone()) } diff --git a/src/core/rpc/keep.rs b/src/core/rpc/keep.rs index 1426473..1d7cd9d 100644 --- a/src/core/rpc/keep.rs +++ b/src/core/rpc/keep.rs @@ -13,9 +13,15 @@ impl Heartbeat { (Poller::new(), Self {}) } - pub(super) fn pong(&self) {} + pub(super) fn pong(&self) { + + } + + pub(super) fn ping(&self) { - pub(super) fn ping(&self) {} + } - pub(super) fn interrupt(&self) {} + pub(super) fn interrupt(&self) { + + } } diff --git a/src/core/rpc/mod.rs b/src/core/rpc/mod.rs index aa1bc9a..947d93d 100644 --- a/src/core/rpc/mod.rs +++ b/src/core/rpc/mod.rs @@ -6,7 +6,6 @@ mod polling; pub mod structs; use std::{ - marker::PhantomData, pin::Pin, task::{Context, Poll}, }; @@ -49,6 +48,8 @@ pub trait AsyncCallee { pub trait Encoder { fn encode(self) -> error::Result>; + + fn borrow_encode(&self) -> error::Result>; } pub trait Decoder { @@ -61,7 +62,11 @@ where T: serde::Serialize, { fn encode(self) -> error::Result> { - bincode::serialize(&self).map_err(Into::into) + self.borrow_encode() + } + + fn borrow_encode(&self) -> error::Result> { + rmp_serde::to_vec(self).map_err(Into::into) } } @@ -72,6 +77,6 @@ where type Output = T; fn decode(self) -> error::Result { - bincode::deserialize(&self).map_err(Into::into) + rmp_serde::from_slice(&self).map_err(Into::into) } } diff --git a/src/core/stream/handshake.rs b/src/core/stream/handshake.rs index 43197c3..78c26d3 100644 --- a/src/core/stream/handshake.rs +++ b/src/core/stream/handshake.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use crate::{ config::{ - client::WithForwardService, AuthWithAccount, AuthWithSecret, Authentication, Compress, + client::{PrepareForward, WithForwardService}, AuthWithAccount, AuthWithSecret, Authentication, Compress, Crypto, Expose, }, core::{ @@ -27,12 +27,19 @@ pub enum ClientConfig { Forward(ForwardConfig), } +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct MuxConfig { + pub magic: u32, + pub secret: [u8; 16], +} + #[derive(Debug, Serialize, Deserialize)] pub struct ForwardConfig { pub exposes: HashSet, pub channel: Option>, pub cryptos: HashSet, pub compress: HashSet, + pub prepares: PrepareForward, } impl Handshake for T where T: Stream + Send + Unpin {} @@ -138,9 +145,20 @@ impl<'a> From<&'a WithForwardService> for ClientConfig { fn from(value: &'a WithForwardService) -> Self { Self::Forward(ForwardConfig { exposes: value.exposes.clone(), + prepares: value.prepares.clone(), channel: value.channel.clone(), cryptos: value.crypto.clone(), compress: value.compress.clone(), }) } } + +impl Default for MuxConfig { + fn default() -> Self { + Self { + // @MUX + magic: 0x404d5558, + secret: rand::random(), + } + } +} diff --git a/src/error/mod.rs b/src/error/mod.rs index 4e30917..96552c5 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -16,11 +16,17 @@ pub enum FusoError { InvalidExposeType, NotResponse, Custom(String), - Bincode(bincode::Error), + MsgPack(MsgPack), TomlDeError(toml::de::Error), StdIo(std::io::Error), } +#[derive(Debug)] +pub enum MsgPack{ + Encode(rmp_serde::encode::Error), + Decode(rmp_serde::decode::Error) +} + impl From for FusoError { fn from(value: std::io::Error) -> Self { Self::StdIo(value) @@ -33,9 +39,15 @@ impl From for FusoError { } } -impl From for FusoError { - fn from(value: bincode::Error) -> Self { - Self::Bincode(value) +impl From for FusoError { + fn from(value: rmp_serde::encode::Error) -> Self { + Self::MsgPack(MsgPack::Encode(value)) + } +} + +impl From for FusoError { + fn from(value: rmp_serde::decode::Error) -> Self { + Self::MsgPack(MsgPack::Decode(value)) } } diff --git a/src/runtime/tokio/port_forward/mod.rs b/src/runtime/tokio/port_forward/mod.rs index 8b423e5..14e351d 100644 --- a/src/runtime/tokio/port_forward/mod.rs +++ b/src/runtime/tokio/port_forward/mod.rs @@ -2,7 +2,7 @@ use std::net::SocketAddr; use crate::{ client::port_forward::Protocol, config::client::FinalTarget, core::{ - accepter::Accepter, connector::Connector, processor::Preprocessor, rpc::structs::port_forward::VisitorProtocol, transfer::AbstractTransmitter, AbstractStream, Connection, Stream + accepter::Accepter, connector::Connector, processor::Preprocessor, rpc::structs::port_forward::VisitorProtocol, stream::handshake::MuxConfig, transfer::AbstractTransmitter, AbstractStream, Connection, Stream }, error, server::port_forward::{MuxAccepter, Whence} }; @@ -12,8 +12,8 @@ impl MuxAccepter where A: Accepter)> + Unpin + Send, { - pub fn new(magic: u32, secret: [u8; 16], accepter: A) -> Self { - Self::new_runtime(accepter, magic, secret) + pub fn new(conf: MuxConfig, accepter: A) -> Self { + Self::new_runtime(accepter, conf) } } @@ -22,14 +22,14 @@ where A: Accepter + Unpin + 'static, T: Stream + Send + Unpin + 'static, { - pub fn new(stream: T, accepter: A, prepvis: P, prepmap: M) -> Self + pub fn new(transport: T, accepter: A, prepvis: P, prepmap: M) -> Self where P: Preprocessor, Output = error::Result>, P: Send + Sync + 'static, M: Preprocessor, Output = error::Result>>, M: Send + Sync + 'static, { - Self::new_with_runtime(stream, accepter, prepvis, prepmap) + Self::new_with_runtime(transport, accepter, prepvis, prepmap) } } diff --git a/src/server/port_forward/accepter.rs b/src/server/port_forward/accepter.rs index b244dc4..0849fd9 100644 --- a/src/server/port_forward/accepter.rs +++ b/src/server/port_forward/accepter.rs @@ -10,6 +10,7 @@ use rc4::{KeyInit, Rc4, StreamCipher}; use super::{Connection, Rc4MagicHandshake, Whence}; use crate::core::future::Poller; use crate::core::io::AsyncReadExt; +use crate::core::stream::handshake::MuxConfig; use crate::core::Stream; use crate::core::{accepter::Accepter, AbstractStream}; use crate::error; @@ -100,7 +101,7 @@ where Rc4::new((&handshaker.secret).into()) .try_apply_keystream(&mut buf) - .map(|()| handshaker.expect.eq(&u32::from_be_bytes(buf))) + .map(|()| handshaker.expect.eq(&u32::from_le_bytes(buf))) .map_or_else(|_| Ok(false), |z| Ok(z)) } } @@ -110,13 +111,13 @@ impl MuxAccepter where A: Accepter)> + Unpin + Send, { - pub fn new_runtime(accepter: A, magic: u32, secret: [u8; 16]) -> Self { + pub fn new_runtime(accepter: A, conf: MuxConfig) -> Self { Self { accepter, connections: Poller::new(), handshaker: Arc::new(Rc4MagicHandshake { - secret, - expect: magic, + expect: conf.magic, + secret: conf.secret, }), _marked: PhantomData, } diff --git a/src/server/port_forward/mod.rs b/src/server/port_forward/mod.rs index ca5dae7..df6c1f9 100644 --- a/src/server/port_forward/mod.rs +++ b/src/server/port_forward/mod.rs @@ -25,7 +25,7 @@ use crate::{ core::{ accepter::Accepter, io::{AsyncRead, AsyncWrite}, - processor::{Preprocessor, WrappedPreprocessor}, + processor::{Preprocessor, AbstractPreprocessor}, rpc::structs::port_forward::{Request, VisitorProtocol}, }, error, @@ -60,8 +60,8 @@ pub struct PortForwarder { accepter: A, visitors: Visitors, transport: Transport, - prepmap: WrappedPreprocessor<'static, Connection, error::Result>, - prepvis: WrappedPreprocessor<'static, Connection, error::Result>, + prepmap: AbstractPreprocessor<'static, Connection, error::Result>, + prepvis: AbstractPreprocessor<'static, Connection, error::Result>, _marked: PhantomData, } @@ -126,7 +126,7 @@ where match outcome { Outcome::Mapped(token) => match self.visitors.take(token) { None => { - log::debug!("mapping successful {{ token={token} }}") + log::trace!("mapping successful {{ token={token} }}") } Some(_) => { log::debug!("mapping error {{ token={token} }}") @@ -188,7 +188,7 @@ where T: Stream + Unpin + Send + 'static, R: Runtime + 'static, { - pub fn new_with_runtime(stream: T, accepter: A, prepvis: P, prepmap: M) -> Self + pub fn new_with_runtime(transport: T, accepter: A, prepvis: P, prepmap: M) -> Self where P: Preprocessor>, P: Send + Sync + 'static, @@ -197,7 +197,7 @@ where { let mut poller = Poller::new(); - let (transport, hold) = Transport::new::(std::time::Duration::from_secs(10), stream); + let (transport, hold) = Transport::new::(std::time::Duration::from_secs(10), transport); poller.add(async move { match hold.await { @@ -211,8 +211,8 @@ where accepter, transport, visitors: Default::default(), - prepvis: WrappedPreprocessor(Arc::new(prepvis)), - prepmap: WrappedPreprocessor(Arc::new(prepmap)), + prepvis: AbstractPreprocessor(Arc::new(prepvis)), + prepmap: AbstractPreprocessor(Arc::new(prepmap)), _marked: PhantomData, } } @@ -228,7 +228,8 @@ where getter: Getter<()>, timeout: std::time::Duration, ) -> error::Result { - log::debug!("wait for mapping {{ token={token}, timeout={timeout:?} }}"); + log::trace!("wait for mapping {{ token={token}, timeout={timeout:?} }}"); + let mut select = Select::new(); select.add(async move { @@ -248,7 +249,7 @@ where conn: Connection, visitors: Visitors, transport: Transport, - preprocessor: WrappedPreprocessor<'_, Connection, error::Result>, + preprocessor: AbstractPreprocessor<'_, Connection, error::Result>, ) -> error::Result { let mut transport = transport; @@ -290,7 +291,7 @@ where async fn do_prepare_mapping( conn: Connection, _: Transport, - preprocessor: WrappedPreprocessor<'_, Connection, error::Result>, + preprocessor: AbstractPreprocessor<'_, Connection, error::Result>, ) -> error::Result { let mut conn = preprocessor.prepare(conn).await?; diff --git a/src/server/port_forward/preprocessor.rs b/src/server/port_forward/preprocessor.rs index ff90dc0..72bb219 100644 --- a/src/server/port_forward/preprocessor.rs +++ b/src/server/port_forward/preprocessor.rs @@ -5,6 +5,12 @@ use crate::{ use super::Connection; + +pub struct Socks5Preprocessor { + udp_forward: bool, +} + + impl Preprocessor for () { type Output = error::Result; @@ -19,3 +25,11 @@ impl Preprocessor for Option<()> { Box::pin(async move { Ok(input) }) } } + + +impl Preprocessor for Socks5Preprocessor { + type Output = error::Result; + fn prepare<'a>(&'a self, input: Connection) -> crate::core::BoxedFuture<'a, Self::Output> { + unimplemented!() + } +} From 1842e2ee0a80af23b362adcb0ed0ece5f71b1c44 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Thu, 9 May 2024 09:02:54 +0800 Subject: [PATCH 18/29] =?UTF-8?q?=E5=AE=8C=E5=96=84=E9=83=A8=E5=88=86?= =?UTF-8?q?=E7=AB=AF=E5=8F=A3=E8=BD=AC=E5=8F=91=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bin/client.rs | 23 ++- src/bin/main.rs | 28 ++- src/bin/server.rs | 13 +- src/config/client.rs | 55 ++++-- src/core/io/mod.rs | 42 ++-- src/core/mod.rs | 32 ++-- src/core/net/kcp.rs | 2 +- src/core/net/udp.rs | 167 ++++++++++++++-- src/core/rpc/mod.rs | 2 +- src/core/rpc/structs.rs | 2 +- src/core/stream/mod.rs | 1 + src/core/stream/virio.rs | 244 ++++++++++++++++++++++++ src/core/transfer.rs | 77 +++++++- src/error/mod.rs | 15 +- src/runtime/tokio/kcp.rs | 42 ++-- src/server/port_forward/accepter.rs | 2 +- src/server/port_forward/preprocessor.rs | 154 ++++++++++++++- 17 files changed, 782 insertions(+), 119 deletions(-) create mode 100644 src/core/stream/virio.rs diff --git a/src/bin/client.rs b/src/bin/client.rs index f80bd21..866b7de 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -13,7 +13,7 @@ use fuso::{ core::{ accepter::AccepterExt, connector::MultiConnector, - io::{AsyncReadExt, AsyncWriteExt, StreamExt}, + io::{AsyncReadExt, AsyncWriteExt}, net::{TcpListener, TcpStream}, protocol::{AsyncPacketRead, AsyncPacketSend}, rpc::{AsyncCall, Caller, Encoder}, @@ -214,7 +214,17 @@ async fn enter_forward_service_main( log::debug!("target {:?}", target); match target { - FinalTarget::Udp { addr, port } => todo!(), + FinalTarget::Udp { addr, port } => { + let mut a = linker.link(Protocol::Tcp).await.unwrap(); + + let mut buf = [0u8; 1024]; + + let n = a.recv(&mut buf).await.unwrap(); + + + log::debug!("{:?}", &buf[..n]); + + } FinalTarget::Shell { path, args } => todo!(), FinalTarget::Dynamic => todo!(), FinalTarget::Tcp { addr, port } => { @@ -232,10 +242,9 @@ async fn enter_forward_service_main( .await; match result { - Ok(mut stream) => match linker.link(Protocol::Tcp).await { - Ok(mut transmitter) => { - log::debug!("start forwarding ......"); - + Ok(stream) => match linker.link(Protocol::Tcp).await { + Ok(transmitter) => { + transmitter.transfer(stream).await; } Err(e) => { log::debug!("{:?}", e); @@ -323,7 +332,7 @@ async fn enter_bridge_service_main( match result { Ok(upstream) => { - upstream.copy(&mut stream).await; + upstream.transfer(stream).await; } Err(e) => {} }; diff --git a/src/bin/main.rs b/src/bin/main.rs index cee5e36..e46e978 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -1,3 +1,25 @@ -fn main(){ - -} \ No newline at end of file +use std::io::Read; + +use tokio::io::AsyncReadExt; + +#[tokio::main] +async fn main() { + let mut c = tun::Configuration::default(); + + c.address((10, 0, 0, 0)).netmask((255, 255, 255, 0)).up(); + + #[cfg(target_os = "linux")] + c.platform(|config| { + config.packet_information(true); + }); + + let mut dev = tun::create_as_async(&c).unwrap(); + + let mut buf = [0; 4096]; + + loop { + let amount = dev.read(&mut buf).await.unwrap(); + + println!("{:?}", &buf[0..amount]); + } +} diff --git a/src/bin/server.rs b/src/bin/server.rs index 733e74f..9ac75db 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -4,14 +4,13 @@ use std::{ time::Duration, }; - use fuso::{ config::{client::WithForwardService, server::Config, Expose, Stateful}, core::{ accepter::{AccepterExt, MultiAccepter, StreamAccepter, TaggedAccepter}, future::Select, handshake::Handshaker, - io::{AsyncReadExt, AsyncWriteExt, StreamExt}, + io::{AsyncReadExt, AsyncWriteExt}, net::{KcpListener, TcpListener}, processor::{IProcessor, Processor, StreamProcessor}, protocol::{AsyncPacketRead, AsyncPacketSend}, @@ -22,11 +21,12 @@ use fuso::{ handshake::{ClientConfig, ForwardConfig, Handshake, MuxConfig}, UseCompress, UseCrypto, }, + transfer::TransmitterExt, AbstractStream, Stream, }, error, - runtime::tokio::TokioRuntime, - server::port_forward::{ForwardAccepter, MuxAccepter, PortForwarder}, + runtime::tokio::{UdpWithTokioRuntime, TokioRuntime, TokioUdpSocket}, + server::port_forward::{ForwardAccepter, MuxAccepter, PortForwarder, Socks5Preprocessor}, }; use kcp_rust::KcpConfig; @@ -234,7 +234,6 @@ where } } else { accepter.add({ - log::debug!("using mux accepter"); let mux: MuxConfig = transport.recv_packet().await?.decode()?; @@ -260,7 +259,9 @@ where }); } - let mut forwarder = PortForwarder::new(transport, accepter, (), None); + let previs = Socks5Preprocessor::::new(); + + let mut forwarder = PortForwarder::new(transport, accepter, previs, None); log::debug!("port forwarder started ."); diff --git a/src/config/client.rs b/src/config/client.rs index 778da66..abd907d 100644 --- a/src/config/client.rs +++ b/src/config/client.rs @@ -3,6 +3,7 @@ use std::{ fmt::Display, io, net::{IpAddr, Ipv4Addr}, + str::FromStr, }; use crate::error; @@ -50,12 +51,16 @@ pub struct Server { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(untagged)] -pub enum ServerAddr { - WithIpAddr(Vec), - WithDomain(Vec), +#[serde(into = "String")] +#[serde(try_from = "String")] +pub enum Addr { + WithIpAddr(IpAddr), + WithDomain(String), } +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ServerAddr(pub Vec); + #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "lowercase")] pub enum Service { @@ -205,16 +210,14 @@ impl ServerAddr { F: Fn(Host<'a>, u16) -> Fut + 'a, Fut: std::future::Future> + 'a, { - match self { - ServerAddr::WithIpAddr(addrs) => { - for ip in addrs { + for addr in self.0.iter() { + match addr { + Addr::WithIpAddr(ip) => { if let Ok(output) = f(Host::Ip(ip), port).await { return Ok(output); } } - } - ServerAddr::WithDomain(domains) => { - for domain in domains { + Addr::WithDomain(domain) => { if let Ok(output) = f(Host::Domain(domain), port).await { return Ok(output); } @@ -250,7 +253,9 @@ impl Default for Config { fn default() -> Self { Config { server: Server { - addr: ServerAddr::WithIpAddr(vec![IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))]), + addr: ServerAddr(vec![Addr::WithIpAddr(IpAddr::V4(Ipv4Addr::new( + 127, 0, 0, 1, + )))]), ports: vec![6528], retries: -1, // always crypto: Default::default(), @@ -295,6 +300,24 @@ impl Config { } } +impl From for Addr { + fn from(value: String) -> Self { + match IpAddr::from_str(&value) { + Ok(ip) => Self::WithIpAddr(ip), + Err(_) => Self::WithDomain(value), + } + } +} + +impl From for String { + fn from(value: Addr) -> Self { + match value { + Addr::WithIpAddr(ip) => ip.to_string(), + Addr::WithDomain(domain) => domain, + } + } +} + /// 默认随机分配一个端口 fn default_exposes() -> HashSet { let mut exposes = HashSet::new(); @@ -309,9 +332,11 @@ fn default_socks5_udp_forward() -> bool { } #[cfg(test)] -#[cfg(feature = "fuso-toml")] +// #[cfg(feature = "fuso-toml")] mod tests { + use crate::core::rpc::{Decoder, Encoder}; + use super::Config; #[test] @@ -330,4 +355,10 @@ mod tests { Ok(()) } + + #[test] + fn test_serialize(){ + let a = super::Config::default(); + let a: super::Config = a.encode().unwrap().decode().unwrap(); + } } diff --git a/src/core/io/mod.rs b/src/core/io/mod.rs index dfdd26a..29deab0 100644 --- a/src/core/io/mod.rs +++ b/src/core/io/mod.rs @@ -75,42 +75,24 @@ pub trait AsyncWriteExt: AsyncWrite + Unpin { } } -pub trait StreamExt { - fn transfer<'a, T>(self, to: T) -> BoxedFuture<'a, error::Result<()>> - where - T: AsyncRead + AsyncWrite + Unpin + Send + 'a, - Self: Sized + Stream + Unpin + Send + 'a, - { - Box::pin(async move { - let (reader_2, writer_2) = to.split(); - let (reader_1, writer_1) = self.split(); - select![reader_1.copy(writer_2), reader_2.copy(writer_1)] - }) - } - - fn copy<'a, T>(mut self, mut to: T) -> BoxedFuture<'a, error::Result<()>> - where - T: AsyncWrite + Unpin + Send + 'a, - Self: AsyncRead + Unpin + Send + Sized + 'a, - { - Box::pin(async move { - let mut buf = [0u8; 2048]; +pub async fn copy<'a, W, R>(mut writer: W, mut reader: R) -> error::Result<()> +where + W: AsyncWrite + Unpin + Send + 'a, + R: AsyncRead + Unpin + Send + Sized + 'a, +{ + let mut buf = [0u8; 2048]; - loop { - let n = self.read(&mut buf).await?; + loop { + let n = reader.read(&mut buf).await?; - if n == 0 { - break Err(io::Error::from(io::ErrorKind::UnexpectedEof).into()); - } + if n == 0 { + break Err(io::Error::from(io::ErrorKind::UnexpectedEof).into()); + } - to.write_all(&buf[..n]).await?; - } - }) + writer.write_all(&buf[..n]).await?; } } -impl StreamExt for T {} - #[pin_project::pin_project] pub struct Read<'a, R> { reader: &'a mut R, diff --git a/src/core/mod.rs b/src/core/mod.rs index 7e8ea69..6c5bb1a 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -10,6 +10,7 @@ use self::io::{AsyncRead, AsyncWrite}; pub mod accepter; pub mod channel; +pub mod connection; pub mod connector; pub mod future; pub mod handshake; @@ -23,7 +24,6 @@ pub mod stream; pub mod task; pub mod token; pub mod transfer; -pub mod connection; pub type BoxedFuture<'a, O> = Pin + Send + 'a>>; @@ -152,23 +152,25 @@ impl<'a> AsyncRead for Connection<'a> { cx: &mut std::task::Context<'_>, buf: &mut [u8], ) -> std::task::Poll> { - if let Some(cursor) = self.cursor.as_mut() { - let n = cursor.read(buf)?; - if n > 0 { - return Poll::Ready(Ok(n)); + let n = match self.cursor.as_mut() { + None => 0, + Some(cursor) => cursor.read(buf)?, + }; + + let n = if n <= 0 { + match Pin::new(&mut self.stream).poll_read(cx, buf)? { + Poll::Pending => return Poll::Pending, + Poll::Ready(n) => n, } - } + } else { + n + }; - match Pin::new(&mut self.stream).poll_read(cx, buf)? { - Poll::Pending => Poll::Pending, - Poll::Ready(n) => { - if let Some(marked) = self.marked.as_mut() { - marked.write_all(&buf[..n])?; - } - - Poll::Ready(Ok(n)) - } + if let Some(marked) = self.marked.as_mut() { + marked.write_all(&buf[..n])?; } + + Poll::Ready(Ok(n)) } } diff --git a/src/core/net/kcp.rs b/src/core/net/kcp.rs index e7aadeb..95c044f 100644 --- a/src/core/net/kcp.rs +++ b/src/core/net/kcp.rs @@ -42,7 +42,7 @@ impl KcpListener { P: KcpProvider, A: Into, { - let udp = UdpSocket::bind::(addr).await?; + let (_, udp) = UdpSocket::bind::(addr).await?; let listener = kcp_rust::KcpListener::new::(udp, conf)?; diff --git a/src/core/net/udp.rs b/src/core/net/udp.rs index 0ab2bff..17a74d2 100644 --- a/src/core/net/udp.rs +++ b/src/core/net/udp.rs @@ -1,18 +1,56 @@ use std::{net::SocketAddr, pin::Pin}; -use crate::{core::{BoxedFuture, Provider}, error}; +use crate::{ + core::{BoxedFuture, Provider}, + error, +}; pub type AbstractDatagram<'a> = Box; pub trait UdpProvider { - type Binder: Provider>>, Arg = SocketAddr>; - type Connect: Provider>>, Arg = SocketAddr>; + type Binder: Provider< + BoxedFuture<'static, error::Result<(SocketAddr, AbstractDatagram<'static>)>>, + Arg = SocketAddr, + >; + type Connect: Provider< + BoxedFuture<'static, error::Result<(SocketAddr, AbstractDatagram<'static>)>>, + Arg = SocketAddr, + >; } pub struct UdpSocket<'a> { inner: AbstractDatagram<'a>, } +#[pin_project::pin_project] +pub struct UdpSend<'a, U> { + #[pin] + udp: &'a mut U, + data: &'a [u8], +} + +#[pin_project::pin_project] +pub struct UdpRecv<'a, U> { + #[pin] + udp: &'a mut U, + buf: &'a mut [u8], +} + +#[pin_project::pin_project] +pub struct UdpSendTo<'a, U> { + #[pin] + udp: &'a mut U, + addr: &'a SocketAddr, + data: &'a [u8], +} + +#[pin_project::pin_project] +pub struct UdpRecvFrom<'a, U> { + #[pin] + udp: &'a mut U, + buf: &'a mut [u8], +} + pub trait AsyncRecvfrom { fn poll_recvfrom( &mut self, @@ -46,11 +84,54 @@ pub trait AsyncSend { ) -> std::task::Poll>; } +pub trait AsyncSendExt: AsyncSend { + fn send<'a>(&'a mut self, data: &'a [u8]) -> UdpSend<'a, Self> + where + Self: Sized, + { + UdpSend { udp: self, data } + } +} + +pub trait AsyncSendToExt: AsyncSendTo { + fn send_to<'a>(&'a mut self, addr: &'a SocketAddr, data: &'a [u8]) -> UdpSendTo<'a, Self> + where + Self: Sized, + { + UdpSendTo { + udp: self, + data, + addr, + } + } +} + +pub trait AsyncRecvExt: AsyncRecv { + fn recv<'a>(&'a mut self, buf: &'a mut [u8]) -> UdpRecv<'a, Self> + where + Self: Sized, + { + UdpRecv { udp: self, buf } + } +} + +pub trait AsyncRecvFromExt: AsyncRecvfrom { + fn recvfrom<'a>(&'a mut self, buf: &'a mut [u8]) -> UdpRecvFrom<'a, Self> + where + Self: Sized, + { + UdpRecvFrom { udp: self, buf } + } +} + pub trait Datagram: AsyncRecvfrom + AsyncSendTo + AsyncRecv + AsyncSend { fn boxed_clone(&self) -> Box; } - +impl AsyncRecvExt for T where T: AsyncRecv + Unpin {} +impl AsyncSendExt for T where T: AsyncSend + Unpin {} +impl AsyncSendToExt for T where T: AsyncSendTo + Unpin {} +impl AsyncRecvFromExt for T where T: AsyncRecvfrom + Unpin {} impl<'a> kcp_rust::AsyncRecvfrom for UdpSocket<'a> { fn poll_recvfrom( @@ -143,23 +224,85 @@ impl<'a> Clone for UdpSocket<'a> { } impl<'a> UdpSocket<'a> { - pub async fn bind(addr: A) -> error::Result + pub async fn bind(addr: A) -> error::Result<(SocketAddr, Self)> where P: UdpProvider, A: Into, { - Ok(UdpSocket { - inner: P::Binder::call(addr.into()).await?, - }) + let (addr, udp) = P::Binder::call(addr.into()).await?; + Ok((addr, UdpSocket { inner: udp })) } - pub async fn connect(addr: A) -> error::Result + pub async fn connect(addr: A) -> error::Result<(SocketAddr, Self)> where P: UdpProvider, A: Into, { - Ok(UdpSocket { - inner: P::Connect::call(addr.into()).await?, - }) + let (addr, udp) = P::Connect::call(addr.into()).await?; + Ok((addr, UdpSocket { inner: udp })) + } +} + +impl<'a, U> std::future::Future for UdpSend<'a, U> +where + U: AsyncSend + Unpin, +{ + type Output = error::Result; + fn poll( + self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + let mut this = self.project(); + Pin::new(&mut **this.udp) + .poll_send(cx, this.data) + .map_err(Into::into) + } +} + +impl<'a, U> std::future::Future for UdpRecv<'a, U> +where + U: AsyncRecv + Unpin, +{ + type Output = error::Result; + fn poll( + self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + let mut this = self.project(); + Pin::new(&mut **this.udp) + .poll_recv(cx, this.buf) + .map_err(Into::into) + } +} + +impl<'a, U> std::future::Future for UdpSendTo<'a, U> +where + U: AsyncSendTo + Unpin, +{ + type Output = error::Result; + fn poll( + self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + let mut this = self.project(); + Pin::new(&mut **this.udp) + .poll_sendto(cx, &this.addr, this.data) + .map_err(Into::into) + } +} + +impl<'a, U> std::future::Future for UdpRecvFrom<'a, U> +where + U: AsyncRecvfrom + Unpin, +{ + type Output = error::Result<(SocketAddr, usize)>; + fn poll( + self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + let mut this = self.project(); + Pin::new(&mut **this.udp) + .poll_recvfrom(cx, this.buf) + .map_err(Into::into) } } diff --git a/src/core/rpc/mod.rs b/src/core/rpc/mod.rs index 947d93d..fe964f8 100644 --- a/src/core/rpc/mod.rs +++ b/src/core/rpc/mod.rs @@ -66,7 +66,7 @@ where } fn borrow_encode(&self) -> error::Result> { - rmp_serde::to_vec(self).map_err(Into::into) + rmp_serde::to_vec_named(self).map_err(Into::into) } } diff --git a/src/core/rpc/structs.rs b/src/core/rpc/structs.rs index 730de9e..8950010 100644 --- a/src/core/rpc/structs.rs +++ b/src/core/rpc/structs.rs @@ -8,7 +8,7 @@ pub mod port_forward { Udp(ServerAddr, u16), Tcp(ServerAddr, u16), } - + #[derive(Debug, Serialize, Deserialize)] pub enum Request { New(u64, Option), diff --git a/src/core/stream/mod.rs b/src/core/stream/mod.rs index 2ef6881..3f0aac1 100644 --- a/src/core/stream/mod.rs +++ b/src/core/stream/mod.rs @@ -5,6 +5,7 @@ use self::{compress::CompressedStream, crypto::EncryptedStream}; use super::io::{AsyncRead, AsyncWrite}; pub mod codec; +pub mod virio; pub mod compress; pub mod crypto; pub mod fallback; diff --git a/src/core/stream/virio.rs b/src/core/stream/virio.rs new file mode 100644 index 0000000..44ef313 --- /dev/null +++ b/src/core/stream/virio.rs @@ -0,0 +1,244 @@ +use std::{ + collections::VecDeque, + io::Read, + pin::Pin, + sync::Arc, + task::{Poll, Waker}, +}; + +use parking_lot::Mutex; + +use crate::{ + core::io::{AsyncRead, AsyncWrite}, + error, +}; + +type VirWaker = Arc>>; + +#[derive(Clone, Debug)] +struct Container { + total: usize, + maxbuf: usize, + buffer: VecDeque>, +} + +#[derive(Clone, Debug)] +pub struct VirBuf { + container: Arc>, + read_waker: VirWaker, + write_waker: VirWaker, +} + +#[derive(Clone, Debug)] +pub struct Vitio { + left: VirBuf, + right: VirBuf, +} + +impl AsyncRead for Vitio { + fn poll_read( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + Pin::new(&mut self.left).poll_read(cx, buf) + } +} + +impl AsyncWrite for Vitio { + fn poll_write( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + Pin::new(&mut self.right).poll_write(cx, buf) + } + + fn poll_flush( + self: std::pin::Pin<&mut Self>, + _: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + Poll::Ready(Ok(())) + } +} + +impl VirBuf { + fn new(read_waker: VirWaker, write_waker: VirWaker) -> Self { + Self { + read_waker, + write_waker, + container: Default::default(), + } + } +} + +impl Default for Container { + fn default() -> Self { + Self { + total: 0, + maxbuf: 5, + buffer: Default::default(), + } + } +} + +impl AsyncRead for VirBuf { + fn poll_read( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + let mut ct = self.container.lock(); + if ct.total == 0 { + self.read_waker.lock().replace(cx.waker().clone()); + Poll::Pending + } else { + let n = ct.fill(buf)?; + ct.total -= n; + drop(ct); + self.try_wake_write(); + Poll::Ready(Ok(n)) + } + } +} + +impl AsyncWrite for VirBuf { + fn poll_write( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + if !self.can_fill() { + self.write_waker.lock().replace(cx.waker().clone()); + Poll::Pending + } else { + { + let mut ct = self.container.lock(); + ct.buffer.push_back(buf.to_vec()); + ct.total += buf.len(); + } + + self.try_wake_read(); + Poll::Ready(Ok(buf.len())) + } + } + + fn poll_flush( + self: std::pin::Pin<&mut Self>, + _: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + Poll::Ready(Ok(())) + } +} + +impl VirBuf { + fn can_fill(&self) -> bool { + let ct = self.container.lock(); + ct.buffer.len() < ct.maxbuf + } + + fn try_wake_write(&mut self) { + if let Some(waker) = self.write_waker.lock().take() { + waker.wake(); + } + } + + fn try_wake_read(&mut self) { + if let Some(waker) = self.read_waker.lock().take() { + waker.wake(); + } + } +} + +impl Container { + fn fill(&mut self, buf: &mut [u8]) -> error::Result { + let mut off = 0; + + while off < buf.len() { + match self.buffer.pop_front() { + None => break, + Some(mut data) => { + let total = data.len(); + + let mut vio = std::io::Cursor::new(&mut data); + + let n = vio.read(&mut buf[off..])?; + + off += n; + + if n < total { + self.buffer.push_front(buf[n..].to_vec()); + } + } + } + } + + Ok(off) + } +} + +pub fn open() -> (Vitio, Vitio) { + let read_waker = VirWaker::default(); + let write_waker = VirWaker::default(); + + let left = VirBuf::new(read_waker, write_waker); + + let read_waker = VirWaker::default(); + let write_waker = VirWaker::default(); + + let right = VirBuf::new(read_waker, write_waker); + + let v1 = Vitio { + left: right.clone(), + right: left.clone(), + }; + + let v2 = Vitio { left, right }; + + (v1, v2) +} + +#[cfg(test)] +mod tests { + use crate::core::io::{AsyncReadExt, AsyncWriteExt}; + + use super::open; + + #[tokio::test] + async fn k() { + let (mut v1, mut v2) = open(); + + tokio::spawn(async move { + loop { + println!("call1 ..."); + + let mut buf = [0u8; 1024]; + + let n = v2.read(&mut buf).await.unwrap(); + + println!("{:?}", &buf[..n]); + + v2.write_all(&mut buf[..n]).await.unwrap(); + } + }); + + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + + println!("call2 ..."); + + v1.write(&[0x1, 0x2]).await.unwrap(); + v1.write(&[0x1, 0x2]).await.unwrap(); + + let mut buf = [0u8; 1024]; + + let n = v1.read(&mut buf).await.unwrap(); + + println!("{:?}", &buf[..n]); + + v1.write(&[0x1, 0x2]).await.unwrap(); + + let n = v1.read(&mut buf).await.unwrap(); + + println!("{:?}", &buf[..n]) + } +} diff --git a/src/core/transfer.rs b/src/core/transfer.rs index c6b1948..f4f1d8a 100644 --- a/src/core/transfer.rs +++ b/src/core/transfer.rs @@ -1,12 +1,15 @@ use std::{ io, pin::Pin, + sync::Arc, task::{Context, Poll}, }; -use crate::error; +use parking_lot::Mutex; -use super::Stream; +use crate::{core::future::Select, error}; + +use super::{BoxedFuture, Stream}; #[pin_project::pin_project] pub struct TransmitSend<'t, T> { @@ -30,6 +33,14 @@ pub struct TransmitRecv<'t, T> { transmitter: &'t mut T, } +pub struct TransmiterWriter { + transmiter: Arc>, +} + +pub struct TransmitterReader { + transmiter: Arc>, +} + pub trait Transmitter: Unpin { fn poll_send( self: Pin<&mut Self>, @@ -75,6 +86,36 @@ pub trait TransmitterExt: Transmitter { transmitter: self, } } + + fn split(self) -> (TransmiterWriter, TransmitterReader) + where + Self: Sized, + { + let this = Arc::new(Mutex::new(self)); + + ( + TransmiterWriter { + transmiter: this.clone(), + }, + TransmitterReader { transmiter: this }, + ) + } + + fn transfer<'a, T>(self, to: T) -> Select<'a, error::Result<()>> + where + T: Transmitter + Send + 'a, + Self: Sized + Send + 'a, + { + let (s1_writer, s1_reader) = to.split(); + let (s2_writer, s2_reader) = self.split(); + + let mut select = Select::new(); + + select.add(crate::core::io::copy(s1_writer, s2_reader)); + select.add(crate::core::io::copy(s2_writer, s1_reader)); + + select + } } impl TransmitterExt for T where T: Transmitter {} @@ -181,3 +222,35 @@ where Pin::new(&mut **this.transmitter).poll_recv(cx, this.buf) } } + +impl crate::core::AsyncRead for TransmitterReader +where + T: Transmitter + Unpin, +{ + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + let mut this = self.transmiter.lock(); + Pin::new(&mut *this).poll_recv(cx, buf) + } +} + +impl crate::core::AsyncWrite for TransmiterWriter +where + T: Transmitter + Unpin, +{ + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + let mut this = self.transmiter.lock(); + Pin::new(&mut *this).poll_send(cx, buf) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} diff --git a/src/error/mod.rs b/src/error/mod.rs index 96552c5..c6915af 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -22,11 +22,13 @@ pub enum FusoError { } #[derive(Debug)] -pub enum MsgPack{ +pub enum MsgPack { Encode(rmp_serde::encode::Error), - Decode(rmp_serde::decode::Error) + Decode(rmp_serde::decode::Error), } +impl std::error::Error for FusoError {} + impl From for FusoError { fn from(value: std::io::Error) -> Self { Self::StdIo(value) @@ -57,6 +59,15 @@ impl From for FusoError { } } +impl From for std::io::Error { + fn from(value: FusoError) -> Self { + match value { + FusoError::StdIo(io) => io, + value => std::io::Error::new(std::io::ErrorKind::Other, value), + } + } +} + impl Display for FusoError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self) diff --git a/src/runtime/tokio/kcp.rs b/src/runtime/tokio/kcp.rs index 0974eda..3bf9254 100644 --- a/src/runtime/tokio/kcp.rs +++ b/src/runtime/tokio/kcp.rs @@ -12,19 +12,18 @@ use crate::{ runtime::Runtime, }; -pub struct KcpWithTokioRuntime; +pub struct UdpWithTokioRuntime; pub struct TokioUdpSocket(Arc); - #[cfg(feature = "fuso-rt-tokio")] impl KcpListener { pub async fn bind(conf: kcp_rust::Config, addr: A) -> error::Result where A: Into, { - let udp = crate::core::net::UdpSocket::bind::(addr).await?; - let listener = kcp_rust::KcpListener::new::(udp, conf)?; + let (_, udp) = crate::core::net::UdpSocket::bind::(addr).await?; + let listener = kcp_rust::KcpListener::new::(udp, conf)?; Ok(Self { inner: Arc::new(listener), stored: LazyFuture::new(), @@ -32,29 +31,32 @@ impl KcpListener { } } - -impl crate::core::net::UdpProvider for KcpWithTokioRuntime { +impl crate::core::net::UdpProvider for UdpWithTokioRuntime { type Binder = Self; type Connect = Self; } -impl crate::core::Provider>>> - for KcpWithTokioRuntime +impl + crate::core::Provider< + BoxedFuture<'static, error::Result<(SocketAddr, AbstractDatagram<'static>)>>, + > for UdpWithTokioRuntime { type Arg = SocketAddr; - fn call(addr: Self::Arg) -> BoxedFuture<'static, error::Result>> { + fn call( + addr: Self::Arg, + ) -> BoxedFuture<'static, error::Result<(SocketAddr, AbstractDatagram<'static>)>> { Box::pin(async move { - let boxed: AbstractDatagram<'_> = Box::new(TokioUdpSocket(Arc::new({ - tokio::net::UdpSocket::bind(addr).await? - }))); - Ok(boxed) + let udp = tokio::net::UdpSocket::bind(addr).await?; + let addr = udp.local_addr()?; + let boxed: AbstractDatagram<'_> = Box::new(TokioUdpSocket(Arc::new(udp))); + Ok((addr, boxed)) }) } } -impl kcp_rust::KcpRuntime for KcpWithTokioRuntime { +impl kcp_rust::KcpRuntime for UdpWithTokioRuntime { type Err = error::FusoError; type Runner = Self; @@ -66,20 +68,20 @@ impl kcp_rust::KcpRuntime for KcpWithTokioRuntime { } } -impl kcp_rust::Runner for KcpWithTokioRuntime { +impl kcp_rust::Runner for UdpWithTokioRuntime { type Err = error::FusoError; fn start(process: kcp_rust::Background) -> std::result::Result<(), Self::Err> { - crate::runtime::tokio::TokioRuntime::spawn(async move{ - if let Err(e) = process.await { - log::warn!("{:?}", e); - } + crate::runtime::tokio::TokioRuntime::spawn(async move { + if let Err(e) = process.await { + log::warn!("{:?}", e); + } }); Ok(()) } } -impl kcp_rust::Timer for KcpWithTokioRuntime { +impl kcp_rust::Timer for UdpWithTokioRuntime { type Ret = (); type Output = BoxedFuture<'static, Self::Ret>; diff --git a/src/server/port_forward/accepter.rs b/src/server/port_forward/accepter.rs index 0849fd9..f43dc9e 100644 --- a/src/server/port_forward/accepter.rs +++ b/src/server/port_forward/accepter.rs @@ -57,7 +57,7 @@ where connection.mark(); let r = R::wait_for( - std::time::Duration::from_secs(10), + std::time::Duration::from_millis(1000), Self::try_handshake(handshaker, &mut connection), ) .await; diff --git a/src/server/port_forward/preprocessor.rs b/src/server/port_forward/preprocessor.rs index 72bb219..12eb503 100644 --- a/src/server/port_forward/preprocessor.rs +++ b/src/server/port_forward/preprocessor.rs @@ -1,15 +1,40 @@ +use std::{marker::PhantomData, task::Poll}; + +use fuso_socks::Socks; + use crate::{ - core::{processor::Preprocessor, rpc::structs::port_forward::VisitorProtocol}, + config::client::{Addr, ServerAddr}, + core::{ + io::{AsyncReadExt, AsyncWriteExt}, + net::{AsyncRecvExt, AsyncRecvFromExt, AsyncSendToExt, UdpProvider}, + processor::Preprocessor, + rpc::structs::port_forward::{Target, VisitorProtocol}, + stream::virio::{self, Vitio}, + transfer::TransmitterExt, + AbstractStream, + }, error, }; use super::Connection; - -pub struct Socks5Preprocessor { - udp_forward: bool, +macro_rules! unpack_socks_addr { + ($addr: expr) => { + match $addr { + fuso_socks::Addr::Socket(ip, port) => (ServerAddr(vec![Addr::WithIpAddr(ip)]), port), + fuso_socks::Addr::Domain(domain, port) => { + (ServerAddr(vec![Addr::WithDomain(domain)]), port) + } + } + }; } +pub struct UdpForwarder {} + +pub struct Socks5Preprocessor

{ + udp_forwarder: UdpForwarder, + _marked: PhantomData

, +} impl Preprocessor for () { type Output = error::Result; @@ -26,10 +51,127 @@ impl Preprocessor for Option<()> { } } +impl futures::AsyncRead for Connection { + fn poll_read( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + crate::core::io::AsyncRead::poll_read(self, cx, buf).map_err(Into::into) + } +} + +impl futures::AsyncWrite for Connection { + fn poll_write( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + crate::core::io::AsyncWrite::poll_write(self, cx, buf).map_err(Into::into) + } + + fn poll_flush( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + crate::core::io::AsyncWrite::poll_flush(self, cx).map_err(Into::into) + } -impl Preprocessor for Socks5Preprocessor { + fn poll_close( + self: std::pin::Pin<&mut Self>, + _: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + Poll::Ready(Ok(())) + } +} + +impl

Socks5Preprocessor

{ + pub fn new() -> Self { + Socks5Preprocessor { + udp_forwarder: UdpForwarder {}, + _marked: PhantomData, + } + } +} + +impl

Preprocessor for Socks5Preprocessor

+where + P: UdpProvider + Sync + 'static, +{ type Output = error::Result; fn prepare<'a>(&'a self, input: Connection) -> crate::core::BoxedFuture<'a, Self::Output> { - unimplemented!() + Box::pin(async move { + let mut input = input; + + input.mark(); + + Ok({ + match fuso_socks::Socks::parse(&mut input, None).await? { + Socks::Invalid => { + input.reset(); + VisitorProtocol::Other(input, None) + } + + Socks::Tcp(addr) => { + input.discard(); + let (addr, port) = unpack_socks_addr!(addr); + VisitorProtocol::Socks(input, Target::Tcp(addr, port)) + } + Socks::Udp(addr) => { + input.discard(); + let (addr, port) = unpack_socks_addr!(addr); + + let conn = self.udp_forwarder.open(input).await?; + + VisitorProtocol::Socks(conn, Target::Udp(addr, port)) + } + } + }) + }) + } +} + + + +impl UdpForwarder { + pub async fn open(&self, conn: Connection) -> error::Result { + let (vio1, vio2) = virio::open(); + + let new_conn = Connection::new(conn.addr().clone(), AbstractStream::new(vio1)); + + Ok(new_conn) + } +} + + +async fn enter_udp_forward( + mut vio: Vitio, + mut input: Connection, +) -> error::Result<()> { + let (listen, udp) = crate::core::net::UdpSocket::bind::(([0, 0, 0, 0], 0)).await?; + + let mut buf = Vec::new(); + + buf.extend(&[0x05, 0x00, 0x00]); + + match listen { + std::net::SocketAddr::V4(addr) => { + buf.push(0x01); + buf.extend(addr.ip().octets()); + buf.extend(addr.port().to_be_bytes()); + } + std::net::SocketAddr::V6(_) => todo!(), } + + input.write_all(&buf).await?; + + let mut udp = udp; + let mut buf = [0u8; 1024]; + let (addr, n) = udp.recvfrom(&mut buf).await.unwrap(); + + vio.send_all(&buf[..n]).await; + + let a = vio.read(&mut buf).await?; + + Ok(()) } From 1773d720394730c7549324ef772b398060668aa9 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Sat, 11 May 2024 00:50:13 +0800 Subject: [PATCH 19/29] =?UTF-8?q?=E5=AE=8C=E5=96=84=E9=83=A8=E5=88=86?= =?UTF-8?q?=E7=AB=AF=E5=8F=A3=E8=BD=AC=E5=8F=91=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 136 ++++++++++++++-- Cargo.toml | 7 +- config/client.toml | 5 +- src/bin/client.rs | 65 ++++++-- src/bin/server.rs | 56 +++++-- src/client/port_forward/mod.rs | 15 +- src/config/client.rs | 33 ++-- src/config/mod.rs | 10 +- src/config/server.rs | 8 +- src/core/future.rs | 8 +- src/core/processor.rs | 53 +++++++ src/core/protocol.rs | 1 + src/core/rpc/structs.rs | 1 + src/core/stream/fragment.rs | 17 ++ src/core/stream/mod.rs | 170 +++++++++++++++++++- src/core/stream/virio.rs | 109 +++++++------ src/core/transfer.rs | 40 +++-- src/error/mod.rs | 1 + src/runtime/tokio/port_forward/mod.rs | 25 ++- src/server/mod.rs | 1 + src/server/port_forward/connection.rs | 202 ++++++++++++++++++++++++ src/server/port_forward/mod.rs | 95 +++++++++-- src/server/port_forward/preprocessor.rs | 126 ++++++--------- src/server/udp_forward/mod.rs | 151 ++++++++++++++++++ 24 files changed, 1095 insertions(+), 240 deletions(-) create mode 100644 src/core/stream/fragment.rs create mode 100644 src/server/port_forward/connection.rs create mode 100644 src/server/udp_forward/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 4070fc5..ca97c25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,7 +82,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.50", ] [[package]] @@ -179,6 +179,26 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +[[package]] +name = "c2rust-bitfields" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b43c3f07ab0ef604fa6f595aa46ec2f8a22172c975e186f6f5bf9829a3b72c41" +dependencies = [ + "c2rust-bitfields-derive", +] + +[[package]] +name = "c2rust-bitfields-derive" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3cbc102e2597c9744c8bd8c15915d554300601c91a079430d309816b0912545" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "cc" version = "1.0.87" @@ -232,7 +252,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.50", ] [[package]] @@ -310,6 +330,7 @@ dependencies = [ "clap", "env_logger", "fuso-socks", + "futures", "kcp-rust", "log", "parking_lot", @@ -320,12 +341,12 @@ dependencies = [ "serde", "tokio", "toml", + "tun", ] [[package]] name = "fuso-socks" version = "0.1.0" -source = "git+https://github.com/editso/fuso-socks5#556cfd0308cd177da2022caa598378dca59e9248" dependencies = [ "async-trait", "bytes", @@ -389,7 +410,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.50", ] [[package]] @@ -593,6 +614,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "ioctl-sys" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bd11f3a29434026f5ff98c730b668ba74b1033637b8817940b54d040696133c" + [[package]] name = "itoa" version = "1.0.10" @@ -615,6 +642,16 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets 0.52.3", +] + [[package]] name = "lock_api" version = "0.4.11" @@ -728,9 +765,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "percent-encoding" @@ -755,7 +792,7 @@ checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.50", ] [[package]] @@ -934,7 +971,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.50", ] [[package]] @@ -1019,6 +1056,17 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +[[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.50" @@ -1036,6 +1084,26 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "thiserror" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + [[package]] name = "tokio" version = "1.36.0" @@ -1063,7 +1131,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.50", ] [[package]] @@ -1162,6 +1230,24 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tun" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0adb9992bbd5ca76f3847ed579ad4ee8defb2ec2eea918cceef17ccc66fa4fd4" +dependencies = [ + "byteorder", + "bytes", + "futures-core", + "ioctl-sys", + "libc", + "log", + "thiserror", + "tokio", + "tokio-util", + "wintun", +] + [[package]] name = "typenum" version = "1.17.0" @@ -1192,6 +1278,25 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "windows" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" +dependencies = [ + "windows-core", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -1332,3 +1437,16 @@ checksum = "7a4191c47f15cc3ec71fcb4913cb83d58def65dd3787610213c649283b5ce178" dependencies = [ "memchr", ] + +[[package]] +name = "wintun" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29b83b0eca06dd125dbcd48a45327c708a6da8aada3d95a3f06db0ce4b17e0d4" +dependencies = [ + "c2rust-bitfields", + "libloading", + "log", + "thiserror", + "windows", +] diff --git a/Cargo.toml b/Cargo.toml index cafb5bf..3bb0228 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ default = ["fuso-config", "fuso-cli", "fuso-kcp", "fuso-rt-tokio", "fuso-manager fuso-kcp = ["kcp-rust"] fuso-cli = ["clap"] fuso-serde = ["serde"] -fuso-socks5 = [ "dep:fuso-socks" ] +fuso-socks5 = [ "dep:fuso-socks", "dep:futures" ] fuso-config = ["toml", "serde"] fuso-runtime = [] fuso-rt-tokio = ["dep:tokio", "fuso-runtime"] @@ -60,6 +60,8 @@ pin-project = "1.1.4" parking_lot = "0.12.1" rmp-serde = "1.3.0" rand = "0.8.5" +tun = { version = "0.6.1", features = ["async"] } +futures = { version = "0.3.30", optional = true} [dependencies.kcp-rust] optional = true @@ -69,8 +71,7 @@ default-features = false [dependencies.fuso-socks] optional = true -version = "*" -git = "https://github.com/editso/fuso-socks5" +path = "../fuso-socks5" [dependencies.axum] optional = true diff --git a/config/client.toml b/config/client.toml index cb1ac11..058bd4a 100644 --- a/config/client.toml +++ b/config/client.toml @@ -13,14 +13,15 @@ auth = { type = "secret", secret = "12345678" } type = "forward" boot = "fork" exposes = [ "8080/tcp" ] -# channel = [ "8002/tcp" ] +channel = [ "8002/tcp", "9090/kcp"] restart = "always" cryptos = ["aes"] compress = ["lz4"] target = { type = "tcp", port = 9999, addr = ["127.0.0.1"] } keep_alive = { interval = 100 } -[ssh1.prepares.socks5] +[ssh1.socks5] +udp = { bind = "0.0.0.0", port = 9999 } auth = { type = "secret", secret = "1234" } diff --git a/src/bin/client.rs b/src/bin/client.rs index 866b7de..36416a8 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -1,4 +1,4 @@ -use std::{net::SocketAddr, time::Duration}; +use std::{net::SocketAddr, sync::Arc, time::Duration}; use fuso::{ cli, @@ -16,9 +16,10 @@ use fuso::{ io::{AsyncReadExt, AsyncWriteExt}, net::{TcpListener, TcpStream}, protocol::{AsyncPacketRead, AsyncPacketSend}, - rpc::{AsyncCall, Caller, Encoder}, + rpc::{structs::port_forward::Target, AsyncCall, Caller, Decoder, Encoder}, stream::{ compress::CompressedStream, + fragment::Fragment, handshake::{Handshake, MuxConfig}, UseCompress, UseCrypto, }, @@ -29,6 +30,7 @@ use fuso::{ runner::{FnRunnable, NamedRunnable, Rise, ServiceRunner}, runtime::{tokio::TokioRuntime, Runtime}, }; +use kcp_rust::KcpConnector; fn main() { env_logger::Builder::new() @@ -151,7 +153,7 @@ async fn enter_forward_service_main( if let Some(channels) = service.channel.as_ref() { for expose in channels.iter() { match expose { - Expose::Kcp(_, _) => todo!(), + Expose::Kcp(_, port) => {} Expose::Tcp(_, port) => { let port = *port; let cryptos = service.crypto.clone(); @@ -209,24 +211,55 @@ async fn enter_forward_service_main( log::debug!("forward started ."); loop { - let (mut linker, target) = forwarder.accept().await?; + let (linker, target) = forwarder.accept().await?; tokio::spawn(async move { log::debug!("target {:?}", target); match target { - FinalTarget::Udp { addr, port } => { - let mut a = linker.link(Protocol::Tcp).await.unwrap(); - - let mut buf = [0u8; 1024]; - - let n = a.recv(&mut buf).await.unwrap(); - - - log::debug!("{:?}", &buf[..n]); - - } + FinalTarget::Udp { addr, port } => {} FinalTarget::Shell { path, args } => todo!(), - FinalTarget::Dynamic => todo!(), + FinalTarget::Dynamic => { + let transmitter = linker.link(Protocol::Tcp).await.unwrap(); + + let udp = Arc::new(tokio::net::UdpSocket::bind("0.0.0.0:0").await.unwrap()); + + let (writer, mut reader) = transmitter.split(); + + loop { + let pkt = match reader.recv_packet().await { + Err(_) => break, + Ok(data) => data, + }; + + let pkt: Fragment = pkt.decode().unwrap(); + + let udp = udp.clone(); + let mut writer = writer.clone(); + + match pkt { + Fragment::UdpForward { + saddr, + daddr, + dport, + data, + } => { + let target = format!("{}:{dport}", daddr.to_string()); + log::debug!("udp forward {saddr} -> {target}"); + udp.send_to(&data, target).await.unwrap(); + let mut buf = [0u8; 1400]; + let (n, addr) = udp.recv_from(&mut buf).await.unwrap(); + + writer + .send_packet( + &Fragment::Udp(saddr, buf[..n].to_vec()).encode().unwrap(), + ) + .await + .unwrap(); + } + _ => {} + } + } + } FinalTarget::Tcp { addr, port } => { let result = addr .try_connect(port, |host, port| async move { diff --git a/src/bin/server.rs b/src/bin/server.rs index 9ac75db..c78c37f 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -8,11 +8,11 @@ use fuso::{ config::{client::WithForwardService, server::Config, Expose, Stateful}, core::{ accepter::{AccepterExt, MultiAccepter, StreamAccepter, TaggedAccepter}, - future::Select, + future::{Poller, Select}, handshake::Handshaker, io::{AsyncReadExt, AsyncWriteExt}, - net::{KcpListener, TcpListener}, - processor::{IProcessor, Processor, StreamProcessor}, + net::{KcpListener, TcpListener, UdpSendTo, UdpSocket}, + processor::{IProcessor, PreprocessorSelector, Processor, StreamProcessor}, protocol::{AsyncPacketRead, AsyncPacketSend}, rpc::Decoder, split::SplitStream, @@ -25,8 +25,11 @@ use fuso::{ AbstractStream, Stream, }, error, - runtime::tokio::{UdpWithTokioRuntime, TokioRuntime, TokioUdpSocket}, - server::port_forward::{ForwardAccepter, MuxAccepter, PortForwarder, Socks5Preprocessor}, + runtime::tokio::{TokioRuntime, TokioUdpSocket, UdpWithTokioRuntime}, + server::{ + port_forward::{ForwardAccepter, MuxAccepter, PortForwarder, Socks5Preprocessor}, + udp_forward::UdpForwarderImpl, + }, }; use kcp_rust::KcpConfig; @@ -50,6 +53,7 @@ async fn enter_fuso_main(conf: Config) -> error::Result<()> { // conf.listens.len() env_logger::builder() .filter_level(log::LevelFilter::Debug) + .filter_module("fuso_socks", log::LevelFilter::Error) .init(); enter_fuso_serve(Stateful::new(conf)).await?; @@ -259,19 +263,41 @@ where }); } - let previs = Socks5Preprocessor::::new(); + let mut poller = Poller::new(); + let mut previs = PreprocessorSelector::new(); + + if let Some(ref socks5) = conf.prepares.socks5 { + previs.add({ + if let Some(ref udp) = socks5.udp { + let (addr, udp) = + UdpSocket::bind::((udp.bind, udp.port)).await?; + let (task, forwarder) = UdpForwarderImpl::new(addr, udp); + poller.add(task); + Socks5Preprocessor::new(forwarder) + } else { + Socks5Preprocessor::new(()) + } + }) + } let mut forwarder = PortForwarder::new(transport, accepter, previs, None); log::debug!("port forwarder started ."); - loop { - let (c1, c2) = forwarder.accept().await?; - log::debug!("start forward ................"); - tokio::spawn(async move { - if let Err(e) = c1.transfer(c2).await { - log::warn!("{e}") - }; - }); - } + poller.add(async move { + loop { + let (c1, c2) = forwarder.accept().await?; + + log::debug!("start forward {} -> {}", c1.addr(), c2.addr()); + + tokio::spawn(async move { + + let _ = c1.transfer(c2).await; + + + }); + } + }); + + poller.await } diff --git a/src/client/port_forward/mod.rs b/src/client/port_forward/mod.rs index 2f5c136..44d4fd9 100644 --- a/src/client/port_forward/mod.rs +++ b/src/client/port_forward/mod.rs @@ -8,12 +8,9 @@ pub use linker::*; use crate::core::connector::ReplicableConnector; use crate::core::rpc::structs::port_forward::Target; use crate::core::transfer::AbstractTransmitter; -use crate::{ - core::{ - connector::AbstractConnector, - rpc::{structs::port_forward::Request, AsyncCallee}, - }, - error, +use crate::core::{ + connector::AbstractConnector, + rpc::{structs::port_forward::Request, AsyncCallee}, }; use std::sync::Arc; use std::{marker::PhantomData, pin::Pin, task::Poll}; @@ -21,7 +18,7 @@ use std::{marker::PhantomData, pin::Pin, task::Poll}; use crate::{ client::port_forward::transport::Transport, config::client::FinalTarget, - core::{accepter::Accepter, connector::Connector, Connection, Stream}, + core::{accepter::Accepter, connector::Connector, Stream}, runtime::Runtime, }; @@ -70,6 +67,10 @@ where match Pin::new(&mut self.transport).poll_next(ctx)? { Poll::Pending => Poll::Pending, Poll::Ready((request, responder)) => match request { + Request::Dyn(token, _) => { + let linker = Linker::new(token, self.connector.clone(), responder); + Poll::Ready(Ok((linker, FinalTarget::Dynamic))) + } Request::New(token, target) => Poll::Ready(Ok({ let linker = Linker::new(token, self.connector.clone(), responder); (linker, { diff --git a/src/config/client.rs b/src/config/client.rs index abd907d..7c7d839 100644 --- a/src/config/client.rs +++ b/src/config/client.rs @@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize}; use super::{ default_auth_timeout, Authentication, BootKind, Compress, Crypto, Expose, KeepAlive, - RestartPolicy, + ListenMetadata, RestartPolicy, }; #[derive(Debug, Serialize, Deserialize)] @@ -97,22 +97,28 @@ pub struct WithForwardService { #[serde(default = "Default::default")] pub compress: HashSet, /// 访问端建立连接后的前置处理方式 + #[serde(flatten)] #[serde(default = "Default::default")] pub prepares: PrepareForward, } #[derive(Clone, Debug, Default, Serialize, Deserialize, Hash, PartialEq, Eq)] pub struct PrepareForward { - socks5: Option, + pub socks5: Option, } #[derive(Clone, Debug, Serialize, Deserialize, Hash, PartialEq, Eq)] pub struct WithSocks5Prepare { + #[serde(default = "Default::default")] + pub udp: Option, /// 认证方式 - auth: Authentication, - /// 是否启动udp转发 - #[serde(default = "default_socks5_udp_forward")] - udp_forward: bool, + pub auth: Authentication, +} + +#[derive(Clone, Debug, Serialize, Deserialize, Hash, PartialEq, Eq)] +pub struct UdpForwardConfig { + pub bind: IpAddr, + pub port: u16, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -318,6 +324,15 @@ impl From for String { } } +impl ToString for Addr { + fn to_string(&self) -> String { + match self { + Addr::WithIpAddr(ip) => ip.to_string(), + Addr::WithDomain(domain) => domain.to_string(), + } + } +} + /// 默认随机分配一个端口 fn default_exposes() -> HashSet { let mut exposes = HashSet::new(); @@ -327,10 +342,6 @@ fn default_exposes() -> HashSet { exposes } -fn default_socks5_udp_forward() -> bool { - true -} - #[cfg(test)] // #[cfg(feature = "fuso-toml")] mod tests { @@ -357,7 +368,7 @@ mod tests { } #[test] - fn test_serialize(){ + fn test_serialize() { let a = super::Config::default(); let a: super::Config = a.encode().unwrap().decode().unwrap(); } diff --git a/src/config/mod.rs b/src/config/mod.rs index 226a8aa..2155024 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,6 +1,6 @@ use std::{ fmt::Display, - net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}, + net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}, ops::Deref, sync::Arc, }; @@ -48,6 +48,14 @@ pub enum Crypto { Rsa, } +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] + +pub struct ListenMetadata { + pub bind: IpAddr, + pub port: u16, +} + + #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] #[serde(rename_all = "lowercase")] pub enum Compress { diff --git a/src/config/server.rs b/src/config/server.rs index 9539d8d..b61bbd1 100644 --- a/src/config/server.rs +++ b/src/config/server.rs @@ -6,7 +6,7 @@ use std::{ use serde::{Deserialize, Serialize}; -use super::{default_auth_timeout, Authentication, Compress, Crypto}; +use super::{default_auth_timeout, Authentication, Compress, Crypto, ListenMetadata}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Config { @@ -55,12 +55,6 @@ pub enum Listen { Tunnel(ListenMetadata), } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] - -pub struct ListenMetadata { - pub bind: IpAddr, - pub port: u16, -} #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct KcpListenMetadata { diff --git a/src/core/future.rs b/src/core/future.rs index 202a17b..831eb11 100644 --- a/src/core/future.rs +++ b/src/core/future.rs @@ -55,6 +55,10 @@ impl<'a, O> LazyFuture<'a, O> { pub fn new() -> Self { Self(Default::default()) } + + pub fn reset(&mut self) { + drop(self.0.take()); + } } impl<'a, O> Poller<'a, O> { @@ -69,7 +73,7 @@ impl<'a, O> Poller<'a, O> { self.0.push(Box::pin(fut)) } - pub fn has_more(&self) -> bool{ + pub fn has_more(&self) -> bool { self.0.len() > 0 } } @@ -107,7 +111,7 @@ impl std::future::Future for Poller<'_, O> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { for (idx, fut) in self.0.iter_mut().enumerate() { if let Poll::Ready(o) = Pin::new(fut).poll(cx) { - self.0.remove(idx); + drop(self.0.remove(idx)); return Poll::Ready(o); } } diff --git a/src/core/processor.rs b/src/core/processor.rs index ec36806..087df74 100644 --- a/src/core/processor.rs +++ b/src/core/processor.rs @@ -1,5 +1,7 @@ use std::sync::Arc; +use crate::error; + use super::{ io::{AsyncRead, AsyncWrite}, BoxedFuture, @@ -11,6 +13,16 @@ pub enum Process { Resolve(R), } +pub enum Selector { + Next(In), + Accepted(Out), +} + +pub enum Prepare { + Ok(Out), + Bad(In), +} + pub trait Preprocessor { type Output; @@ -43,6 +55,10 @@ pub struct AbstractPreprocessor<'a, In, Out>( pub(crate) Arc + Sync + Send + 'a>, ); +pub struct PreprocessorSelector<'a, In, Out> { + pres: Vec>>>, +} + impl IProcessor for AbstractProcessor<'_, A, R> where A: Send, @@ -99,3 +115,40 @@ impl Clone for AbstractPreprocessor<'_, In, Out> { Self(self.0.clone()) } } + +impl<'p, In, Out> PreprocessorSelector<'p, In, Out> { + pub fn new() -> Self { + Self { + pres: Default::default(), + } + } + + pub fn add

(&mut self, p: P) + where + P: Preprocessor>> + Sync + Send + 'p, + { + self.pres.push(AbstractPreprocessor(Arc::new(p))) + } +} + +impl<'p, In, Out> Preprocessor for PreprocessorSelector<'p, In, Out> +where + In: Send, +{ + type Output = error::Result>; + + fn prepare<'a>(&'a self, input: In) -> BoxedFuture<'a, Self::Output> { + Box::pin(async move { + let mut input = input; + + for prep in self.pres.iter() { + input = match prep.prepare(input).await? { + Selector::Next(input) => input, + Selector::Accepted(out) => return Ok(Prepare::Ok(out)), + } + } + + Ok(Prepare::Bad(input)) + }) + } +} diff --git a/src/core/protocol.rs b/src/core/protocol.rs index 6186512..8d45b50 100644 --- a/src/core/protocol.rs +++ b/src/core/protocol.rs @@ -106,6 +106,7 @@ impl Buffer { match Pin::new(&mut *reader).poll_read(cx, &mut self.data[off..])? { Poll::Pending => break, Poll::Ready(0) => { + log::debug!("error ................"); return Poll::Ready(Err(io::Error::from(io::ErrorKind::UnexpectedEof).into())) } Poll::Ready(n) => { diff --git a/src/core/rpc/structs.rs b/src/core/rpc/structs.rs index 8950010..d065d50 100644 --- a/src/core/rpc/structs.rs +++ b/src/core/rpc/structs.rs @@ -12,6 +12,7 @@ pub mod port_forward { #[derive(Debug, Serialize, Deserialize)] pub enum Request { New(u64, Option), + Dyn(u64, Option) } #[derive(Debug, Serialize, Deserialize)] diff --git a/src/core/stream/fragment.rs b/src/core/stream/fragment.rs new file mode 100644 index 0000000..b1bb51b --- /dev/null +++ b/src/core/stream/fragment.rs @@ -0,0 +1,17 @@ +use std::net::SocketAddr; + +use serde::{Deserialize, Serialize}; + +use crate::config::client::Addr; + +#[derive(Serialize, Deserialize)] +pub enum Fragment { + Ping, + Udp(SocketAddr, Vec), + UdpForward { + saddr: SocketAddr, + daddr: Addr, + dport: u16, + data: Vec, + }, +} diff --git a/src/core/stream/mod.rs b/src/core/stream/mod.rs index 3f0aac1..6a54a3c 100644 --- a/src/core/stream/mod.rs +++ b/src/core/stream/mod.rs @@ -1,15 +1,27 @@ -use crate::config::{Compress, Crypto}; - -use self::{compress::CompressedStream, crypto::EncryptedStream}; - -use super::io::{AsyncRead, AsyncWrite}; - pub mod codec; -pub mod virio; pub mod compress; pub mod crypto; pub mod fallback; +pub mod fragment; pub mod handshake; +pub mod virio; + +use std::{pin::Pin, sync::Arc, task::Poll}; + +use parking_lot::Mutex; + +use crate::{ + config::{Compress, Crypto}, + error, +}; + +use self::{compress::CompressedStream, crypto::EncryptedStream}; + +use super::{ + future::LazyFuture, + io::{AsyncRead, AsyncWrite}, + AbstractStream, +}; pub trait UseCrypto<'a>: AsyncRead + AsyncWrite + Send + Unpin { fn use_crypto<'crypto, C>(self, cryptos: C) -> EncryptedStream<'a> @@ -33,3 +45,147 @@ pub trait UseCompress<'a>: AsyncRead + AsyncWrite + Send + Unpin { impl<'a, T> UseCrypto<'a> for T where T: AsyncRead + AsyncWrite + Send + Unpin {} impl<'a, T> UseCompress<'a> for T where T: AsyncRead + AsyncWrite + Send + Unpin {} + +#[derive(Clone)] +pub struct CloneableStream(Arc>>); + +pub struct KeepAliveStream { + stream: CloneableStream, + poller: LazyFuture<'static, error::Result<()>>, + last_check: std::time::Instant, + last_write: std::time::Instant, +} + +pub struct Socks5UdpStream<'a> { + conn: AbstractStream<'a>, + stream: AbstractStream<'a>, +} + +impl CloneableStream { + pub fn new(s: S) -> Self + where + S: Into>, + { + Self(Arc::new(Mutex::new(s.into()))) + } +} + +impl<'a> Socks5UdpStream<'a> { + pub fn new(dep: AbstractStream<'a>, stream: AbstractStream<'a>) -> Self { + Self { conn: dep, stream } + } +} + +impl AsyncRead for CloneableStream { + fn poll_read( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + let mut this = self.0.lock(); + Pin::new(&mut *this).poll_read(cx, buf) + } +} + +impl AsyncWrite for CloneableStream { + fn poll_write( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + let mut this = self.0.lock(); + Pin::new(&mut *this).poll_write(cx, buf) + } + + fn poll_flush( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let mut this = self.0.lock(); + Pin::new(&mut *this).poll_flush(cx) + } +} + +impl KeepAliveStream { + pub fn new(s: CloneableStream) -> Self { + Self { + stream: s, + poller: LazyFuture::new(), + last_check: std::time::Instant::now(), + last_write: std::time::Instant::now(), + } + } + + /// 返回下一次更新时间 + pub fn next_update(&self) -> usize { + unimplemented!() + } + + pub fn duplicate(&self) -> AbstractStream<'static> { + AbstractStream::new(self.stream.clone()) + } + + pub fn poll_check( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let stream = self.stream.clone(); + + let last_write = self.last_write; + let last_check = self.last_check; + + self.poller.poll(cx, move || { + let mut stream = stream.clone(); + async move { + let now = std::time::Instant::now(); + + // if now.duration_since(last_check).as_secs() > 100000 { + // log::debug!("timeout ..."); + // return Err(error::FusoError::Timeout); + // } + + // if now.duration_since(last_write).as_secs() > 10 { + // stream.send_packet(&Fragment::Ping.encode()?).await?; + // } + + Ok(()) + } + }) + } +} + +impl<'a> AsyncRead for Socks5UdpStream<'a> { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + if let std::task::Poll::Ready(0) = Pin::new(&mut self.conn).poll_read(cx, buf)? { + Poll::Ready(Err(error::FusoError::UdpForwardTerm)) + } else { + Pin::new(&mut self.stream).poll_read(cx, buf) + } + } +} + +impl<'a> AsyncWrite for Socks5UdpStream<'a> { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + let mut sb = [0u8; 1]; + if let std::task::Poll::Ready(0) = Pin::new(&mut self.conn).poll_read(cx, &mut sb)? { + Poll::Ready(Err(error::FusoError::UdpForwardTerm)) + } else { + Pin::new(&mut self.stream).poll_write(cx, buf) + } + } + + fn poll_flush( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + Pin::new(&mut self.stream).poll_flush(cx) + } +} diff --git a/src/core/stream/virio.rs b/src/core/stream/virio.rs index 44ef313..4356547 100644 --- a/src/core/stream/virio.rs +++ b/src/core/stream/virio.rs @@ -1,6 +1,5 @@ use std::{ collections::VecDeque, - io::Read, pin::Pin, sync::Arc, task::{Poll, Waker}, @@ -88,17 +87,18 @@ impl AsyncRead for VirBuf { cx: &mut std::task::Context<'_>, buf: &mut [u8], ) -> std::task::Poll> { - let mut ct = self.container.lock(); - if ct.total == 0 { + let n = { self.container.lock().read(buf)? }; + + let r = if n == 0 { self.read_waker.lock().replace(cx.waker().clone()); Poll::Pending } else { - let n = ct.fill(buf)?; - ct.total -= n; - drop(ct); - self.try_wake_write(); Poll::Ready(Ok(n)) - } + }; + + self.try_wake_write(); + + r } } @@ -108,18 +108,12 @@ impl AsyncWrite for VirBuf { cx: &mut std::task::Context<'_>, buf: &[u8], ) -> std::task::Poll> { - if !self.can_fill() { - self.write_waker.lock().replace(cx.waker().clone()); - Poll::Pending - } else { - { - let mut ct = self.container.lock(); - ct.buffer.push_back(buf.to_vec()); - ct.total += buf.len(); - } - + if self.container.lock().write(buf) { self.try_wake_read(); Poll::Ready(Ok(buf.len())) + } else { + self.write_waker.lock().replace(cx.waker().clone()); + Poll::Pending } } @@ -132,11 +126,6 @@ impl AsyncWrite for VirBuf { } impl VirBuf { - fn can_fill(&self) -> bool { - let ct = self.container.lock(); - ct.buffer.len() < ct.maxbuf - } - fn try_wake_write(&mut self) { if let Some(waker) = self.write_waker.lock().take() { waker.wake(); @@ -151,28 +140,41 @@ impl VirBuf { } impl Container { - fn fill(&mut self, buf: &mut [u8]) -> error::Result { + fn write(&mut self, buf: &[u8]) -> bool { + if self.buffer.len() >= self.maxbuf { + false + } else { + self.buffer.push_back(buf.to_vec()); + self.total += buf.len(); + true + } + } + + fn read(&mut self, buf: &mut [u8]) -> error::Result { let mut off = 0; while off < buf.len() { match self.buffer.pop_front() { None => break, - Some(mut data) => { + Some(data) => { + let rem = buf.len() - off; let total = data.len(); - let mut vio = std::io::Cursor::new(&mut data); + let fill = if total > rem { rem } else { total }; - let n = vio.read(&mut buf[off..])?; + buf[off..off + fill].copy_from_slice(&data[..fill]); - off += n; + off += fill; - if n < total { - self.buffer.push_front(buf[n..].to_vec()); + if fill < total { + self.buffer.push_front(data[fill..].to_vec()); } } } } + self.total -= off; + Ok(off) } } @@ -200,44 +202,47 @@ pub fn open() -> (Vitio, Vitio) { #[cfg(test)] mod tests { - use crate::core::io::{AsyncReadExt, AsyncWriteExt}; + use crate::{ + core::{ + io::{AsyncReadExt, AsyncWriteExt}, + protocol::{AsyncPacketRead, AsyncPacketSend}, + rpc::{Decoder, Encoder}, + stream::fragment::Fragment, + }, + error, + }; use super::open; #[tokio::test] async fn k() { - let (mut v1, mut v2) = open(); + env_logger::builder() + .filter_level(log::LevelFilter::Debug) + .init(); - tokio::spawn(async move { - loop { - println!("call1 ..."); - - let mut buf = [0u8; 1024]; - - let n = v2.read(&mut buf).await.unwrap(); + let (mut v1, mut v2) = open(); - println!("{:?}", &buf[..n]); + // tokio::time::sleep(std::time::Duration::from_secs(1)).await; - v2.write_all(&mut buf[..n]).await.unwrap(); - } - }); + println!("call2 ..."); - tokio::time::sleep(std::time::Duration::from_secs(1)).await; + v1.send_packet(&Fragment::Ping.encode().unwrap()) + .await + .unwrap(); - println!("call2 ..."); + // let n = v2.recv_packet().await.unwrap(); - v1.write(&[0x1, 0x2]).await.unwrap(); - v1.write(&[0x1, 0x2]).await.unwrap(); + // let f: error::Result = f.to_vec().decode(); - let mut buf = [0u8; 1024]; + // f.unwrap(); - let n = v1.read(&mut buf).await.unwrap(); + let mut buf = [0u8; 1]; - println!("{:?}", &buf[..n]); + let da = v2.recv_packet().await.unwrap(); - v1.write(&[0x1, 0x2]).await.unwrap(); + println!("{da:?}"); - let n = v1.read(&mut buf).await.unwrap(); + let n = v2.read(&mut buf).await.unwrap(); println!("{:?}", &buf[..n]) } diff --git a/src/core/transfer.rs b/src/core/transfer.rs index f4f1d8a..291240f 100644 --- a/src/core/transfer.rs +++ b/src/core/transfer.rs @@ -9,7 +9,7 @@ use parking_lot::Mutex; use crate::{core::future::Select, error}; -use super::{BoxedFuture, Stream}; +use super::Stream; #[pin_project::pin_project] pub struct TransmitSend<'t, T> { @@ -33,12 +33,12 @@ pub struct TransmitRecv<'t, T> { transmitter: &'t mut T, } -pub struct TransmiterWriter { - transmiter: Arc>, +pub struct TransmitWriter { + transmitter: Arc>, } pub struct TransmitterReader { - transmiter: Arc>, + transmitter: Arc>, } pub trait Transmitter: Unpin { @@ -87,17 +87,17 @@ pub trait TransmitterExt: Transmitter { } } - fn split(self) -> (TransmiterWriter, TransmitterReader) + fn split(self) -> (TransmitWriter, TransmitterReader) where Self: Sized, { let this = Arc::new(Mutex::new(self)); ( - TransmiterWriter { - transmiter: this.clone(), + TransmitWriter { + transmitter: this.clone(), }, - TransmitterReader { transmiter: this }, + TransmitterReader { transmitter: this }, ) } @@ -108,7 +108,7 @@ pub trait TransmitterExt: Transmitter { { let (s1_writer, s1_reader) = to.split(); let (s2_writer, s2_reader) = self.split(); - + let mut select = Select::new(); select.add(crate::core::io::copy(s1_writer, s2_reader)); @@ -232,12 +232,12 @@ where cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - let mut this = self.transmiter.lock(); + let mut this = self.transmitter.lock(); Pin::new(&mut *this).poll_recv(cx, buf) } } -impl crate::core::AsyncWrite for TransmiterWriter +impl crate::core::AsyncWrite for TransmitWriter where T: Transmitter + Unpin, { @@ -246,7 +246,7 @@ where cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - let mut this = self.transmiter.lock(); + let mut this = self.transmitter.lock(); Pin::new(&mut *this).poll_send(cx, buf) } @@ -254,3 +254,19 @@ where Poll::Ready(Ok(())) } } + +impl Clone for TransmitterReader { + fn clone(&self) -> Self { + Self { + transmitter: self.transmitter.clone(), + } + } +} + +impl Clone for TransmitWriter { + fn clone(&self) -> Self { + Self { + transmitter: self.transmitter.clone(), + } + } +} diff --git a/src/error/mod.rs b/src/error/mod.rs index c6915af..baac5d0 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -9,6 +9,7 @@ pub enum FusoError { Abort, AuthError, Cancel, + UdpForwardTerm, InvalidConnection, InvaledSetter, BadRpcCall(u64), diff --git a/src/runtime/tokio/port_forward/mod.rs b/src/runtime/tokio/port_forward/mod.rs index 14e351d..7a929fa 100644 --- a/src/runtime/tokio/port_forward/mod.rs +++ b/src/runtime/tokio/port_forward/mod.rs @@ -1,9 +1,19 @@ use std::net::SocketAddr; use crate::{ - client::port_forward::Protocol, config::client::FinalTarget, core::{ - accepter::Accepter, connector::Connector, processor::Preprocessor, rpc::structs::port_forward::VisitorProtocol, stream::handshake::MuxConfig, transfer::AbstractTransmitter, AbstractStream, Connection, Stream - }, error, server::port_forward::{MuxAccepter, Whence} + client::port_forward::Protocol, + config::client::FinalTarget, + core::{ + accepter::Accepter, + connector::Connector, + processor::{AbstractPreprocessor, Preprocessor}, + rpc::structs::port_forward::VisitorProtocol, + stream::handshake::MuxConfig, + transfer::AbstractTransmitter, + AbstractStream, Connection, Stream, + }, + error, + server::port_forward::{MuxAccepter, Whence}, }; use super::TokioRuntime; @@ -24,8 +34,7 @@ where { pub fn new(transport: T, accepter: A, prepvis: P, prepmap: M) -> Self where - P: Preprocessor, Output = error::Result>, - P: Send + Sync + 'static, + P: Into, error::Result>>, M: Preprocessor, Output = error::Result>>, M: Send + Sync + 'static, { @@ -39,7 +48,11 @@ where { pub fn new(transport: S, target: FinalTarget, connector: C) -> Self where - C: Connector> + Sync + Send + Unpin + 'static, + C: Connector> + + Sync + + Send + + Unpin + + 'static, { Self::new_with_runtime(transport, target, connector) } diff --git a/src/server/mod.rs b/src/server/mod.rs index 8745499..ddd9e23 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -6,6 +6,7 @@ mod handshake; +pub mod udp_forward; pub mod port_forward; pub mod proxy_tunnel; diff --git a/src/server/port_forward/connection.rs b/src/server/port_forward/connection.rs new file mode 100644 index 0000000..5b1d294 --- /dev/null +++ b/src/server/port_forward/connection.rs @@ -0,0 +1,202 @@ +use std::{marker::PhantomData, net::SocketAddr, pin::Pin, sync::Arc, task::Poll}; + +use parking_lot::Mutex; + +use crate::{ + core::{ + future::LazyFuture, + io::{AsyncRead, AsyncWrite}, + stream::{CloneableStream, KeepAliveStream}, + AbstractStream, + }, + error, + runtime::Runtime, +}; + +use super::Connection; + +#[derive(Clone)] +struct Hot(Arc>); + +struct HotStream<'a> { + nac: Hot, + conn: AbstractStream<'a>, +} + +struct UConnection { + /// 活跃连接数 + nac: Hot, + addr: SocketAddr, + conn: KeepAliveStream, +} + +pub struct UdpConnections { + conns: Arc>>, + /// 单条连接最大会话数 + max_nac: usize, + next_update: Arc>>, + _marked: PhantomData, +} + +impl Default for UdpConnections { + fn default() -> Self { + Self { + conns: Default::default(), + max_nac: 50, + next_update: Arc::new(Mutex::new(LazyFuture::new())), + _marked: PhantomData, + } + } +} + +impl UdpConnections { + pub async fn exchange(&self, conn: Connection) -> error::Result { + log::debug!("new udp forward channel {}", conn.addr()); + + let mut uc = UConnection { + nac: Hot(Arc::new(Mutex::new(0))), + addr: conn.addr().clone(), + conn: KeepAliveStream::new(CloneableStream::new(AbstractStream::new(conn))), + }; + + let new_conn = uc.apply()?; + + self.conns.lock().push(uc); + + Ok(new_conn) + } + + pub async fn apply_idle_conn(&self) -> error::Result> { + let mut conns = self.conns.lock(); + let mut retval = None; + + for conn in conns.iter_mut() { + if conn.nac.value() < self.max_nac { + retval = Some(conn.apply()?); + break; + } + } + + // 在下一次获取连接时, 始终选择热度最低的那个 + conns.sort_by(|a, b| a.nac.value().cmp(&b.nac.value())); + + Ok(retval) + } +} + +impl UConnection { + pub fn apply(&mut self) -> error::Result { + self.nac.add(); + Ok(Connection::new( + self.addr, + AbstractStream::new(HotStream { + nac: self.nac.clone(), + conn: self.conn.duplicate(), + }), + )) + } +} + +impl std::future::Future for UConnection { + type Output = error::Result<()>; + + fn poll( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + Pin::new(&mut self.conn).poll_check(cx) + } +} + +impl std::future::Future for UdpConnections +where + R: Runtime, +{ + type Output = error::Result<()>; + + fn poll( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + let mut conns = self.conns.lock(); + + conns.retain_mut(|conn| match Pin::new(conn).poll(cx) { + Poll::Ready(Err(_)) => false, + _ => true, + }); + + let time = conns + .iter() + .map(|u| u.conn.next_update()) + .min_by(|x, y| x.cmp(y)); + + if let Some(next) = time { + let mut next_update = self.next_update.lock(); + next_update.reset(); + let _ = next_update.poll(cx, || { + R::sleep(std::time::Duration::from_millis(next as u64)) + }); + } + + std::task::Poll::Pending + } +} + + +impl<'a> AsyncRead for HotStream<'a> { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> Poll> { + Pin::new(&mut self.conn).poll_read(cx, buf) + } +} + +impl<'a> AsyncWrite for HotStream<'a> { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut self.conn).poll_write(cx, buf) + } + + fn poll_flush( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll> { + Pin::new(&mut self.conn).poll_flush(cx) + } +} + +impl<'a> Drop for HotStream<'a> { + fn drop(&mut self) { + self.nac.sub(); + } +} + +impl Clone for UdpConnections { + fn clone(&self) -> Self { + Self { + conns: self.conns.clone(), + max_nac: self.max_nac.clone(), + next_update: self.next_update.clone(), + _marked: PhantomData, + } + } +} + +impl Hot { + pub fn sub(&self) { + *self.0.lock() -= 1; + } + + pub fn add(&self) { + *self.0.lock() += 1; + } + + pub fn value(&self) -> usize { + *self.0.lock() + } +} diff --git a/src/server/port_forward/mod.rs b/src/server/port_forward/mod.rs index df6c1f9..91bae55 100644 --- a/src/server/port_forward/mod.rs +++ b/src/server/port_forward/mod.rs @@ -1,4 +1,5 @@ mod accepter; +mod connection; mod handshake; mod preprocessor; mod transport; @@ -15,7 +16,7 @@ use std::{collections::HashMap, pin::Pin, sync::Arc, task::Poll}; use crate::core::future::{Poller, Select}; use crate::core::io::AsyncReadExt; -use crate::core::rpc::structs::port_forward::{self}; +use crate::core::rpc::structs::port_forward::{self, Target}; use crate::core::rpc::AsyncCall; use crate::core::task::{setter, Getter, Setter}; use crate::core::token::IncToken; @@ -25,7 +26,7 @@ use crate::{ core::{ accepter::Accepter, io::{AsyncRead, AsyncWrite}, - processor::{Preprocessor, AbstractPreprocessor}, + processor::{AbstractPreprocessor, Preprocessor}, rpc::structs::port_forward::{Request, VisitorProtocol}, }, error, @@ -33,6 +34,8 @@ use crate::{ use transport::Transport; +use self::connection::UdpConnections; + type Connection = crate::core::Connection<'static>; enum Outcome { @@ -40,10 +43,24 @@ enum Outcome { Mapped(u64), Pending(u64, Getter<()>), Timeout(u64), + Complete(Connection, Connection), Transport(u64, error::FusoError), Stopped(Option), } +#[derive(Debug, Clone, Copy)] +enum ConnKind { + Udp, + Tcp, +} + +struct StashedConn { + kind: ConnKind, + conn: Connection, + #[allow(unused)] + guard: Setter<()>, +} + pub enum Whence { Visitor(Connection), Mapping(Connection), @@ -52,11 +69,12 @@ pub enum Whence { #[derive(Clone)] pub struct Visitors { inc_token: IncToken, - connections: Arc)>>>, + connections: Arc>>, } pub struct PortForwarder { poller: Poller<'static, error::Result>, + uconns: UdpConnections, accepter: A, visitors: Visitors, transport: Transport, @@ -69,7 +87,7 @@ impl Accepter for PortForwarder where A: Accepter + Unpin + 'static, T: Stream + Unpin + Send + 'static, - R: Runtime + Unpin + 'static, + R: Runtime + Sync + Send + Unpin + 'static, { type Output = (Connection, Connection); @@ -90,6 +108,7 @@ where Some(Whence::Visitor(conn)) => { let fut = Self::do_prepare_visitor( conn, + self.uconns.clone(), self.visitors.clone(), self.transport.clone(), self.prepvis.clone(), @@ -102,6 +121,8 @@ where Some(Whence::Mapping(conn)) => { let fut = Self::do_prepare_mapping( conn, + self.uconns.clone(), + self.visitors.clone(), self.transport.clone(), self.prepmap.clone(), ); @@ -121,6 +142,10 @@ where } }; + if let Poll::Ready(()) = Pin::new(&mut self.uconns).poll(ctx)? { + return Poll::Ready(Err(error::FusoError::Abort)); + } + polled = need_poll; match outcome { @@ -132,6 +157,9 @@ where log::debug!("mapping error {{ token={token} }}") } }, + Outcome::Complete(conn, trans) => { + return Poll::Ready(Ok((conn, trans))); + } Outcome::Pending(token, getter) => { if self.visitors.exists(token) { self.poller.add(Self::wait_transport( @@ -190,8 +218,7 @@ where { pub fn new_with_runtime(transport: T, accepter: A, prepvis: P, prepmap: M) -> Self where - P: Preprocessor>, - P: Send + Sync + 'static, + P: Into>>, M: Preprocessor>, M: Send + Sync + 'static, { @@ -206,12 +233,15 @@ where } }); + let uconns = UdpConnections::default(); + Self { poller, + uconns, accepter, transport, visitors: Default::default(), - prepvis: AbstractPreprocessor(Arc::new(prepvis)), + prepvis: prepvis.into(), prepmap: AbstractPreprocessor(Arc::new(prepmap)), _marked: PhantomData, } @@ -247,33 +277,54 @@ where async fn do_prepare_visitor( conn: Connection, + uconns: UdpConnections, visitors: Visitors, transport: Transport, preprocessor: AbstractPreprocessor<'_, Connection, error::Result>, ) -> error::Result { + let mut conn_kind = ConnKind::Tcp; + let mut udp_forward = false; let mut transport = transport; let (conn, addr) = match preprocessor.prepare(conn).await? { - VisitorProtocol::Socks(conn, socks) => (conn, Some(socks)), + VisitorProtocol::Socks(conn, target) => (conn, Some(target)), VisitorProtocol::Other(conn, addr) => match addr { None => (conn, None), Some(target) => (conn, Some(target)), }, }; - log::debug!("create mapping {} -- [T] -- {:?}", conn.addr(), addr); + if let Some(target) = addr.as_ref() { + if let Target::Udp(_, _) = target { + conn_kind = ConnKind::Udp; + udp_forward = true; + } + } + + if udp_forward { + if let Some(transport) = uconns.apply_idle_conn().await? { + return Ok(Outcome::Complete(conn, transport)); + } + } + + log::trace!("create mapping {} -- [T] -- {:?}", conn.addr(), addr); let (setter, getter) = setter(); - let token = visitors.store(conn, setter); + let token = visitors.store(conn_kind, conn, setter); + + let request = match udp_forward { + true => Request::Dyn(token, addr), + false => Request::New(token, addr), + }; let result = R::wait_for(std::time::Duration::from_secs(10), async move { - match transport.call(Request::New(token, addr)).await { + match transport.call(request).await { Err(e) => Err(e), Ok(resp) => match resp { port_forward::Response::Ok => Ok(()), - port_forward::Response::Cancel => Err(error::FusoError::Cancel), port_forward::Response::Error(msg) => Err(msg.into()), + port_forward::Response::Cancel => Err(error::FusoError::Cancel), }, } }) @@ -290,6 +341,8 @@ where async fn do_prepare_mapping( conn: Connection, + uconns: UdpConnections, + visitors: Visitors, _: Transport, preprocessor: AbstractPreprocessor<'_, Connection, error::Result>, ) -> error::Result { @@ -301,7 +354,11 @@ where let token = u64::from_be_bytes(buf); - log::debug!("created mapping {{ token={token} }}"); + if let Some(ConnKind::Udp) = visitors.kind(token) { + conn = uconns.exchange(conn).await?; + }; + + log::trace!("created mapping {{ token={token} }}"); Ok(Outcome::Ready(token, conn)) } @@ -322,13 +379,19 @@ impl Visitors { } fn take(&self, token: u64) -> Option { - self.connections.lock().remove(&token).map(|(c, _)| c) + self.connections.lock().remove(&token).map(|sc| sc.conn) + } + + fn kind(&self, token: u64) -> Option { + self.connections.lock().get(&token).map(|sc| sc.kind) } - fn store(&self, conn: Connection, setter: Setter<()>) -> u64 { + fn store(&self, kind: ConnKind, conn: Connection, guard: Setter<()>) -> u64 { let token = self.next_token(); - self.connections.lock().insert(token, (conn, setter)); + self.connections + .lock() + .insert(token, StashedConn { kind, conn, guard }); token } diff --git a/src/server/port_forward/preprocessor.rs b/src/server/port_forward/preprocessor.rs index 12eb503..a7a690e 100644 --- a/src/server/port_forward/preprocessor.rs +++ b/src/server/port_forward/preprocessor.rs @@ -1,39 +1,43 @@ -use std::{marker::PhantomData, task::Poll}; +use std::{sync::Arc, task::Poll}; use fuso_socks::Socks; use crate::{ config::client::{Addr, ServerAddr}, core::{ - io::{AsyncReadExt, AsyncWriteExt}, - net::{AsyncRecvExt, AsyncRecvFromExt, AsyncSendToExt, UdpProvider}, - processor::Preprocessor, + processor::{self, AbstractPreprocessor, Preprocessor, PreprocessorSelector, Selector}, rpc::structs::port_forward::{Target, VisitorProtocol}, - stream::virio::{self, Vitio}, - transfer::TransmitterExt, - AbstractStream, }, error, + server::udp_forward::UdpForwarder, }; use super::Connection; +#[macro_export] macro_rules! unpack_socks_addr { ($addr: expr) => { match $addr { - fuso_socks::Addr::Socket(ip, port) => (ServerAddr(vec![Addr::WithIpAddr(ip)]), port), - fuso_socks::Addr::Domain(domain, port) => { - (ServerAddr(vec![Addr::WithDomain(domain)]), port) - } + fuso_socks::Addr::Socket(ip, port) => ( + $crate::config::client::ServerAddr(vec![$crate::config::client::Addr::WithIpAddr( + ip, + )]), + port, + ), + fuso_socks::Addr::Domain(domain, port) => ( + $crate::config::client::ServerAddr(vec![$crate::config::client::Addr::WithDomain( + domain, + )]), + port, + ), } }; } -pub struct UdpForwarder {} +pub struct WithPortForwardPreprocessor<'a>(PreprocessorSelector<'a, Connection, VisitorProtocol>); -pub struct Socks5Preprocessor

{ - udp_forwarder: UdpForwarder, - _marked: PhantomData

, +pub struct Socks5Preprocessor { + udp_forwarder: Arc, } impl Preprocessor for () { @@ -85,20 +89,40 @@ impl futures::AsyncWrite for Connection { } } -impl

Socks5Preprocessor

{ - pub fn new() -> Self { +impl<'p> Preprocessor for WithPortForwardPreprocessor<'p> { + type Output = error::Result; + + fn prepare<'a>(&'a self, input: Connection) -> crate::core::BoxedFuture<'a, Self::Output> { + Box::pin(async move { + match self.0.prepare(input).await? { + processor::Prepare::Ok(out) => Ok(out), + processor::Prepare::Bad(input) => Ok(VisitorProtocol::Other(input, None)), + } + }) + } +} + +impl<'a> From> + for AbstractPreprocessor<'a, Connection, error::Result> +{ + fn from(selector: PreprocessorSelector<'a, Connection, VisitorProtocol>) -> Self { + AbstractPreprocessor(Arc::new(WithPortForwardPreprocessor(selector))) + } +} + +impl Socks5Preprocessor { + pub fn new(udp_forwarder: F) -> Self + where + F: UdpForwarder + Sync + Send + 'static, + { Socks5Preprocessor { - udp_forwarder: UdpForwarder {}, - _marked: PhantomData, + udp_forwarder: Arc::new(udp_forwarder), } } } -impl

Preprocessor for Socks5Preprocessor

-where - P: UdpProvider + Sync + 'static, -{ - type Output = error::Result; +impl Preprocessor for Socks5Preprocessor { + type Output = error::Result>; fn prepare<'a>(&'a self, input: Connection) -> crate::core::BoxedFuture<'a, Self::Output> { Box::pin(async move { let mut input = input; @@ -109,69 +133,23 @@ where match fuso_socks::Socks::parse(&mut input, None).await? { Socks::Invalid => { input.reset(); - VisitorProtocol::Other(input, None) + Selector::Next(input) } - Socks::Tcp(addr) => { input.discard(); let (addr, port) = unpack_socks_addr!(addr); - VisitorProtocol::Socks(input, Target::Tcp(addr, port)) + Selector::Accepted(VisitorProtocol::Socks(input, Target::Tcp(addr, port))) } Socks::Udp(addr) => { input.discard(); - let (addr, port) = unpack_socks_addr!(addr); + let (addr, port) = unpack_socks_addr!(addr); let conn = self.udp_forwarder.open(input).await?; - VisitorProtocol::Socks(conn, Target::Udp(addr, port)) + Selector::Accepted(VisitorProtocol::Socks(conn, Target::Udp(addr, port))) } } }) }) } } - - - -impl UdpForwarder { - pub async fn open(&self, conn: Connection) -> error::Result { - let (vio1, vio2) = virio::open(); - - let new_conn = Connection::new(conn.addr().clone(), AbstractStream::new(vio1)); - - Ok(new_conn) - } -} - - -async fn enter_udp_forward( - mut vio: Vitio, - mut input: Connection, -) -> error::Result<()> { - let (listen, udp) = crate::core::net::UdpSocket::bind::(([0, 0, 0, 0], 0)).await?; - - let mut buf = Vec::new(); - - buf.extend(&[0x05, 0x00, 0x00]); - - match listen { - std::net::SocketAddr::V4(addr) => { - buf.push(0x01); - buf.extend(addr.ip().octets()); - buf.extend(addr.port().to_be_bytes()); - } - std::net::SocketAddr::V6(_) => todo!(), - } - - input.write_all(&buf).await?; - - let mut udp = udp; - let mut buf = [0u8; 1024]; - let (addr, n) = udp.recvfrom(&mut buf).await.unwrap(); - - vio.send_all(&buf[..n]).await; - - let a = vio.read(&mut buf).await?; - - Ok(()) -} diff --git a/src/server/udp_forward/mod.rs b/src/server/udp_forward/mod.rs new file mode 100644 index 0000000..e13b637 --- /dev/null +++ b/src/server/udp_forward/mod.rs @@ -0,0 +1,151 @@ +use std::net::SocketAddr; + +use crate::{ + core::{ + future::Select, + io::AsyncWriteExt, + net::{AsyncRecvFromExt, AsyncSendToExt, UdpSocket}, + protocol::{AsyncPacketRead, AsyncPacketSend}, + rpc::{Decoder, Encoder}, + stream::{ + fragment::Fragment, + virio::{self, Vitio}, + Socks5UdpStream, + }, + transfer::TransmitterExt, + AbstractStream, BoxedFuture, + }, + error, unpack_socks_addr, +}; + +type Connection = crate::core::Connection<'static>; + +pub trait UdpForwarder { + fn open<'a>(&'a self, input: Connection) -> BoxedFuture<'a, error::Result>; +} + +pub struct UdpForwarderImpl { + vio: Vitio, + addr: SocketAddr, +} + +impl UdpForwarderImpl { + pub fn new(addr: SocketAddr, udp: UdpSocket) -> (Select>, Self) { + let mut poller = Select::new(); + let (vio_1, vio_2) = virio::open(); + + let (mut writer, mut reader) = vio_2.split(); + + poller.add({ + let mut udp = udp.clone(); + async move { + log::debug!("socks5 udp forward bind at: {addr}"); + + let mut buf = [0u8; 1400]; + + loop { + let (addr, n) = udp.recvfrom(&mut buf).await?; + + if let Ok(pkt) = fuso_socks::ForwardPacket::unpack(&buf[..n]).await { + log::trace!( + "receive udp forward packet({}bytes) from {} to {}", + pkt.data().len(), + addr, + pkt.addr() + ); + + let (mut daddr, port) = unpack_socks_addr!(pkt.addr); + + writer + .send_packet(&{ + Fragment::UdpForward { + saddr: addr, + daddr: unsafe { daddr.0.pop().unwrap_unchecked() }, + dport: port, + data: pkt.data, + } + .encode()? + }) + .await?; + } + } + } + }); + + poller.add(async move { + let mut udp = udp; + loop { + let pkt = reader.recv_packet().await?; + let cloned = pkt.clone(); + + + let fragment: error::Result = cloned.decode(); + + match fragment { + Ok(frg) => { + match frg { + Fragment::Ping => todo!(), + Fragment::UdpForward { .. } => todo!(), + Fragment::Udp(dst, data) => { + log::trace!("response to {} -> {:?}", dst, data); + + let packed = fuso_socks::ForwardPacket::pack(addr, &data); + + udp.send_to(&dst, &packed).await?; + } + } + }, + Err(e) => { + log::error!("{e:?}: {pkt:?}", ) + }, + } + + + } + }); + + (poller, Self { vio: vio_1, addr }) + } +} + +impl UdpForwarder for UdpForwarderImpl { + fn open<'a>(&'a self, mut input: Connection) -> BoxedFuture<'a, error::Result> { + Box::pin(async move { + let mut buf = Vec::new(); + + buf.extend(&[0x05, 0x00, 0x00]); + + match self.addr { + std::net::SocketAddr::V4(addr) => { + buf.push(0x01); + buf.extend(addr.ip().octets()); + buf.extend(addr.port().to_be_bytes()); + } + std::net::SocketAddr::V6(_) => unimplemented!(), + } + + input.write_all(&buf).await?; + + let addr = input.addr().clone(); + + let stream = Socks5UdpStream::new( + AbstractStream::new(input), + AbstractStream::new(self.vio.clone()), + ); + + Ok(Connection::new(addr, AbstractStream::new(stream))) + }) + } +} + +impl UdpForwarder for () { + fn open<'a>(&'a self, mut input: Connection) -> BoxedFuture<'a, error::Result> { + Box::pin(async move { + input + .write_all(&[0x05, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) + .await?; + + Err(error::FusoError::Cancel) + }) + } +} From f53242f1806e55e5ab0c2d461796deb85d1c5af2 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Sat, 11 May 2024 14:44:21 +0800 Subject: [PATCH 20/29] =?UTF-8?q?=E5=AE=8C=E5=96=84=E9=83=A8=E5=88=86?= =?UTF-8?q?=E7=AB=AF=E5=8F=A3=E8=BD=AC=E5=8F=91=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 179 +++++++++++++++++++++++++- Cargo.toml | 2 + build.rs | 24 +++- config/client.toml | 2 +- src/api/mod.rs | 1 + src/api/routes/mod.rs | 8 ++ src/bin/client.rs | 2 + src/bin/server.rs | 4 + src/error/mod.rs | 7 + src/lib.rs | 5 + src/server/port_forward/connection.rs | 24 ++-- src/toy/mod.rs | 1 + src/toy/pty/c/mod.rs | 3 + src/toy/pty/c/pty_impl.c | 76 +++++++++++ src/toy/pty/c/pty_impl.h | 17 +++ src/toy/pty/mod.rs | 158 +++++++++++++++++++++++ 16 files changed, 496 insertions(+), 17 deletions(-) create mode 100644 src/api/mod.rs create mode 100644 src/api/routes/mod.rs create mode 100644 src/toy/mod.rs create mode 100644 src/toy/pty/c/mod.rs create mode 100644 src/toy/pty/c/pty_impl.c create mode 100644 src/toy/pty/c/pty_impl.h create mode 100644 src/toy/pty/mod.rs diff --git a/Cargo.lock b/Cargo.lock index ca97c25..ebc68c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -161,12 +161,41 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags 2.5.0", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.50", + "which", +] + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + [[package]] name = "byteorder" version = "1.5.0" @@ -205,6 +234,15 @@ version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3286b845d0fccbdd15af433f61c5970e711987036cb468f437ff6badd70f4e24" +[[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.0" @@ -221,6 +259,17 @@ dependencies = [ "inout", ] +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.5.1" @@ -277,6 +326,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "either" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" + [[package]] name = "env_filter" version = "0.1.0" @@ -306,6 +361,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "fnv" version = "1.0.7" @@ -326,6 +391,7 @@ name = "fuso" version = "1.0.5-beta" dependencies = [ "axum", + "bindgen", "cc", "clap", "env_logger", @@ -333,6 +399,7 @@ dependencies = [ "futures", "kcp-rust", "log", + "mio", "parking_lot", "pin-project", "rand", @@ -470,6 +537,12 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "h2" version = "0.4.3" @@ -507,6 +580,15 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" +[[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 = "http" version = "1.1.0" @@ -620,6 +702,15 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bd11f3a29434026f5ff98c730b668ba74b1033637b8817940b54d040696133c" +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.10" @@ -636,6 +727,18 @@ dependencies = [ "pin-project", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.153" @@ -652,6 +755,12 @@ dependencies = [ "windows-targets 0.52.3", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + [[package]] name = "lock_api" version = "0.4.11" @@ -686,6 +795,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[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.2" @@ -697,15 +812,26 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", + "log", "wasi", "windows-sys 0.48.0", ] +[[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-traits" version = "0.2.19" @@ -813,6 +939,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "prettyplease" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" +dependencies = [ + "proc-macro2", + "syn 2.0.50", +] + [[package]] name = "proc-macro2" version = "1.0.78" @@ -876,7 +1012,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -936,6 +1072,25 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -1016,6 +1171,12 @@ dependencies = [ "serde", ] +[[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.1" @@ -1278,6 +1439,18 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + [[package]] name = "windows" version = "0.51.1" diff --git a/Cargo.toml b/Cargo.toml index 3bb0228..821806b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ fuso-manager = [ "dep:axum", "fuso-rt-tokio"] [build-dependencies] cc = "1.0" +bindgen = "0.69.4" [dependencies] @@ -62,6 +63,7 @@ rmp-serde = "1.3.0" rand = "0.8.5" tun = { version = "0.6.1", features = ["async"] } futures = { version = "0.3.30", optional = true} +mio = { version = "0.8.11", features = ["os-poll"] } [dependencies.kcp-rust] optional = true diff --git a/build.rs b/build.rs index f7baf93..b0b7915 100644 --- a/build.rs +++ b/build.rs @@ -1,7 +1,29 @@ fn main() { - // cc::Build::new() // .file(concat!("src/core/compress/lz4/third_party/lib/", "lz4.c")) // .include("src/core/compress/lz4/third_party/lib/") // .compile("lib_third_party_compress_lz4") + compile_tty_and_generate_bindings(); +} + +fn compile_tty_and_generate_bindings() { + let manifest_root = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + let tty_source_dir = format!("{manifest_root}/src/toy/pty/c"); + let tty_bindings_out_dir = format!("{}/pty_bindings.rs", std::env::var("OUT_DIR").unwrap()); + + println!("cargo:rerun-if-changed={tty_source_dir}"); + + cc::Build::new() + .file(format!("{tty_source_dir}/pty_impl.c")) + .include(&tty_source_dir) + .warnings(false) + .compile("pty"); + + bindgen::builder() + .header(format!("{tty_source_dir}/pty_impl.h")) + .layout_tests(false) + .generate() + .expect("Failed to generate tty bindings") + .write_to_file(&tty_bindings_out_dir) + .expect(&format!("Failed to write bindings {tty_bindings_out_dir}")); } diff --git a/config/client.toml b/config/client.toml index 058bd4a..d037b9a 100644 --- a/config/client.toml +++ b/config/client.toml @@ -13,7 +13,7 @@ auth = { type = "secret", secret = "12345678" } type = "forward" boot = "fork" exposes = [ "8080/tcp" ] -channel = [ "8002/tcp", "9090/kcp"] +channel = [ "8002/tcp"] restart = "always" cryptos = ["aes"] compress = ["lz4"] diff --git a/src/api/mod.rs b/src/api/mod.rs new file mode 100644 index 0000000..2742106 --- /dev/null +++ b/src/api/mod.rs @@ -0,0 +1 @@ +mod routes; \ No newline at end of file diff --git a/src/api/routes/mod.rs b/src/api/routes/mod.rs new file mode 100644 index 0000000..a0e5fc3 --- /dev/null +++ b/src/api/routes/mod.rs @@ -0,0 +1,8 @@ +async fn fetch_info(){ + +} + + +async fn fetch_service(){ + +} \ No newline at end of file diff --git a/src/bin/client.rs b/src/bin/client.rs index 36416a8..caea614 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -210,6 +210,8 @@ async fn enter_forward_service_main( log::debug!("forward started ."); + + loop { let (linker, target) = forwarder.accept().await?; tokio::spawn(async move { diff --git a/src/bin/server.rs b/src/bin/server.rs index c78c37f..84468ca 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -57,6 +57,8 @@ async fn enter_fuso_main(conf: Config) -> error::Result<()> { .init(); enter_fuso_serve(Stateful::new(conf)).await?; + + // axum::serve(tcp_listener, make_service) loop { @@ -290,6 +292,8 @@ where log::debug!("start forward {} -> {}", c1.addr(), c2.addr()); + + tokio::spawn(async move { let _ = c1.transfer(c2).await; diff --git a/src/error/mod.rs b/src/error/mod.rs index baac5d0..f0955ee 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -17,6 +17,7 @@ pub enum FusoError { InvalidExposeType, NotResponse, Custom(String), + BadCString(std::ffi::NulError), MsgPack(MsgPack), TomlDeError(toml::de::Error), StdIo(std::io::Error), @@ -42,6 +43,12 @@ impl From for FusoError { } } +impl From for FusoError{ + fn from(value: std::ffi::NulError) -> Self { + Self::BadCString(value) + } +} + impl From for FusoError { fn from(value: rmp_serde::encode::Error) -> Self { Self::MsgPack(MsgPack::Encode(value)) diff --git a/src/lib.rs b/src/lib.rs index b052793..d280796 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,11 @@ pub mod error; pub mod runtime; +#[cfg(feature = "fuso-manager")] +pub mod api; + +mod toy; +pub use toy::*; pub fn enter_async_main(fut: F) -> error::Result<()> where diff --git a/src/server/port_forward/connection.rs b/src/server/port_forward/connection.rs index 5b1d294..317c35e 100644 --- a/src/server/port_forward/connection.rs +++ b/src/server/port_forward/connection.rs @@ -125,18 +125,18 @@ where _ => true, }); - let time = conns - .iter() - .map(|u| u.conn.next_update()) - .min_by(|x, y| x.cmp(y)); - - if let Some(next) = time { - let mut next_update = self.next_update.lock(); - next_update.reset(); - let _ = next_update.poll(cx, || { - R::sleep(std::time::Duration::from_millis(next as u64)) - }); - } + // let time = conns + // .iter() + // .map(|u| u.conn.next_update()) + // .min_by(|x, y| x.cmp(y)); + + // if let Some(next) = time { + // let mut next_update = self.next_update.lock(); + // next_update.reset(); + // let _ = next_update.poll(cx, || { + // R::sleep(std::time::Duration::from_millis(next as u64)) + // }); + // } std::task::Poll::Pending } diff --git a/src/toy/mod.rs b/src/toy/mod.rs new file mode 100644 index 0000000..ae151f1 --- /dev/null +++ b/src/toy/mod.rs @@ -0,0 +1 @@ +pub mod pty; \ No newline at end of file diff --git a/src/toy/pty/c/mod.rs b/src/toy/pty/c/mod.rs new file mode 100644 index 0000000..8eadc1c --- /dev/null +++ b/src/toy/pty/c/mod.rs @@ -0,0 +1,3 @@ +pub mod bindings { + include!(concat!(env!("OUT_DIR"), "/pty_bindings.rs")); +} diff --git a/src/toy/pty/c/pty_impl.c b/src/toy/pty/c/pty_impl.c new file mode 100644 index 0000000..cd714f3 --- /dev/null +++ b/src/toy/pty/c/pty_impl.c @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pty_impl.h" + +void enter_pty_main(const int argc, char *const *argv, const char **envp); + +int pty_spawn(pty_process *process) { + int pid; + int master; + int flags; + int status; + + struct winsize size = {.ws_row = 100, .ws_col = 100, 0, 0}; + + pid = forkpty(&master, NULL, NULL, &size); + + if (pid < 0) { + return -errno; + } else if (pid == 0) { + enter_pty_main(process->argc, process->argv, process->envp); + } + + if ((flags = fcntl(master, F_GETFL)) == -1) { + status = -errno; + goto exit_pty; + } + + if (fcntl(master, F_SETFL, flags | O_NONBLOCK) == -1) { + status = -errno; + goto exit_pty; + } + + process->pid = pid; + process->pty = master; + + return 0; + +exit_pty: + + close(master); + + kill(pid, SIGKILL); + + waitpid(pid, NULL, 0); + + return status; +} + +void pty_exit(pty_process *process) { + if (!process) + return; + + if (process->pty) { + close(process->pty); + } + + kill(process->pid, SIGKILL); + waitpid(process->pid, NULL, 0); + + process->pty = -1; + process->pid = -1; +} + +void enter_pty_main(const int argc, char *const *argv, const char **envp) { + int ret = execvp(argv[0], argv); + if (ret < 0) { + _exit(-errno); + } +} diff --git a/src/toy/pty/c/pty_impl.h b/src/toy/pty/c/pty_impl.h new file mode 100644 index 0000000..5e96a23 --- /dev/null +++ b/src/toy/pty/c/pty_impl.h @@ -0,0 +1,17 @@ +#ifndef __TTY_H__ +#define __TTY_H__ + +typedef struct _pty_process { + int pid; + int pty; + const int argc; + const char* work; + char *const *argv; + const char **envp; +} pty_process; + +int pty_spawn(pty_process *process); + +void pty_exit(pty_process *process); + +#endif \ No newline at end of file diff --git a/src/toy/pty/mod.rs b/src/toy/pty/mod.rs new file mode 100644 index 0000000..69b5cdf --- /dev/null +++ b/src/toy/pty/mod.rs @@ -0,0 +1,158 @@ +mod c; + +use std::{collections::HashMap, ffi::CString}; + +use c::bindings; + +use crate::{ + core::io::{AsyncRead, AsyncWrite}, + error, +}; + +extern "C" { + pub fn read(fd: i32, buf: *mut u8, nbyte: usize) -> isize; +} + +#[derive(Debug)] +struct Process { + args: Vec<*const u8>, + envp: Vec<*const u8>, + work_dir: *const u8, +} + +#[derive(Debug)] +pub struct Pty { + pty: bindings::pty_process, + proc: Process, + work: Option, + argv: Vec, + envp: Vec, +} + +impl Pty { + fn open( + prog: String, + work: Option, + args: Option>, + envp: Option>, + ) -> error::Result { + unsafe { + let mut pty = std::mem::zeroed::(); + + let work = match work { + Some(work) => Some(CString::new(work)?), + None => None, + }; + + let mut argv = Vec::::new(); + + argv.push(CString::new(prog)?); + + if let Some(args) = args { + for arg in args { + argv.push(CString::new(arg)?); + } + } + + let envp = match envp { + None => Vec::new(), + Some(envp) => envp.into_iter().fold(Vec::new(), |mut envp, (k, v)| { + envp.push(CString::new(format!("{k}={v}")).unwrap()); + envp + }), + }; + + let proc = Process { + args: { + let mut args = vec![]; + args.extend(argv.iter().map(|arg| arg.as_ptr() as *const u8)); + args.push(std::ptr::null()); + args + }, + envp: { + let mut envs = vec![]; + envs.extend(envp.iter().map(|env| env.as_ptr() as *const u8)); + envs.push(std::ptr::null()); + envs + }, + work_dir: { + work.as_ref() + .map(|work| work.as_ptr() as _) + .unwrap_or(std::ptr::null()) + }, + }; + + pty.argv = proc.args.as_ptr() as _; + pty.envp = proc.envp.as_ptr() as _; + pty.work = proc.work_dir as _; + + let error = bindings::pty_spawn(&mut pty as *mut bindings::pty_process); + + if error < 0 { + Err(std::io::Error::from_raw_os_error(error).into()) + } else { + Ok(Self { + pty, + proc, + work, + argv, + envp, + }) + } + } + } +} + +impl Drop for Pty { + fn drop(&mut self) { + unsafe { bindings::pty_exit(&mut self.pty as *mut bindings::pty_process) } + } +} + +impl AsyncRead for Pty { + fn poll_read( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + unimplemented!() + } +} + +impl AsyncWrite for Pty { + fn poll_write( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + unimplemented!() + } + + fn poll_flush( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + unimplemented!() + } +} + +#[cfg(test)] +mod tests { + use std::os::fd::FromRawFd; + + use mio::{unix::SourceFd, Events, Interest, Token}; + use tokio::io::AsyncReadExt; + + use super::Pty; + + #[tokio::test] + async fn test_pty() { + let pty = Pty::open("/bin/sh".to_owned(), None, None, None).unwrap(); + + let poll = mio::Poll::new().unwrap(); + + poll.registry(); + + println!("{pty:?} {:?}", pty.argv) + } +} From f951b1e2e853a88ccca81c7f455b5df9f0b180af Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Sun, 12 May 2024 13:47:52 +0800 Subject: [PATCH 21/29] =?UTF-8?q?=E6=B7=BB=E5=8A=A0windows=20pty=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .clang-format | 28 +++ Cargo.lock | 174 +--------------- Cargo.toml | 4 +- build.rs | 8 - src/bin/client.rs | 9 +- src/core/io/mod.rs | 1 + src/core/task/mod.rs | 8 +- src/error/mod.rs | 7 + src/runtime/tokio/mod.rs | 1 + src/runtime/tokio/pty.rs | 104 ++++++++++ src/server/udp_forward/mod.rs | 2 +- src/toy/pty/c/mod.rs | 90 +++++++- src/toy/pty/c/pty_impl.c | 252 +++++++++++++++++++++++ src/toy/pty/c/pty_impl.h | 27 ++- src/toy/pty/mod.rs | 373 ++++++++++++++++++++++++++++++---- 15 files changed, 857 insertions(+), 231 deletions(-) create mode 100644 .clang-format create mode 100644 src/runtime/tokio/pty.rs diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..acb294b --- /dev/null +++ b/.clang-format @@ -0,0 +1,28 @@ +Language: Cpp +BasedOnStyle: LLVM +SortIncludes: false +ColumnLimit: 100 +IndentWidth: 2 +PointerAlignment: Right +AlignTrailingComments: true +MaxEmptyLinesToKeep: 2 +SpaceBeforeAssignmentOperators: true +DerivePointerAlignment: true +AlignConsecutiveMacros: true +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: true +AllowShortBlocksOnASingleLine: Empty +AllowShortFunctionsOnASingleLine: Empty +# AllowShortFunctionsOnASingleLine: SFS_None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: true +AlignAfterOpenBracket: Align +# AllowShortIfStatementsOnASingleLine: true +IndentCaseLabels: true +# AlignArrayOfStructures: Left +# ArrayInitializerAlignmentStyle: Left +# TrailingCommentsAlignmentKinds: TCAS_Always +AllowShortCaseLabelsOnASingleLine: false +AlignEscapedNewlines: Left +FixNamespaceComments: true +# AlwaysBreakAfterDefinitionReturnType: DRTBS_None \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index ebc68c8..fc89475 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -161,41 +161,12 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "bindgen" -version = "0.69.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" -dependencies = [ - "bitflags 2.5.0", - "cexpr", - "clang-sys", - "itertools", - "lazy_static", - "lazycell", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.50", - "which", -] - [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bitflags" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" - [[package]] name = "byteorder" version = "1.5.0" @@ -234,15 +205,6 @@ version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3286b845d0fccbdd15af433f61c5970e711987036cb468f437ff6badd70f4e24" -[[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.0" @@ -259,17 +221,6 @@ dependencies = [ "inout", ] -[[package]] -name = "clang-sys" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" version = "4.5.1" @@ -326,12 +277,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "either" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" - [[package]] name = "env_filter" version = "0.1.0" @@ -361,16 +306,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "errno" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "fnv" version = "1.0.7" @@ -391,7 +326,6 @@ name = "fuso" version = "1.0.5-beta" dependencies = [ "axum", - "bindgen", "cc", "clap", "env_logger", @@ -414,6 +348,7 @@ dependencies = [ [[package]] name = "fuso-socks" version = "0.1.0" +source = "git+https://github.com/editso/fuso-socks5?branch=2.0#1053a880bb15193f27e570c36143a0d34ece05f7" dependencies = [ "async-trait", "bytes", @@ -537,12 +472,6 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - [[package]] name = "h2" version = "0.4.3" @@ -580,15 +509,6 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" -[[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 = "http" version = "1.1.0" @@ -702,15 +622,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bd11f3a29434026f5ff98c730b668ba74b1033637b8817940b54d040696133c" -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.10" @@ -727,18 +638,6 @@ dependencies = [ "pin-project", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" version = "0.2.153" @@ -755,12 +654,6 @@ dependencies = [ "windows-targets 0.52.3", ] -[[package]] -name = "linux-raw-sys" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" - [[package]] name = "lock_api" version = "0.4.11" @@ -795,12 +688,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[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.2" @@ -822,16 +709,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[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-traits" version = "0.2.19" @@ -939,16 +816,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "prettyplease" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" -dependencies = [ - "proc-macro2", - "syn 2.0.50", -] - [[package]] name = "proc-macro2" version = "1.0.78" @@ -1012,7 +879,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] @@ -1072,25 +939,6 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustix" -version = "0.38.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" -dependencies = [ - "bitflags 2.5.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - [[package]] name = "rustversion" version = "1.0.14" @@ -1171,12 +1019,6 @@ dependencies = [ "serde", ] -[[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.1" @@ -1439,18 +1281,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - [[package]] name = "windows" version = "0.51.1" diff --git a/Cargo.toml b/Cargo.toml index 821806b..a986db8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,6 @@ fuso-manager = [ "dep:axum", "fuso-rt-tokio"] [build-dependencies] cc = "1.0" -bindgen = "0.69.4" [dependencies] @@ -73,7 +72,8 @@ default-features = false [dependencies.fuso-socks] optional = true -path = "../fuso-socks5" +git = "https://github.com/editso/fuso-socks5" +branch = "2.0" [dependencies.axum] optional = true diff --git a/build.rs b/build.rs index b0b7915..2b4f55d 100644 --- a/build.rs +++ b/build.rs @@ -18,12 +18,4 @@ fn compile_tty_and_generate_bindings() { .include(&tty_source_dir) .warnings(false) .compile("pty"); - - bindgen::builder() - .header(format!("{tty_source_dir}/pty_impl.h")) - .layout_tests(false) - .generate() - .expect("Failed to generate tty bindings") - .write_to_file(&tty_bindings_out_dir) - .expect(&format!("Failed to write bindings {tty_bindings_out_dir}")); } diff --git a/src/bin/client.rs b/src/bin/client.rs index caea614..eec6e05 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -210,8 +210,6 @@ async fn enter_forward_service_main( log::debug!("forward started ."); - - loop { let (linker, target) = forwarder.accept().await?; tokio::spawn(async move { @@ -219,7 +217,12 @@ async fn enter_forward_service_main( match target { FinalTarget::Udp { addr, port } => {} - FinalTarget::Shell { path, args } => todo!(), + FinalTarget::Shell { path, args } => { + let mut builder = fuso::pty::builder(""); + + + + } FinalTarget::Dynamic => { let transmitter = linker.link(Protocol::Tcp).await.unwrap(); diff --git a/src/core/io/mod.rs b/src/core/io/mod.rs index 29deab0..3ce36c5 100644 --- a/src/core/io/mod.rs +++ b/src/core/io/mod.rs @@ -90,6 +90,7 @@ where } writer.write_all(&buf[..n]).await?; + writer.flush().await?; } } diff --git a/src/core/task/mod.rs b/src/core/task/mod.rs index c0cf188..fb4ed7c 100644 --- a/src/core/task/mod.rs +++ b/src/core/task/mod.rs @@ -22,7 +22,7 @@ pub struct Getter { pub struct Setter { val: Arc>>, - setted: bool, + set: bool, waker: Arc>>, } @@ -30,7 +30,7 @@ impl Setter { pub fn set(mut self, val: V) -> error::Result<()> { drop(self.val.lock().replace(val)); self.try_wake(); - self.setted = true; + self.set = true; Ok(()) } @@ -48,7 +48,7 @@ impl Setter { impl Drop for Setter { fn drop(&mut self) { - if !self.setted { + if !self.set { self.invalid(); } } @@ -97,7 +97,7 @@ pub fn setter() -> (Setter, Getter) { Setter { val: val.clone(), waker: waker.clone(), - setted: false, + set: false, }, Getter { val, waker }, ) diff --git a/src/error/mod.rs b/src/error/mod.rs index f0955ee..c63cba8 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -17,6 +17,7 @@ pub enum FusoError { InvalidExposeType, NotResponse, Custom(String), + Utf8Error(std::str::Utf8Error), BadCString(std::ffi::NulError), MsgPack(MsgPack), TomlDeError(toml::de::Error), @@ -76,6 +77,12 @@ impl From for std::io::Error { } } +impl From for FusoError{ + fn from(value: std::str::Utf8Error) -> Self { + Self::Utf8Error(value) + } +} + impl Display for FusoError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self) diff --git a/src/runtime/tokio/mod.rs b/src/runtime/tokio/mod.rs index 4e28d33..b1e2d35 100644 --- a/src/runtime/tokio/mod.rs +++ b/src/runtime/tokio/mod.rs @@ -1,5 +1,6 @@ mod kcp; mod tcp; +mod pty; mod port_forward; diff --git a/src/runtime/tokio/pty.rs b/src/runtime/tokio/pty.rs new file mode 100644 index 0000000..4bc89ff --- /dev/null +++ b/src/runtime/tokio/pty.rs @@ -0,0 +1,104 @@ +use std::task::Poll; + +use tokio::io::ReadBuf; + +use crate::core::io::{AsyncRead, AsyncWrite}; + +impl AsyncRead for tokio::io::Stdin { + fn poll_read( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + let mut buf = ReadBuf::new(buf); + match tokio::io::AsyncRead::poll_read(self, cx, &mut buf)? { + Poll::Pending => Poll::Pending, + Poll::Ready(()) => Poll::Ready(Ok(buf.filled().len())), + } + } +} + +impl AsyncWrite for tokio::io::Stdout { + fn poll_write( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> Poll> { + tokio::io::AsyncWrite::poll_write(self, cx, buf).map_err(Into::into) + } + + fn poll_flush( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll> { + tokio::io::AsyncWrite::poll_flush(self, cx).map_err(Into::into) + } +} + +#[cfg(windows)] +mod windows { + use std::task::Poll; + + use tokio::io::ReadBuf; + + use crate::{ + core::io::{AsyncRead, AsyncWrite}, + error, + pty::{self, OpenPty, OpenedPty, PtyBuilder}, + }; + + struct WithTokio; + + impl PtyBuilder { + pub fn build(self) -> error::Result { + self.build_with::() + } + } + + impl OpenPty for WithTokio { + fn connect_input(input: String) -> error::Result { + tokio::net::windows::named_pipe::ClientOptions::new() + .open(input) + .map(pty::AbstractPtyInput::new) + .map_err(Into::into) + } + + fn connect_output(output: String) -> error::Result { + tokio::net::windows::named_pipe::ClientOptions::new() + .open(output) + .map(pty::AbstractPtyOutput::new) + .map_err(Into::into) + } + } + + impl AsyncRead for tokio::net::windows::named_pipe::NamedPipeClient { + fn poll_read( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + let mut buf = ReadBuf::new(buf); + match tokio::io::AsyncRead::poll_read(self, cx, &mut buf)? { + Poll::Pending => Poll::Pending, + Poll::Ready(()) => Poll::Ready(Ok(buf.filled().len())), + } + } + } + + impl AsyncWrite for tokio::net::windows::named_pipe::NamedPipeClient { + fn poll_write( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + tokio::io::AsyncWrite::poll_write(self, cx, buf).map_err(Into::into) + } + + fn poll_flush( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + tokio::io::AsyncWrite::poll_flush(self, cx).map_err(Into::into) + } + } +} diff --git a/src/server/udp_forward/mod.rs b/src/server/udp_forward/mod.rs index e13b637..0c0f6f5 100644 --- a/src/server/udp_forward/mod.rs +++ b/src/server/udp_forward/mod.rs @@ -53,7 +53,7 @@ impl UdpForwarderImpl { addr, pkt.addr() ); - + let (mut daddr, port) = unpack_socks_addr!(pkt.addr); writer diff --git a/src/toy/pty/c/mod.rs b/src/toy/pty/c/mod.rs index 8eadc1c..fae5714 100644 --- a/src/toy/pty/c/mod.rs +++ b/src/toy/pty/c/mod.rs @@ -1,3 +1,91 @@ pub mod bindings { - include!(concat!(env!("OUT_DIR"), "/pty_bindings.rs")); + + #[repr(C)] + #[cfg(not(windows))] + pub struct PtyProcess { + pub pid: ::std::os::raw::c_int, + pub pty: ::std::os::raw::c_int, + pub argv: *const *mut ::std::os::raw::c_char, + pub environs: *const *const ::std::os::raw::c_char, + pub work: *const ::std::os::raw::c_char, + } + + #[repr(C)] + #[cfg(windows)] + pub struct PtyProcess { + pub pid: ::std::os::raw::c_int, + pub pty: *const ::std::os::raw::c_void, + pub cmdline: *const ::std::os::raw::c_char, + pub environs: *const ::std::os::raw::c_ushort, + pub work_dir: *const ::std::os::raw::c_char, + + pub wait: *const ::std::os::raw::c_void, + pub handle: *const ::std::os::raw::c_void, + pub startup_info: *const ::std::os::raw::c_void, + pub pipe_input_name: *const u8, + pub pipe_output_name: *const u8, + + pub exit_cb: *const extern "C" fn(*mut PtyProcess), + pub exit_cb_data: *const ::std::os::raw::c_void, + } + + extern "C" { + pub fn pty_exit(process: *mut PtyProcess); + pub fn pty_spawn(process: *mut PtyProcess) -> ::std::os::raw::c_int; + } +} + +#[cfg(windows)] +pub mod windows_ext { + + use std::os::windows; + + use crate::error; + + type DWORD = ::std::os::raw::c_ulong; + + const ENABLE_ECHO_INPUT: DWORD = 0x0004; + const ENABLE_LINE_INPUT: DWORD = 0x0002; + + pub trait ConsoleExt { + fn set_console_mode(&self, mode: DWORD) -> error::Result<()>; + + fn enable_input_mode(&self) -> error::Result<()> { + self.set_console_mode(!(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT)) + } + } + + impl ConsoleExt for T + where + T: std::os::windows::io::AsRawHandle, + { + fn set_console_mode(&self, mode: DWORD) -> error::Result<()> { + #[link(name = "kernel32")] + extern "C" { + fn GetLastError() -> DWORD; + + /// https://learn.microsoft.com/en-us/windows/console/setconsolemode + fn SetConsoleMode(handle: windows::raw::HANDLE, mode: DWORD) -> bool; + + /// https://learn.microsoft.com/en-us/windows/console/getconsolemode + fn GetConsoleMode(handle: windows::raw::HANDLE, mode: *mut DWORD) -> bool; + } + + let handle = self.as_raw_handle(); + + unsafe { + let mut old_mode = DWORD::default(); + + if !GetConsoleMode(handle, &mut old_mode as *mut DWORD) { + return Err(std::io::Error::from_raw_os_error(GetLastError() as _).into()); + } + + if !SetConsoleMode(handle, old_mode & mode) { + return Err(std::io::Error::from_raw_os_error(GetLastError() as _).into()); + } + + Ok(()) + } + } + } } diff --git a/src/toy/pty/c/pty_impl.c b/src/toy/pty/c/pty_impl.c index cd714f3..0ebddee 100644 --- a/src/toy/pty/c/pty_impl.c +++ b/src/toy/pty/c/pty_impl.c @@ -1,3 +1,11 @@ +#ifdef _WIN32 +#include +#include +#include + +#include + +#else #include #include #include @@ -6,9 +14,251 @@ #include #include #include +#endif #include "pty_impl.h" +#ifdef _WIN32 + +typedef HRESULT (*WINAPI CreatePseudoConsoleFn)(COORD size, HANDLE hInput, HANDLE hOutput, + DWORD dwFlags, HPCON *phPC); + +typedef HRESULT (*WINAPI ResizePseudoConsoleFn)(HPCON hPC, COORD size); + +typedef void (*WINAPI ClosePseudoConsoleFn)(HPCON hPC); + +static struct { + HMODULE module; + ClosePseudoConsoleFn close_pseudo_console; + CreatePseudoConsoleFn create_pseudo_console; + ResizePseudoConsoleFn resize_pseudo_console; +} conpty_api = {.module = NULL, + .resize_pseudo_console = NULL, + .create_pseudo_console = NULL, + .resize_pseudo_console = NULL}; + +int conpty_init(); + +void clean_process_startup_info(STARTUPINFOEX *si); + +int setup_pseudo_console(pty_process *process, STARTUPINFOEX *psi, COORD size); + +int pty_spawn(pty_process *process) { + STARTUPINFOEX *psi; + PROCESS_INFORMATION pi; + COORD size; + HANDLE hJob; + HANDLE hWait; + HANDLE inputReadSide; + HANDLE outputWriteSide; + + if (conpty_init() != 0) + return -1; + + size.X = 100; + size.Y = 100; + + hWait = NULL; + psi = (STARTUPINFOEX *)VirtualAlloc(NULL, sizeof(STARTUPINFOEX), MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE); + + if (!psi) { + return -1; + } + + if (setup_pseudo_console(process, psi, size) != 0) { + goto error; + } + + ZeroMemory(&pi, sizeof(pi)); + + hJob = CreateJobObject(NULL, NULL); + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {0}; + jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + + SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli)); + AssignProcessToJobObject(hJob, GetCurrentProcess()); + + SetConsoleCtrlHandler(NULL, FALSE); + + if (!CreateProcessA(NULL, (LPSTR)process->cmdline, NULL, NULL, FALSE, + EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT | CREATE_SUSPENDED, + (LPVOID)process->environs, (LPCSTR)process->work, &psi->StartupInfo, &pi)) { + goto error; + } + + if (process->exit_cb) { + if (!RegisterWaitForSingleObject(&hWait, pi.hProcess, process->exit_cb, process->exit_cb_data, + INFINITE, WT_EXECUTEONLYONCE)) { + goto error; + } + } + + + process->wait = hWait; + process->pid = pi.dwProcessId; + process->handle = pi.hProcess; + process->startup_info = psi; + + ResumeThread(pi.hThread); + CloseHandle(pi.hThread); + + AssignProcessToJobObject(hJob, pi.hProcess); + + return 0; + +error: + + clean_process_startup_info(psi); + + return HRESULT_FROM_WIN32(GetLastError()); +} + +void pty_exit(pty_process *process) { + if (!process || conpty_init() != 0) + return; + + if (process->handle) { + + if (process->wait) { + UnregisterWait(process->wait); + process->wait = NULL; + } + + clean_process_startup_info(process->startup_info); + TerminateProcess((HANDLE)process->handle, 0); + CloseHandle(process->handle); + } + + if (process->pty) { + conpty_api.close_pseudo_console(process->pty); + } + + process->pty = NULL; + process->handle = NULL; + process->startup_info = NULL; +} + +void clean_process_startup_info(STARTUPINFOEX *si) { + if (!si) + return; + + if (si->lpAttributeList != NULL) { + DeleteProcThreadAttributeList(si->lpAttributeList); + } + + VirtualFree(si, 0, MEM_RELEASE); +} + +int setup_pseudo_console(pty_process *process, STARTUPINFOEX *si, COORD size) { + HPCON hPC = INVALID_HANDLE_VALUE; + HRESULT hr = S_OK; + + CHAR inputPipeName[255]; + CHAR outputPipeName[255]; + + HANDLE pInputReadSide = INVALID_HANDLE_VALUE; + HANDLE pOutputWriteSide = INVALID_HANDLE_VALUE; + + SECURITY_ATTRIBUTES sa = {0}; + + size_t bytesRequired; + + DWORD pipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT; + DWORD pipeOpenMode = PIPE_ACCESS_INBOUND | PIPE_ACCESS_OUTBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE; + + ZeroMemory(inputPipeName, sizeof(inputPipeName)); + ZeroMemory(outputPipeName, sizeof(outputPipeName)); + + if (!process->pipe_output_name || !process->pipe_input_name) { + return -1; + } + + sprintf(inputPipeName, "\\\\.\\pipe\\%s", process->pipe_input_name); + sprintf(outputPipeName, "\\\\.\\pipe\\%s", process->pipe_output_name); + + pInputReadSide = CreateNamedPipeA(inputPipeName, pipeOpenMode, pipeMode, 1, 0, 0, 30000, &sa); + pOutputWriteSide = CreateNamedPipeA(outputPipeName, pipeOpenMode, pipeMode, 1, 0, 0, 30000, &sa); + + if (pInputReadSide == INVALID_HANDLE_VALUE || pOutputWriteSide == INVALID_HANDLE_VALUE) { + goto cleanup; + } + + hr = conpty_api.create_pseudo_console(size, pInputReadSide, pOutputWriteSide, 0, &hPC); + + if (FAILED(hr)) { + goto cleanup; + } + + ZeroMemory(si, sizeof(STARTUPINFOEX)); + + si->StartupInfo.cb = sizeof(STARTUPINFOEX); + si->StartupInfo.dwFlags |= STARTF_USESTDHANDLES; + si->StartupInfo.hStdError = NULL; + si->StartupInfo.hStdInput = NULL; + si->StartupInfo.hStdOutput = NULL; + + InitializeProcThreadAttributeList(NULL, 1, 0, &bytesRequired); + + si->lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, bytesRequired); + + if (!si->lpAttributeList) { + goto cleanup; + } + + if (!InitializeProcThreadAttributeList(si->lpAttributeList, 1, 0, &bytesRequired)) { + HeapFree(GetProcessHeap(), 0, si->lpAttributeList); + goto cleanup; + } + + if (!UpdateProcThreadAttribute(si->lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, hPC, + sizeof(hPC), NULL, NULL)) { + HeapFree(GetProcessHeap(), 0, si->lpAttributeList); + goto cleanup; + } + + process->pty = hPC; + +cleanup: + + if (pInputReadSide != INVALID_HANDLE_VALUE) + CloseHandle(pInputReadSide); + if (pOutputWriteSide != INVALID_HANDLE_VALUE) + CloseHandle(pOutputWriteSide); + + return S_OK; +} + + +int conpty_init() { + + if (!conpty_api.module) { + conpty_api.module = LoadLibraryA("kernel32.dll"); + if (!conpty_api.module) + return -GetLastError(); + } + + if (!conpty_api.close_pseudo_console) { + conpty_api.close_pseudo_console = + (ClosePseudoConsoleFn)GetProcAddress(conpty_api.module, "ClosePseudoConsole"); + } + + if (!conpty_api.create_pseudo_console) { + conpty_api.create_pseudo_console = + (CreatePseudoConsoleFn)GetProcAddress(conpty_api.module, "CreatePseudoConsole"); + } + + if (!conpty_api.resize_pseudo_console) { + conpty_api.resize_pseudo_console = + (ResizePseudoConsoleFn)GetProcAddress(conpty_api.module, "ResizePseudoConsole"); + } + + return 0; +} + + +#else + void enter_pty_main(const int argc, char *const *argv, const char **envp); int pty_spawn(pty_process *process) { @@ -74,3 +324,5 @@ void enter_pty_main(const int argc, char *const *argv, const char **envp) { _exit(-errno); } } + +#endif \ No newline at end of file diff --git a/src/toy/pty/c/pty_impl.h b/src/toy/pty/c/pty_impl.h index 5e96a23..496a3b4 100644 --- a/src/toy/pty/c/pty_impl.h +++ b/src/toy/pty/c/pty_impl.h @@ -3,11 +3,30 @@ typedef struct _pty_process { int pid; - int pty; - const int argc; - const char* work; + +#ifndef _WIN32 + int pty; char *const *argv; - const char **envp; + const char **environs; +#else + void *pty; + const char *cmdline; + const unsigned short *environs; +#endif + + const char *work; + +#ifdef _WIN32 + void *wait; + void *handle; + void *startup_info; + const char *pipe_input_name; + const char *pipe_output_name; + void (*exit_cb)(void *context, unsigned char); + void *exit_cb_data; + +#endif + } pty_process; int pty_spawn(pty_process *process); diff --git a/src/toy/pty/mod.rs b/src/toy/pty/mod.rs index 69b5cdf..449bb68 100644 --- a/src/toy/pty/mod.rs +++ b/src/toy/pty/mod.rs @@ -1,11 +1,18 @@ mod c; -use std::{collections::HashMap, ffi::CString}; +use std::{collections::HashMap, ffi::CString, pin::Pin, sync::Arc, task::Poll}; use c::bindings; +#[cfg(windows)] +pub use c::windows_ext::*; +use parking_lot::Mutex; + use crate::{ - core::io::{AsyncRead, AsyncWrite}, + core::{ + io::{AsyncRead, AsyncWrite}, + task::{setter, Getter, Setter}, + }, error, }; @@ -14,19 +21,77 @@ extern "C" { } #[derive(Debug)] +#[allow(unused)] struct Process { + #[cfg(not(windows))] args: Vec<*const u8>, - envp: Vec<*const u8>, + + #[cfg(windows)] + cmdline: *const u8, + + #[cfg(not(windows))] + environs: Vec<*const u8>, + + #[cfg(windows)] + environs: Vec, + work_dir: *const u8, + + #[cfg(windows)] + pipe_input_name: *const u8, + #[cfg(windows)] + pipe_output_name: *const u8, } -#[derive(Debug)] +#[allow(unused)] pub struct Pty { - pty: bindings::pty_process, + pty: bindings::PtyProcess, proc: Process, + #[cfg(windows)] + pipe_input_name: CString, + #[cfg(windows)] + pipe_output_name: CString, work: Option, + #[cfg(not(windows))] argv: Vec, - envp: Vec, + #[cfg(not(windows))] + environs: Vec, + #[cfg(windows)] + environs: Vec, + #[cfg(windows)] + cmdline: CString, + #[cfg(windows)] + input_signal: Getter<()>, + output_signal: Getter<()>, +} + +pub struct OpenedPty { + pty: Arc>, + input: Arc>, + output: Arc>, +} + +#[derive(Debug)] +pub struct PtyBuilder { + program: String, + arguments: Vec, + work_dir: Option, + environs: HashMap, + inherit_parent_env: bool, +} + +pub trait PtyInput: AsyncWrite {} + +pub trait PtyOutput: AsyncRead {} + +pub struct AbstractPtyInput(Box); + +pub struct AbstractPtyOutput(Box); + +#[cfg(windows)] +pub trait OpenPty { + fn connect_input(input: String) -> error::Result; + fn connect_output(output: String) -> error::Result; } impl Pty { @@ -37,24 +102,30 @@ impl Pty { envp: Option>, ) -> error::Result { unsafe { - let mut pty = std::mem::zeroed::(); + let mut pty = std::mem::zeroed::(); let work = match work { Some(work) => Some(CString::new(work)?), None => None, }; - let mut argv = Vec::::new(); + #[cfg(not(windows))] + let mut argv = { + let mut argv = Vec::::new(); - argv.push(CString::new(prog)?); + argv.push(CString::new(prog)?); - if let Some(args) = args { - for arg in args { - argv.push(CString::new(arg)?); + if let Some(args) = args { + for arg in args { + argv.push(CString::new(arg)?); + } } - } - let envp = match envp { + argv + }; + + #[cfg(not(windows))] + let environs = match envp { None => Vec::new(), Some(envp) => envp.into_iter().fold(Vec::new(), |mut envp, (k, v)| { envp.push(CString::new(format!("{k}={v}")).unwrap()); @@ -62,31 +133,111 @@ impl Pty { }), }; + #[cfg(windows)] + let environs = { + match envp { + None => vec![], + Some(envp) => envp.into_iter().fold(Vec::new(), |mut envp, (k, v)| { + envp.push(format!("{k}={v}")); + envp + }), + } + }; + + #[cfg(windows)] + let cmdline = { + let mut argv = vec![prog]; + CString::new( + match args { + None => argv, + Some(args) => { + argv.extend(args); + argv + } + } + .join(" "), + )? + }; + + let pipe_input_name = CString::new(format!("pty-fuso-input-{}", std::process::id()))?; + let pipe_output_name = CString::new(format!("pty-fuso-output-{}", std::process::id()))?; + let proc = Process { + #[cfg(not(windows))] args: { let mut args = vec![]; args.extend(argv.iter().map(|arg| arg.as_ptr() as *const u8)); args.push(std::ptr::null()); args }, - envp: { + #[cfg(not(windows))] + environs: { let mut envs = vec![]; - envs.extend(envp.iter().map(|env| env.as_ptr() as *const u8)); + envs.extend(environs.iter().map(|env| env.as_ptr() as *const u8)); envs.push(std::ptr::null()); envs }, + #[cfg(windows)] + environs: { + if environs.is_empty() { + vec![] + } else { + let mut envp = environs.iter().fold(Vec::new(), |mut envs, env| { + envs.extend(env.encode_utf16().collect::>()); + envs.push(0); + envs + }); + envp.push(0); + envp + } + }, work_dir: { work.as_ref() .map(|work| work.as_ptr() as _) .unwrap_or(std::ptr::null()) }, + #[cfg(windows)] + cmdline: cmdline.as_ptr() as _, + pipe_input_name: pipe_input_name.as_ptr() as _, + pipe_output_name: pipe_output_name.as_ptr() as _, }; - pty.argv = proc.args.as_ptr() as _; - pty.envp = proc.envp.as_ptr() as _; - pty.work = proc.work_dir as _; + #[cfg(not(windows))] + { + pty.argv = proc.args.as_ptr() as _; + pty.envp = proc.envp.as_ptr() as _; + } + + #[cfg(windows)] + { + pty.cmdline = proc.cmdline as _; + if proc.environs.is_empty() { + pty.environs = std::ptr::null(); + } else { + pty.environs = proc.environs.as_ptr() as _; + } + } + + pty.work_dir = proc.work_dir as _; + pty.pipe_input_name = proc.pipe_input_name; + pty.pipe_output_name = proc.pipe_output_name; - let error = bindings::pty_spawn(&mut pty as *mut bindings::pty_process); + fn pty_process_exit_cb(data: *mut std::os::raw::c_void) { + unsafe { + drop(Box::<(Setter<()>, Setter<()>)>::from_raw( + data as *mut (Setter<()>, Setter<()>), + )); + } + } + + pty.exit_cb = pty_process_exit_cb as _; + + let (set_input_signal, input_signal) = setter::<()>(); + let (set_output_signal, output_signal) = setter::<()>(); + + pty.exit_cb_data = Box::into_raw(Box::new((set_input_signal, set_output_signal))) as _; + + let error = bindings::pty_spawn(&mut pty as *mut bindings::PtyProcess); if error < 0 { Err(std::io::Error::from_raw_os_error(error).into()) @@ -95,8 +246,17 @@ impl Pty { pty, proc, work, + #[cfg(not(windows))] argv, + #[cfg(not(windows))] envp, + #[cfg(windows)] + cmdline, + environs, + pipe_input_name, + pipe_output_name, + input_signal, + output_signal, }) } } @@ -105,54 +265,195 @@ impl Pty { impl Drop for Pty { fn drop(&mut self) { - unsafe { bindings::pty_exit(&mut self.pty as *mut bindings::pty_process) } + unsafe { bindings::pty_exit(&mut self.pty as *mut bindings::PtyProcess) } } } -impl AsyncRead for Pty { +pub fn builder(program: P) -> PtyBuilder { + PtyBuilder { + program: program.to_string(), + arguments: vec![], + work_dir: None, + environs: Default::default(), + inherit_parent_env: true, + } +} + +impl AbstractPtyInput { + pub fn new(input: I) -> Self + where + I: PtyInput + Send + Sync + Unpin + 'static, + { + Self(Box::new(input)) + } +} + +impl AbstractPtyOutput { + pub fn new(output: I) -> Self + where + I: PtyOutput + Send + Sync + Unpin + 'static, + { + Self(Box::new(output)) + } +} + +impl PtyBuilder { + pub fn arg(&mut self, arg: A) -> &mut Self { + self.arguments.push(arg.to_string().trim().to_owned()); + self + } + + pub fn args(&mut self, args: &[A]) -> &mut Self { + for arg in args { + self.arguments.push(arg.to_string().trim().to_owned()); + } + self + } + + pub fn work_dir(&mut self, work_dir: A) -> &mut Self { + self.work_dir.replace(work_dir.to_string()); + self + } + + pub fn env(&mut self, key: K, value: V) -> &mut Self { + self.environs.insert(key.to_string(), value.to_string()); + self + } + + pub fn envs(&mut self, envs: HashMap) -> &mut Self { + for (k, v) in envs { + self.environs.insert(k.to_string(), v.to_string()); + } + self + } + + pub fn inherit_parent_env(&mut self, inherit: bool) -> &mut Self { + self.inherit_parent_env = inherit; + self + } + + pub fn build_with(mut self) -> error::Result + where + O: OpenPty, + { + if !self.environs.is_empty() && self.inherit_parent_env { + for (k, v) in std::env::vars() { + if !self.environs.contains_key(&k) { + self.env(k, v); + } + } + } + + let pty = Pty::open( + self.program, + self.work_dir, + Some(self.arguments), + Some(self.environs), + )?; + + #[cfg(windows)] + { + let input = format!(r"\\.\\pipe\\{}", pty.pipe_input_name.to_str()?); + let output = format!(r"\\.\\pipe\\{}", pty.pipe_output_name.to_str()?); + + let input = O::connect_input(input)?; + let output = O::connect_output(output)?; + + Ok(OpenedPty { + pty: Arc::new(Mutex::new(pty)), + input: Arc::new(Mutex::new(input)), + output: Arc::new(Mutex::new(output)), + }) + } + } +} + +impl Clone for OpenedPty { + fn clone(&self) -> Self { + Self { + pty: self.pty.clone(), + input: self.input.clone(), + output: self.output.clone(), + } + } +} + +unsafe impl Sync for OpenedPty {} +unsafe impl Send for OpenedPty {} + +impl PtyInput for T where T: AsyncWrite + Unpin {} +impl PtyOutput for T where T: AsyncRead + Unpin {} + +impl AsyncRead for OpenedPty { fn poll_read( self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, buf: &mut [u8], ) -> std::task::Poll> { - unimplemented!() + let mut pty = self.pty.lock(); + match std::future::Future::poll(Pin::new(&mut pty.output_signal), cx)? { + Poll::Ready(()) => Poll::Ready(Ok(0)), + Poll::Pending => { + let mut output = self.output.lock(); + Pin::new(&mut *output.0).poll_read(cx, buf) + } + } } } -impl AsyncWrite for Pty { +impl AsyncWrite for OpenedPty { fn poll_write( self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, buf: &[u8], ) -> std::task::Poll> { - unimplemented!() + let mut pty = self.pty.lock(); + match std::future::Future::poll(Pin::new(&mut pty.input_signal), cx)? { + Poll::Ready(()) => Poll::Ready(Ok(0)), + Poll::Pending => { + let mut input = self.input.lock(); + Pin::new(&mut *input.0).poll_write(cx, buf) + } + } } fn poll_flush( self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - unimplemented!() + let mut pty = self.pty.lock(); + match std::future::Future::poll(Pin::new(&mut pty.input_signal), cx)? { + Poll::Ready(()) => Poll::Ready(Ok(())), + Poll::Pending => { + let mut input = self.input.lock(); + Pin::new(&mut *input.0).poll_flush(cx) + } + } } } #[cfg(test)] mod tests { - use std::os::fd::FromRawFd; - use mio::{unix::SourceFd, Events, Interest, Token}; - use tokio::io::AsyncReadExt; - - use super::Pty; + use crate::{ + core::{io, split::SplitStream}, + pty::c::windows_ext::ConsoleExt, + }; #[tokio::test] + #[cfg(windows)] + #[cfg(feature = "fuso-rt-tokio")] async fn test_pty() { - let pty = Pty::open("/bin/sh".to_owned(), None, None, None).unwrap(); - - let poll = mio::Poll::new().unwrap(); + let (output, input) = super::builder("C:\\windows\\system32\\cmd.exe") + .build() + .expect("Failed to open pty") + .split(); - poll.registry(); + tokio::io::stdin().enable_input_mode().unwrap(); - println!("{pty:?} {:?}", pty.argv) + tokio::select! { + _ = io::copy(input, tokio::io::stdin()) => {} + _ = io::copy(tokio::io::stdout(), output) => {} + }; } } From afdfeb31fede6d4d514e0b1bc6287f13e2b09920 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Sun, 12 May 2024 15:16:22 +0800 Subject: [PATCH 22/29] =?UTF-8?q?=E6=B7=BB=E5=8A=A0linux=E5=B9=B3=E5=8F=B0?= =?UTF-8?q?pty=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/io/mod.rs | 6 +- src/runtime/tokio/pty.rs | 101 ++++++++++++++++++++++++++++ src/toy/pty/c/pty_impl.c | 6 +- src/toy/pty/mod.rs | 142 ++++++++++++++++++++++++++++++++------- 4 files changed, 221 insertions(+), 34 deletions(-) diff --git a/src/core/io/mod.rs b/src/core/io/mod.rs index 3ce36c5..5a0f4c6 100644 --- a/src/core/io/mod.rs +++ b/src/core/io/mod.rs @@ -4,11 +4,7 @@ use std::{ task::{Context, Poll}, }; -use crate::{error, select}; - -use super::{BoxedFuture, Stream}; - -use crate::core::split::SplitStream; +use crate::error; pub trait AsyncRead { fn poll_read( diff --git a/src/runtime/tokio/pty.rs b/src/runtime/tokio/pty.rs index 4bc89ff..ae038ed 100644 --- a/src/runtime/tokio/pty.rs +++ b/src/runtime/tokio/pty.rs @@ -102,3 +102,104 @@ mod windows { } } } + +#[cfg(not(windows))] +mod unix { + use std::{sync::Arc, task::Poll}; + + use crate::{ + core::{ + io::{AsyncRead, AsyncWrite}, + split::SplitStream, + }, + error, + pty::{self, OpenPty, OpenedPty, PtyBuilder}, + }; + + extern "C" { + fn __errno_location() -> *mut i32; + } + + #[derive(Clone)] + struct Pty(Arc>); + + struct WithTokio; + + impl PtyBuilder { + pub fn build(self) -> error::Result { + self.build_with::() + } + } + + impl OpenPty for WithTokio { + fn pipe(input: i32) -> error::Result<(pty::AbstractPtyInput, pty::AbstractPtyOutput)> { + let pty = Pty(Arc::new(tokio::io::unix::AsyncFd::new(input)?)); + let (output, input) = pty.split(); + Ok(( + pty::AbstractPtyInput::new(input), + pty::AbstractPtyOutput::new(output), + )) + } + } + + impl AsyncRead for Pty { + fn poll_read( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + extern "C" { + fn read(fd: i32, buf: *mut u8, count: usize) -> isize; + } + + match self.0.poll_read_ready(cx)? { + std::task::Poll::Pending => Poll::Pending, + std::task::Poll::Ready(mut ar) => unsafe { + let n = read(*ar.get_inner(), buf.as_ptr() as _, buf.len()); + ar.clear_ready(); + if n < 0 { + Poll::Ready(Err( + std::io::Error::from_raw_os_error(*__errno_location()).into() + )) + } else { + Poll::Ready(Ok(n as usize)) + } + }, + } + } + } + + impl AsyncWrite for Pty { + fn poll_write( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + extern "C" { + fn write(fd: i32, buf: *const u8, count: usize) -> isize; + } + + match self.0.poll_write_ready(cx)? { + std::task::Poll::Pending => Poll::Pending, + std::task::Poll::Ready(mut ar) => unsafe { + let n = write(*ar.get_inner(), buf.as_ptr(), buf.len()); + ar.clear_ready(); + if n < 0 { + Poll::Ready(Err( + std::io::Error::from_raw_os_error(*__errno_location()).into() + )) + } else { + Poll::Ready(Ok(n as usize)) + } + }, + } + } + + fn poll_flush( + self: std::pin::Pin<&mut Self>, + _: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + Poll::Ready(Ok(())) + } + } +} diff --git a/src/toy/pty/c/pty_impl.c b/src/toy/pty/c/pty_impl.c index 0ebddee..a780061 100644 --- a/src/toy/pty/c/pty_impl.c +++ b/src/toy/pty/c/pty_impl.c @@ -259,7 +259,7 @@ int conpty_init() { #else -void enter_pty_main(const int argc, char *const *argv, const char **envp); +void enter_pty_main(char *const *argv, const char **envp); int pty_spawn(pty_process *process) { int pid; @@ -274,7 +274,7 @@ int pty_spawn(pty_process *process) { if (pid < 0) { return -errno; } else if (pid == 0) { - enter_pty_main(process->argc, process->argv, process->envp); + enter_pty_main(process->argv, process->environs); } if ((flags = fcntl(master, F_GETFL)) == -1) { @@ -318,7 +318,7 @@ void pty_exit(pty_process *process) { process->pid = -1; } -void enter_pty_main(const int argc, char *const *argv, const char **envp) { +void enter_pty_main(char *const *argv, const char **envp) { int ret = execvp(argv[0], argv); if (ret < 0) { _exit(-errno); diff --git a/src/toy/pty/mod.rs b/src/toy/pty/mod.rs index 449bb68..fe717da 100644 --- a/src/toy/pty/mod.rs +++ b/src/toy/pty/mod.rs @@ -1,18 +1,20 @@ mod c; -use std::{collections::HashMap, ffi::CString, pin::Pin, sync::Arc, task::Poll}; +use std::{collections::HashMap, ffi::CString, pin::Pin, sync::Arc}; use c::bindings; #[cfg(windows)] pub use c::windows_ext::*; +#[cfg(windows)] +use std::task::Poll; +#[cfg(windows)] +use crate::task::{setter, Getter, Setter}; + use parking_lot::Mutex; use crate::{ - core::{ - io::{AsyncRead, AsyncWrite}, - task::{setter, Getter, Setter}, - }, + core::io::{AsyncRead, AsyncWrite}, error, }; @@ -62,6 +64,7 @@ pub struct Pty { cmdline: CString, #[cfg(windows)] input_signal: Getter<()>, + #[cfg(windows)] output_signal: Getter<()>, } @@ -94,6 +97,11 @@ pub trait OpenPty { fn connect_output(output: String) -> error::Result; } +#[cfg(not(windows))] +pub trait OpenPty { + fn pipe(input: i32) -> error::Result<(AbstractPtyInput, AbstractPtyOutput)>; +} + impl Pty { fn open( prog: String, @@ -110,7 +118,7 @@ impl Pty { }; #[cfg(not(windows))] - let mut argv = { + let argv = { let mut argv = Vec::::new(); argv.push(CString::new(prog)?); @@ -159,7 +167,10 @@ impl Pty { )? }; + #[cfg(windows)] let pipe_input_name = CString::new(format!("pty-fuso-input-{}", std::process::id()))?; + + #[cfg(windows)] let pipe_output_name = CString::new(format!("pty-fuso-output-{}", std::process::id()))?; let proc = Process { @@ -198,44 +209,51 @@ impl Pty { }, #[cfg(windows)] cmdline: cmdline.as_ptr() as _, + #[cfg(windows)] pipe_input_name: pipe_input_name.as_ptr() as _, + #[cfg(windows)] pipe_output_name: pipe_output_name.as_ptr() as _, }; #[cfg(not(windows))] { pty.argv = proc.args.as_ptr() as _; - pty.envp = proc.envp.as_ptr() as _; + pty.environs = proc.environs.as_ptr() as _; + } + + #[cfg(not(windows))] + { + pty.work = proc.work_dir as _; } #[cfg(windows)] { + pty.work = proc.work_dir as _; pty.cmdline = proc.cmdline as _; if proc.environs.is_empty() { pty.environs = std::ptr::null(); } else { pty.environs = proc.environs.as_ptr() as _; } - } - - pty.work_dir = proc.work_dir as _; - pty.pipe_input_name = proc.pipe_input_name; - pty.pipe_output_name = proc.pipe_output_name; - fn pty_process_exit_cb(data: *mut std::os::raw::c_void) { - unsafe { - drop(Box::<(Setter<()>, Setter<()>)>::from_raw( - data as *mut (Setter<()>, Setter<()>), - )); + pty.pipe_input_name = proc.pipe_input_name; + pty.pipe_output_name = proc.pipe_output_name; + fn pty_process_exit_cb(data: *mut std::os::raw::c_void) { + unsafe { + drop(Box::<(Setter<()>, Setter<()>)>::from_raw( + data as *mut (Setter<()>, Setter<()>), + )); + } } - } - pty.exit_cb = pty_process_exit_cb as _; + pty.exit_cb = pty_process_exit_cb as _; - let (set_input_signal, input_signal) = setter::<()>(); - let (set_output_signal, output_signal) = setter::<()>(); + let (set_input_signal, input_signal) = setter::<()>(); + let (set_output_signal, output_signal) = setter::<()>(); - pty.exit_cb_data = Box::into_raw(Box::new((set_input_signal, set_output_signal))) as _; + pty.exit_cb_data = + Box::into_raw(Box::new((set_input_signal, set_output_signal))) as _; + } let error = bindings::pty_spawn(&mut pty as *mut bindings::PtyProcess); @@ -248,14 +266,16 @@ impl Pty { work, #[cfg(not(windows))] argv, - #[cfg(not(windows))] - envp, #[cfg(windows)] cmdline, environs, + #[cfg(windows)] pipe_input_name, + #[cfg(windows)] pipe_output_name, + #[cfg(windows)] input_signal, + #[cfg(windows)] output_signal, }) } @@ -365,6 +385,17 @@ impl PtyBuilder { output: Arc::new(Mutex::new(output)), }) } + + #[cfg(not(windows))] + { + let (input, output) = O::pipe(pty.pty.pty)?; + + Ok(OpenedPty { + pty: Arc::new(Mutex::new(pty)), + input: Arc::new(Mutex::new(input)), + output: Arc::new(Mutex::new(output)), + }) + } } } @@ -384,6 +415,7 @@ unsafe impl Send for OpenedPty {} impl PtyInput for T where T: AsyncWrite + Unpin {} impl PtyOutput for T where T: AsyncRead + Unpin {} +#[cfg(windows)] impl AsyncRead for OpenedPty { fn poll_read( self: std::pin::Pin<&mut Self>, @@ -401,6 +433,7 @@ impl AsyncRead for OpenedPty { } } +#[cfg(windows)] impl AsyncWrite for OpenedPty { fn poll_write( self: std::pin::Pin<&mut Self>, @@ -432,7 +465,41 @@ impl AsyncWrite for OpenedPty { } } +#[cfg(not(windows))] +impl AsyncRead for OpenedPty { + fn poll_read( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + let mut output = self.output.lock(); + Pin::new(&mut *output.0).poll_read(cx, buf) + } +} + +#[cfg(not(windows))] +impl AsyncWrite for OpenedPty { + fn poll_write( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + let mut input = self.input.lock(); + Pin::new(&mut *input.0).poll_write(cx, buf) + } + + fn poll_flush( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let mut input = self.input.lock(); + Pin::new(&mut *input.0).poll_flush(cx) + } +} + #[cfg(test)] +#[cfg(windows)] +#[cfg(feature = "fuso-rt-tokio")] mod tests { use crate::{ @@ -441,8 +508,6 @@ mod tests { }; #[tokio::test] - #[cfg(windows)] - #[cfg(feature = "fuso-rt-tokio")] async fn test_pty() { let (output, input) = super::builder("C:\\windows\\system32\\cmd.exe") .build() @@ -457,3 +522,28 @@ mod tests { }; } } + +#[cfg(test)] +#[cfg(not(windows))] +#[cfg(feature = "fuso-rt-tokio")] +mod tests { + + use crate::core::{io, split::SplitStream}; + + #[tokio::test] + async fn test_pty() { + let (output, input) = super::builder("/bin/bash") + .build() + .expect("Failed to open pty") + .split(); + + tokio::select! { + e = io::copy(input, tokio::io::stdin()) => { + println!("{e:?}") + } + e = io::copy(tokio::io::stdout(), output) => { + println!("{e:?}") + } + }; + } +} From 0ebad65809456539b6ae92ca4b7a0b458592b969 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Sun, 12 May 2024 15:19:39 +0800 Subject: [PATCH 23/29] Fix: syntax error --- src/toy/pty/mod.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/toy/pty/mod.rs b/src/toy/pty/mod.rs index fe717da..51dc2a2 100644 --- a/src/toy/pty/mod.rs +++ b/src/toy/pty/mod.rs @@ -8,8 +8,9 @@ use c::bindings; pub use c::windows_ext::*; #[cfg(windows)] use std::task::Poll; + #[cfg(windows)] -use crate::task::{setter, Getter, Setter}; +use crate::core::task::{setter, Getter, Setter}; use parking_lot::Mutex; @@ -228,7 +229,7 @@ impl Pty { #[cfg(windows)] { - pty.work = proc.work_dir as _; + pty.work_dir = proc.work_dir as _; pty.cmdline = proc.cmdline as _; if proc.environs.is_empty() { pty.environs = std::ptr::null(); @@ -247,10 +248,16 @@ impl Pty { } pty.exit_cb = pty_process_exit_cb as _; + } + + #[cfg(windows)] + let (set_input_signal, input_signal) = setter::<()>(); - let (set_input_signal, input_signal) = setter::<()>(); - let (set_output_signal, output_signal) = setter::<()>(); + #[cfg(windows)] + let (set_output_signal, output_signal) = setter::<()>(); + #[cfg(windows)] + { pty.exit_cb_data = Box::into_raw(Box::new((set_input_signal, set_output_signal))) as _; } From a2b41aac4782f14d2b15923f6e4abf1abaab1348 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Sun, 12 May 2024 23:09:58 +0800 Subject: [PATCH 24/29] fix: pty --- Cargo.lock | 135 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 16 +++-- src/bin/client.rs | 11 +++- src/bin/main.rs | 19 +----- src/toy/pty/c/pty_impl.c | 17 ++++- src/toy/pty/mod.rs | 15 +++-- 6 files changed, 181 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc89475..f233d95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,6 +85,19 @@ dependencies = [ "syn 2.0.50", ] +[[package]] +name = "async-tungstenite" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cca750b12e02c389c1694d35c16539f88b8bbaa5945934fdc1b41a776688589" +dependencies = [ + "futures-io", + "futures-util", + "log", + "pin-project-lite", + "tungstenite", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -167,6 +180,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[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" @@ -267,6 +289,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -277,6 +308,22 @@ dependencies = [ "typenum", ] +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[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 = "env_filter" version = "0.1.0" @@ -325,6 +372,7 @@ dependencies = [ name = "fuso" version = "1.0.5-beta" dependencies = [ + "async-tungstenite", "axum", "cc", "clap", @@ -597,6 +645,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indexmap" version = "2.2.3" @@ -1019,6 +1077,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -1107,6 +1176,21 @@ dependencies = [ "syn 2.0.50", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.36.0" @@ -1251,18 +1335,69 @@ dependencies = [ "wintun", ] +[[package]] +name = "tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "url", + "utf-8", +] + [[package]] name = "typenum" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8parse" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index a986db8..490cdc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,15 +37,22 @@ codegen-units = 1 panic = 'abort' [features] -default = ["fuso-config", "fuso-cli", "fuso-kcp", "fuso-rt-tokio", "fuso-manager", "fuso-socks5"] +default = [ + "fuso-config", + "fuso-cli", + "fuso-kcp", + "fuso-rt-tokio", + "fuso-manager", + "fuso-socks5", +] fuso-kcp = ["kcp-rust"] fuso-cli = ["clap"] fuso-serde = ["serde"] -fuso-socks5 = [ "dep:fuso-socks", "dep:futures" ] +fuso-socks5 = ["dep:fuso-socks", "dep:futures"] fuso-config = ["toml", "serde"] fuso-runtime = [] fuso-rt-tokio = ["dep:tokio", "fuso-runtime"] -fuso-manager = [ "dep:axum", "fuso-rt-tokio"] +fuso-manager = ["dep:axum", "async-tungstenite", "fuso-rt-tokio"] [build-dependencies] @@ -61,8 +68,9 @@ parking_lot = "0.12.1" rmp-serde = "1.3.0" rand = "0.8.5" tun = { version = "0.6.1", features = ["async"] } -futures = { version = "0.3.30", optional = true} +futures = { version = "0.3.30", optional = true } mio = { version = "0.8.11", features = ["os-poll"] } +async-tungstenite = { optional = true, version = "0.25.1" } [dependencies.kcp-rust] optional = true diff --git a/src/bin/client.rs b/src/bin/client.rs index eec6e05..ba3983f 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -218,10 +218,15 @@ async fn enter_forward_service_main( match target { FinalTarget::Udp { addr, port } => {} FinalTarget::Shell { path, args } => { - let mut builder = fuso::pty::builder(""); + let mut builder = fuso::pty::builder(path); + + builder.args(&args); - - + let pty = builder.build().unwrap(); + + if let Ok(a) = linker.link(Protocol::Tcp).await { + a.transfer(pty).await.unwrap(); + }; } FinalTarget::Dynamic => { let transmitter = linker.link(Protocol::Tcp).await.unwrap(); diff --git a/src/bin/main.rs b/src/bin/main.rs index e46e978..da250c6 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -4,22 +4,5 @@ use tokio::io::AsyncReadExt; #[tokio::main] async fn main() { - let mut c = tun::Configuration::default(); - - c.address((10, 0, 0, 0)).netmask((255, 255, 255, 0)).up(); - - #[cfg(target_os = "linux")] - c.platform(|config| { - config.packet_information(true); - }); - - let mut dev = tun::create_as_async(&c).unwrap(); - - let mut buf = [0; 4096]; - - loop { - let amount = dev.read(&mut buf).await.unwrap(); - - println!("{:?}", &buf[0..amount]); - } + } diff --git a/src/toy/pty/c/pty_impl.c b/src/toy/pty/c/pty_impl.c index a780061..564fb97 100644 --- a/src/toy/pty/c/pty_impl.c +++ b/src/toy/pty/c/pty_impl.c @@ -6,6 +6,7 @@ #include #else +#include #include #include #include @@ -259,7 +260,7 @@ int conpty_init() { #else -void enter_pty_main(char *const *argv, const char **envp); +void enter_pty_main(const char *cwd, char *const *argv, const char **envp); int pty_spawn(pty_process *process) { int pid; @@ -274,7 +275,7 @@ int pty_spawn(pty_process *process) { if (pid < 0) { return -errno; } else if (pid == 0) { - enter_pty_main(process->argv, process->environs); + enter_pty_main(process->work, process->argv, process->environs); } if ((flags = fcntl(master, F_GETFL)) == -1) { @@ -318,7 +319,17 @@ void pty_exit(pty_process *process) { process->pid = -1; } -void enter_pty_main(char *const *argv, const char **envp) { +void enter_pty_main(const char *cwd, char *const *argv, const char **envp) { + + if (cwd != NULL) + chdir(cwd); + + if (envp != NULL) { + clearenv(); + const char **p = envp; + for (; *p; p++) putenv((char *)*p); + } + int ret = execvp(argv[0], argv); if (ret < 0) { _exit(-errno); diff --git a/src/toy/pty/mod.rs b/src/toy/pty/mod.rs index 51dc2a2..c868f99 100644 --- a/src/toy/pty/mod.rs +++ b/src/toy/pty/mod.rs @@ -4,6 +4,8 @@ use std::{collections::HashMap, ffi::CString, pin::Pin, sync::Arc}; use c::bindings; +#[cfg(windows)] +use crate::task::{setter, Getter, Setter}; #[cfg(windows)] pub use c::windows_ext::*; #[cfg(windows)] @@ -539,10 +541,15 @@ mod tests { #[tokio::test] async fn test_pty() { - let (output, input) = super::builder("/bin/bash") - .build() - .expect("Failed to open pty") - .split(); + let mut builder = super::builder("/bin/bash"); + + builder + .work_dir("/") + .inherit_parent_env(true) + .env("aa", "1") + .env("C", "C"); + + let (output, input) = builder.build().expect("Failed to open pty").split(); tokio::select! { e = io::copy(input, tokio::io::stdin()) => { From 008f6a472c70d88844b79f513240bcf91ffb504f Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Sun, 12 May 2024 23:10:21 +0800 Subject: [PATCH 25/29] pty web --- assets/html/.gitignore | 16 + assets/html/README.md | 33 + assets/html/dist/index.html | 11 + assets/html/package-lock.json | 1597 +++++++++++++++++++++++++++++++++ assets/html/package.json | 23 + assets/html/src/index.js | 16 + assets/html/src/pty.css | 0 assets/html/webpack.config.js | 17 + 8 files changed, 1713 insertions(+) create mode 100644 assets/html/.gitignore create mode 100644 assets/html/README.md create mode 100644 assets/html/dist/index.html create mode 100644 assets/html/package-lock.json create mode 100644 assets/html/package.json create mode 100644 assets/html/src/index.js create mode 100644 assets/html/src/pty.css create mode 100644 assets/html/webpack.config.js diff --git a/assets/html/.gitignore b/assets/html/.gitignore new file mode 100644 index 0000000..6f9bd11 --- /dev/null +++ b/assets/html/.gitignore @@ -0,0 +1,16 @@ + +.cache/ +coverage/ +dist/* +!dist/index.html +node_modules/ +*.log + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db diff --git a/assets/html/README.md b/assets/html/README.md new file mode 100644 index 0000000..9c96292 --- /dev/null +++ b/assets/html/README.md @@ -0,0 +1,33 @@ +# empty-project + +Empty project. + +## Building and running on localhost + +First install dependencies: + +```sh +npm install +``` + +To create a production build: + +```sh +npm run build-prod +``` + +To create a development build: + +```sh +npm run build-dev +``` + +## Running + +```sh +node dist/bundle.js +``` + +## Credits + +Made with [createapp.dev](https://createapp.dev/) diff --git a/assets/html/dist/index.html b/assets/html/dist/index.html new file mode 100644 index 0000000..2fd8efa --- /dev/null +++ b/assets/html/dist/index.html @@ -0,0 +1,11 @@ + + + + Fuso - Pty + + + +

+ + + \ No newline at end of file diff --git a/assets/html/package-lock.json b/assets/html/package-lock.json new file mode 100644 index 0000000..566e21b --- /dev/null +++ b/assets/html/package-lock.json @@ -0,0 +1,1597 @@ +{ + "name": "empty-project", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "empty-project", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@xterm/xterm": "^5.5.0" + }, + "devDependencies": { + "css-loader": "^7.1.1", + "style-loader": "^4.0.0", + "webpack": "^5.91.0", + "webpack-cli": "^5.1.4" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@types/eslint": { + "version": "8.56.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", + "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.12.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz", + "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xterm/xterm": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", + "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==" + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001617", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001617.tgz", + "integrity": "sha512-mLyjzNI9I+Pix8zwcrpxEbGlfqOkF9kM3ptzmKNw5tizSyYwMe+nGLTqMK9cO+0E+Bh6TsBxNAaHWEM8xwSsmA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-loader": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.1.tgz", + "integrity": "sha512-OxIR5P2mjO1PSXk44bWuQ8XtMK4dpEqpIyERCx3ewOo3I8EmbcxMPUc5ScLtQfgXtOojoMv57So4V/C02HQLsw==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.763", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.763.tgz", + "integrity": "sha512-k4J8NrtJ9QrvHLRo8Q18OncqBCB7tIUyqxRcJnlonQ0ioHKYB988GcDFF3ZePmnb8eHEopDs/wPHR/iGAFgoUQ==", + "dev": true + }, + "node_modules/enhanced-resolve": { + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", + "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/envinfo": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz", + "integrity": "sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.2.tgz", + "integrity": "sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", + "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", + "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", + "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/style-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz", + "integrity": "sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==", + "dev": true, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.27.0" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz", + "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/update-browserslist-db": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.15.tgz", + "integrity": "sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/watchpack": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.91.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", + "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.16.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", + "colorette": "^2.0.14", + "commander": "^10.0.1", + "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true + } + } +} diff --git a/assets/html/package.json b/assets/html/package.json new file mode 100644 index 0000000..1656ced --- /dev/null +++ b/assets/html/package.json @@ -0,0 +1,23 @@ +{ + "name": "empty-project", + "version": "1.0.0", + "description": "", + "main": "index.js", + "keywords": [], + "author": "", + "license": "ISC", + "scripts": { + "clean": "rm dist/bundle.js", + "build-dev": "webpack --mode development", + "build-prod": "webpack --mode production" + }, + "dependencies": { + "@xterm/xterm": "^5.5.0" + }, + "devDependencies": { + "css-loader": "^7.1.1", + "style-loader": "^4.0.0", + "webpack": "^5.91.0", + "webpack-cli": "^5.1.4" + } +} diff --git a/assets/html/src/index.js b/assets/html/src/index.js new file mode 100644 index 0000000..5268567 --- /dev/null +++ b/assets/html/src/index.js @@ -0,0 +1,16 @@ +import './pty.css' +import '@xterm/xterm/css/xterm.css' + + +let { Terminal } = require('@xterm/xterm') + +let term = new Terminal(); + + +console.log(document.getElementById("terminal")) + +term.open(document.getElementById('terminal')) + +term.onData(() => { + +}) \ No newline at end of file diff --git a/assets/html/src/pty.css b/assets/html/src/pty.css new file mode 100644 index 0000000..e69de29 diff --git a/assets/html/webpack.config.js b/assets/html/webpack.config.js new file mode 100644 index 0000000..d4d2e1f --- /dev/null +++ b/assets/html/webpack.config.js @@ -0,0 +1,17 @@ +const webpack = require('webpack'); +const path = require('path'); + +const config = { + entry: './src/index.js', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'pty.js' + }, + module: { + rules: [ + { test: /\.css$/, use: [ 'style-loader', 'css-loader'] }, + ], + }, +}; + +module.exports = config; \ No newline at end of file From 69314a573e01bb7c18f11c6ac8186e2851fce4ac Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Sun, 12 May 2024 23:21:36 +0800 Subject: [PATCH 26/29] fix pty --- src/toy/pty/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/toy/pty/mod.rs b/src/toy/pty/mod.rs index c868f99..07899b6 100644 --- a/src/toy/pty/mod.rs +++ b/src/toy/pty/mod.rs @@ -4,8 +4,6 @@ use std::{collections::HashMap, ffi::CString, pin::Pin, sync::Arc}; use c::bindings; -#[cfg(windows)] -use crate::task::{setter, Getter, Setter}; #[cfg(windows)] pub use c::windows_ext::*; #[cfg(windows)] From 5f2f287f131100cdb5b8945e21a3275819dcf588 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Sat, 18 May 2024 16:36:12 +0800 Subject: [PATCH 27/29] rm assets --- assets/html/.gitignore | 16 - assets/html/README.md | 33 - assets/html/dist/index.html | 11 - assets/html/package-lock.json | 1597 --------------------------------- assets/html/package.json | 23 - assets/html/src/index.js | 16 - assets/html/src/pty.css | 0 assets/html/webpack.config.js | 17 - 8 files changed, 1713 deletions(-) delete mode 100644 assets/html/.gitignore delete mode 100644 assets/html/README.md delete mode 100644 assets/html/dist/index.html delete mode 100644 assets/html/package-lock.json delete mode 100644 assets/html/package.json delete mode 100644 assets/html/src/index.js delete mode 100644 assets/html/src/pty.css delete mode 100644 assets/html/webpack.config.js diff --git a/assets/html/.gitignore b/assets/html/.gitignore deleted file mode 100644 index 6f9bd11..0000000 --- a/assets/html/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ - -.cache/ -coverage/ -dist/* -!dist/index.html -node_modules/ -*.log - -# OS generated files -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db -Thumbs.db diff --git a/assets/html/README.md b/assets/html/README.md deleted file mode 100644 index 9c96292..0000000 --- a/assets/html/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# empty-project - -Empty project. - -## Building and running on localhost - -First install dependencies: - -```sh -npm install -``` - -To create a production build: - -```sh -npm run build-prod -``` - -To create a development build: - -```sh -npm run build-dev -``` - -## Running - -```sh -node dist/bundle.js -``` - -## Credits - -Made with [createapp.dev](https://createapp.dev/) diff --git a/assets/html/dist/index.html b/assets/html/dist/index.html deleted file mode 100644 index 2fd8efa..0000000 --- a/assets/html/dist/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - Fuso - Pty - - - -
- - - \ No newline at end of file diff --git a/assets/html/package-lock.json b/assets/html/package-lock.json deleted file mode 100644 index 566e21b..0000000 --- a/assets/html/package-lock.json +++ /dev/null @@ -1,1597 +0,0 @@ -{ - "name": "empty-project", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "empty-project", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "@xterm/xterm": "^5.5.0" - }, - "devDependencies": { - "css-loader": "^7.1.1", - "style-loader": "^4.0.0", - "webpack": "^5.91.0", - "webpack-cli": "^5.1.4" - } - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@types/eslint": { - "version": "8.56.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", - "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.12.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz", - "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", - "dev": true, - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", - "dev": true, - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", - "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", - "dev": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", - "dev": true, - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "dev": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", - "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", - "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", - "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", - "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", - "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webpack-cli/configtest": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", - "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", - "dev": true, - "engines": { - "node": ">=14.15.0" - }, - "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" - } - }, - "node_modules/@webpack-cli/info": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", - "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", - "dev": true, - "engines": { - "node": ">=14.15.0" - }, - "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" - } - }, - "node_modules/@webpack-cli/serve": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", - "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", - "dev": true, - "engines": { - "node": ">=14.15.0" - }, - "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" - }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/@xterm/xterm": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", - "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==" - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "dev": true, - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001617", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001617.tgz", - "integrity": "sha512-mLyjzNI9I+Pix8zwcrpxEbGlfqOkF9kM3ptzmKNw5tizSyYwMe+nGLTqMK9cO+0E+Bh6TsBxNAaHWEM8xwSsmA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-loader": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.1.tgz", - "integrity": "sha512-OxIR5P2mjO1PSXk44bWuQ8XtMK4dpEqpIyERCx3ewOo3I8EmbcxMPUc5ScLtQfgXtOojoMv57So4V/C02HQLsw==", - "dev": true, - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.1.0", - "postcss-modules-local-by-default": "^4.0.5", - "postcss-modules-scope": "^3.2.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.27.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.763", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.763.tgz", - "integrity": "sha512-k4J8NrtJ9QrvHLRo8Q18OncqBCB7tIUyqxRcJnlonQ0ioHKYB988GcDFF3ZePmnb8eHEopDs/wPHR/iGAFgoUQ==", - "dev": true - }, - "node_modules/enhanced-resolve": { - "version": "5.16.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", - "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/envinfo": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz", - "integrity": "sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==", - "dev": true, - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.2.tgz", - "integrity": "sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA==", - "dev": true - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "dev": true, - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/interpret": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", - "dev": true, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", - "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", - "dev": true, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", - "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", - "dev": true, - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", - "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dev": true, - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.16", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", - "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", - "dev": true, - "dependencies": { - "resolve": "^1.20.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/style-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz", - "integrity": "sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==", - "dev": true, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.27.0" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/terser": { - "version": "5.31.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz", - "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==", - "dev": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/update-browserslist-db": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.15.tgz", - "integrity": "sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "node_modules/watchpack": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", - "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", - "dev": true, - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack": { - "version": "5.91.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", - "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", - "dev": true, - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.21.10", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.16.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-cli": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", - "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", - "dev": true, - "dependencies": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^2.1.1", - "@webpack-cli/info": "^2.0.2", - "@webpack-cli/serve": "^2.0.5", - "colorette": "^2.0.14", - "commander": "^10.0.1", - "cross-spawn": "^7.0.3", - "envinfo": "^7.7.3", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^3.1.1", - "rechoir": "^0.8.0", - "webpack-merge": "^5.7.3" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "5.x.x" - }, - "peerDependenciesMeta": { - "@webpack-cli/generators": { - "optional": true - }, - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/webpack-merge": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", - "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", - "dev": true, - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true - } - } -} diff --git a/assets/html/package.json b/assets/html/package.json deleted file mode 100644 index 1656ced..0000000 --- a/assets/html/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "empty-project", - "version": "1.0.0", - "description": "", - "main": "index.js", - "keywords": [], - "author": "", - "license": "ISC", - "scripts": { - "clean": "rm dist/bundle.js", - "build-dev": "webpack --mode development", - "build-prod": "webpack --mode production" - }, - "dependencies": { - "@xterm/xterm": "^5.5.0" - }, - "devDependencies": { - "css-loader": "^7.1.1", - "style-loader": "^4.0.0", - "webpack": "^5.91.0", - "webpack-cli": "^5.1.4" - } -} diff --git a/assets/html/src/index.js b/assets/html/src/index.js deleted file mode 100644 index 5268567..0000000 --- a/assets/html/src/index.js +++ /dev/null @@ -1,16 +0,0 @@ -import './pty.css' -import '@xterm/xterm/css/xterm.css' - - -let { Terminal } = require('@xterm/xterm') - -let term = new Terminal(); - - -console.log(document.getElementById("terminal")) - -term.open(document.getElementById('terminal')) - -term.onData(() => { - -}) \ No newline at end of file diff --git a/assets/html/src/pty.css b/assets/html/src/pty.css deleted file mode 100644 index e69de29..0000000 diff --git a/assets/html/webpack.config.js b/assets/html/webpack.config.js deleted file mode 100644 index d4d2e1f..0000000 --- a/assets/html/webpack.config.js +++ /dev/null @@ -1,17 +0,0 @@ -const webpack = require('webpack'); -const path = require('path'); - -const config = { - entry: './src/index.js', - output: { - path: path.resolve(__dirname, 'dist'), - filename: 'pty.js' - }, - module: { - rules: [ - { test: /\.css$/, use: [ 'style-loader', 'css-loader'] }, - ], - }, -}; - -module.exports = config; \ No newline at end of file From f48b4b9407707f715ac6f8d0a5aef4602ad88090 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Sat, 18 May 2024 16:36:32 +0800 Subject: [PATCH 28/29] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=AB=AF=E5=8F=A3?= =?UTF-8?q?=E8=BD=AC=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 2 +- Cargo.toml | 2 +- build.rs | 1 - config/client.toml | 4 +- config/server.toml | 16 +- src/bin/client.rs | 208 ++++++++++++++------------ src/bin/server.rs | 8 +- src/client/port_forward/connector.rs | 59 ++++++-- src/client/port_forward/mod.rs | 7 +- src/config/mod.rs | 2 +- src/core/connector.rs | 73 ++++++++- src/core/net/kcp.rs | 33 ++++ src/core/net/udp.rs | 4 + src/core/protocol.rs | 4 +- src/core/transfer.rs | 2 +- src/error/mod.rs | 8 + src/runtime/tokio/kcp.rs | 32 +++- src/runtime/tokio/port_forward/mod.rs | 7 +- src/runtime/tokio/tcp.rs | 6 + 19 files changed, 333 insertions(+), 145 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f233d95..f941eb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -689,7 +689,7 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "kcp-rust" version = "0.1.2" -source = "git+https://github.com/editso/kcp-rust#e7cd0c2d74652185e1c31ea6d4e5201ee71b9b89" +source = "git+https://github.com/editso/kcp-rust?branch=master#10485ea9ec1dfe5eb930c7fd5116339aea1183e5" dependencies = [ "cc", "log", diff --git a/Cargo.toml b/Cargo.toml index 490cdc5..7251485 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,7 +74,7 @@ async-tungstenite = { optional = true, version = "0.25.1" } [dependencies.kcp-rust] optional = true -version = "*" +branch = "master" git = "https://github.com/editso/kcp-rust" default-features = false diff --git a/build.rs b/build.rs index 2b4f55d..6003257 100644 --- a/build.rs +++ b/build.rs @@ -9,7 +9,6 @@ fn main() { fn compile_tty_and_generate_bindings() { let manifest_root = std::env::var("CARGO_MANIFEST_DIR").unwrap(); let tty_source_dir = format!("{manifest_root}/src/toy/pty/c"); - let tty_bindings_out_dir = format!("{}/pty_bindings.rs", std::env::var("OUT_DIR").unwrap()); println!("cargo:rerun-if-changed={tty_source_dir}"); diff --git a/config/client.toml b/config/client.toml index d037b9a..c26a50f 100644 --- a/config/client.toml +++ b/config/client.toml @@ -12,8 +12,8 @@ auth = { type = "secret", secret = "12345678" } [ssh1] type = "forward" boot = "fork" -exposes = [ "8080/tcp" ] -channel = [ "8002/tcp"] +exposes = [ "8080/tcp"] +channel = [ "8002/kcp"] restart = "always" cryptos = ["aes"] compress = ["lz4"] diff --git a/config/server.toml b/config/server.toml index a65da44..a018ed9 100644 --- a/config/server.toml +++ b/config/server.toml @@ -13,10 +13,10 @@ port = 8888 context = "/api" auth = { type = "secret", secret = "secret" } -[[listen]] -type = "kcp" -port = 6666 -bind = "0.0.0.0" +# [[listen]] +# type = "kcp" +# port = 6666 +# bind = "0.0.0.0" [[listen]] @@ -28,7 +28,7 @@ auth = { type = "secret", secret = "ffsdfsdfjkl" } compress = ["lz4"] -[[listen]] -type = "tcp" -port = 8880 -bind = "127.0.0.1" +# [[listen]] +# type = "tcp" +# port = 8880 +# bind = "127.0.0.1" diff --git a/src/bin/client.rs b/src/bin/client.rs index ba3983f..4a72360 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -2,7 +2,9 @@ use std::{net::SocketAddr, sync::Arc, time::Duration}; use fuso::{ cli, - client::port_forward::{MuxConnector, PortForwarder, Protocol}, + client::port_forward::{ + MuxTransmitterConnector, PortForwarder, Protocol, TransmitterConnector, + }, config::{ client::{ Config, FinalTarget, Host, ServerAddr, Service, WithBridgeService, WithForwardService, @@ -12,11 +14,13 @@ use fuso::{ }, core::{ accepter::AccepterExt, - connector::MultiConnector, - io::{AsyncReadExt, AsyncWriteExt}, - net::{TcpListener, TcpStream}, + connector::{ + AbstractConnector, Connector, ConnectorWithFn, EncryptedAndCompressedConnector, + MultiConnector, + }, + net::{KcpConnector, TcpListener, TcpStream}, protocol::{AsyncPacketRead, AsyncPacketSend}, - rpc::{structs::port_forward::Target, AsyncCall, Caller, Decoder, Encoder}, + rpc::{Decoder, Encoder}, stream::{ compress::CompressedStream, fragment::Fragment, @@ -24,13 +28,60 @@ use fuso::{ UseCompress, UseCrypto, }, transfer::{AbstractTransmitter, TransmitterExt}, - AbstractStream, Connection, + AbstractStream, }, error, runner::{FnRunnable, NamedRunnable, Rise, ServiceRunner}, - runtime::{tokio::TokioRuntime, Runtime}, + runtime::tokio::{TokioRuntime, UdpWithTokioRuntime}, }; -use kcp_rust::KcpConnector; +use serde::de; + +macro_rules! unsupport_protocol { + () => { + fuso::error::Result::Err(fuso::error::FusoError::UnsupportProtocol) + }; +} + +macro_rules! create_kcp_connector { + ($conf: expr, $info: expr) => {{ + let (addr, port) = $info; + KcpConnector::new::($conf, || async move { + addr.try_connect(port, |host, port| async move { + let udp = tokio::net::UdpSocket::bind("0.0.0.0:0").await?; + udp.connect(format!("{host}:{port}")).await?; + Ok(udp.into()) + }) + .await + }) + .await + }}; +} + +macro_rules! try_connect { + (kcp => $proto: expr, $connector: expr) => {{ + match $proto { + Protocol::Kcp => { + let kcp = $connector.connect(()).await?; + Ok(AbstractStream::new(kcp).into()) + } + _ => unsupport_protocol!(), + } + }}; + (tcp => $proto: expr, $info: expr) => {{ + let (addr, port) = $info; + match $proto { + Protocol::Tcp => Ok(({ + addr.try_connect(port, |host, port| async move { + tokio::net::TcpStream::connect(format!("{host}:{port}")) + .await + .map_err(Into::into) + }) + .await + })), + _ => unsupport_protocol!(), + } + }}; +} fn main() { env_logger::Builder::new() @@ -39,7 +90,7 @@ fn main() { match cli::client::parse() { Ok(conf) => { - conf.check(); + // conf.check(); fuso::enter_async_main(enter_fuso_main(conf)).unwrap() } Err(e) => { @@ -78,7 +129,9 @@ fn enter_fuso_main(mut conf: Config) -> ServiceRunner<'static, TokioRuntime> { sp.register( s.restart.clone(), NamedRunnable::new(name, { - FnRunnable::new(move || enter_forward_service_main(sc.clone(), s.clone())) + FnRunnable::new(move || { + enter_port_forward_service_main(sc.clone(), s.clone()) + }) }), ); } @@ -88,27 +141,7 @@ fn enter_fuso_main(mut conf: Config) -> ServiceRunner<'static, TokioRuntime> { sp.build() } -async fn try_tcp_connect<'a, Cr, Co>( - addr: ServerAddr, - port: u16, - cryptos: Cr, - compress: Co, -) -> error::Result> -where - Cr: Iterator, - Co: Iterator, -{ - addr.try_connect(port, |host, port| async move { - match host { - Host::Ip(ip) => TcpStream::connect(SocketAddr::new(*ip, port)).await, - Host::Domain(domain) => TcpStream::connect(format!("{domain}:{port}")).await, - } - }) - .await - .map(|stream| stream.use_crypto(cryptos).use_compress(compress)) -} - -async fn enter_forward_service_main( +async fn enter_port_forward_service_main( config: Stateful, service: WithForwardService, ) -> error::Result { @@ -116,18 +149,17 @@ async fn enter_forward_service_main( let crypto = &server.crypto; let compress = &server.compress; + let mut using_mux_connector = true; + let result = server .try_connect(|host, port| async move { log::trace!("connect to server {host}:{port}"); - let connection = match host { - Host::Ip(ip) => TcpStream::connect(SocketAddr::new(*ip, port)).await, - Host::Domain(domain) => TcpStream::connect(format!("{domain}:{port}")).await, - }; - - connection.map(|c| { - log::debug!("connection established: {host}:{port}"); - c - }) + TcpStream::connect(format!("{host}:{port}")) + .await + .map(|transport| { + log::debug!("connection established: {host}:{port}"); + transport + }) }) .await? .use_crypto(crypto.iter()) @@ -138,7 +170,7 @@ async fn enter_forward_service_main( ) .await; - let mut stream = match result { + let mut transport = match result { Ok(stream) => stream, Err(e) => { log::error!("fail to handshake {e:?}"); @@ -146,72 +178,62 @@ async fn enter_forward_service_main( } }; - stream.write_config(&service).await?; + transport.write_config(&service).await?; - let mut connector = MultiConnector::>::new(); + let connector = EncryptedAndCompressedConnector::new(service.crypto, service.compress, { + let mut connector = MultiConnector::::new(); - if let Some(channels) = service.channel.as_ref() { - for expose in channels.iter() { + let (mux_connector, exposes) = match &service.channel { + Some(channels) => (false, channels.iter()), + None => (true, service.exposes.iter()), + }; + + for expose in exposes { match expose { - Expose::Kcp(_, port) => {} + Expose::Kcp(_, port) => { + let info = (server.addr.clone(), *port); + let kcp_connector = create_kcp_connector!(Default::default(), info)?; + connector.add(move |proto| { + let connector = kcp_connector.clone(); + async move { try_connect!(kcp => proto, connector) } + }) + } Expose::Tcp(_, port) => { - let port = *port; - let cryptos = service.crypto.clone(); - let compress = service.compress.clone(); - let addr = server.addr.clone(); + let info = (server.addr.clone(), *port); connector.add(move |proto| { - let addr = addr.clone(); - let cryptos = cryptos.clone(); - let compress = compress.clone(); - async move { - try_tcp_connect(addr, port, cryptos.iter(), compress.iter()) - .await - .map(Into::into) - } + let info = info.clone(); + async move { try_connect!(tcp => proto, info)?.map(Into::into) } }) } } } - } else { - let mux: MuxConfig = MuxConfig::default(); - stream.send_packet(&mux.borrow_encode()?).await?; + using_mux_connector = mux_connector; - for expose in service.exposes.clone() { - let addr = server.addr.clone(); + connector + }); - match expose { - Expose::Kcp(_, port) => todo!(), - Expose::Tcp(_, port) => { - connector.add(MuxConnector::new(mux.clone(), move |proto| { - let addr = addr.clone(); - async move { - addr.try_connect(port, |host, port| async move { - let connection = match host { - Host::Ip(ip) => { - TcpStream::connect(SocketAddr::new(*ip, port)).await - } - Host::Domain(domain) => { - TcpStream::connect(format!("{domain}:{port}")).await - } - }?; - - Ok(AbstractStream::new(connection).into()) - }) - .await - } - })) - } - } - } - } - - let mut forwarder = PortForwarder::new(stream, service.target, connector); + let mut forwarder = if !using_mux_connector { + PortForwarder::new( + transport, + service.target, + TransmitterConnector::new(connector), + ) + } else { + let conf = MuxConfig::default(); + transport.send_packet(&conf.borrow_encode()?).await?; + PortForwarder::new( + transport, + service.target, + MuxTransmitterConnector::new(conf, connector), + ) + }; - log::debug!("forward started ."); + log::debug!("port forwarder started ."); loop { let (linker, target) = forwarder.accept().await?; + tokio::spawn(async move { log::debug!("target {:?}", target); @@ -219,7 +241,7 @@ async fn enter_forward_service_main( FinalTarget::Udp { addr, port } => {} FinalTarget::Shell { path, args } => { let mut builder = fuso::pty::builder(path); - + builder.args(&args); let pty = builder.build().unwrap(); @@ -355,7 +377,7 @@ async fn enter_bridge_service_main( log::debug!("bridge started ... {}:{}", service.bind, service.port); loop { - let (addr, mut stream) = listener.accept().await?; + let (addr, stream) = listener.accept().await?; log::debug!("{:?}", addr); diff --git a/src/bin/server.rs b/src/bin/server.rs index 84468ca..79554dc 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -58,7 +58,6 @@ async fn enter_fuso_main(conf: Config) -> error::Result<()> { enter_fuso_serve(Stateful::new(conf)).await?; - // axum::serve(tcp_listener, make_service) loop { @@ -173,7 +172,7 @@ async fn enter_fuso_serve(conf: Stateful) -> error::Result<()> { tokio::spawn(async move { if let Err(e) = handle_connection(conf, transport).await { - log::error!("{:?}", e); + log::error!("transport closed {e:?}"); } }); } @@ -292,13 +291,8 @@ where log::debug!("start forward {} -> {}", c1.addr(), c2.addr()); - - tokio::spawn(async move { - let _ = c1.transfer(c2).await; - - }); } }); diff --git a/src/client/port_forward/connector.rs b/src/client/port_forward/connector.rs index 82068f2..b082d5e 100644 --- a/src/client/port_forward/connector.rs +++ b/src/client/port_forward/connector.rs @@ -1,31 +1,42 @@ use rc4::{KeyInit, Rc4, StreamCipher}; use crate::core::{ - connector::{AbstractConnector, Connector, IntoConnector}, + connector::{AbstractConnector, Connector}, stream::handshake::MuxConfig, transfer::{AbstractTransmitter, TransmitterExt}, + Stream, }; use super::Protocol; -pub struct MuxConnector<'connector> { +pub struct MuxTransmitterConnector<'connector, O> { conf: MuxConfig, - inner: AbstractConnector<'connector, Protocol, AbstractTransmitter<'connector>>, + connector: AbstractConnector<'connector, Protocol, O>, } -impl<'connector> MuxConnector<'connector> { - pub fn new(conf: MuxConfig, connector: I) -> Self +pub struct TransmitterConnector<'c, C> { + connector: AbstractConnector<'c, Protocol, C>, +} + +impl<'connector, O> MuxTransmitterConnector<'connector, O> { + pub fn new(conf: MuxConfig, connector: C) -> Self where - I: IntoConnector<'connector, C, Protocol, AbstractTransmitter<'connector>>, + C: Connector + Send + Sync + Unpin + 'connector, { + #[cfg(debug_assertions)] + log::debug!("using mux transmitter connector {:#?}", conf); + Self { conf, - inner: connector.into(), + connector: AbstractConnector::new(connector), } } } -impl<'connector> Connector for MuxConnector<'connector> { +impl<'connector, O> Connector for MuxTransmitterConnector<'connector, O> +where + O: Stream + Send + Unpin + 'static, +{ type Output = AbstractTransmitter<'connector>; fn connect<'conn>( @@ -33,14 +44,42 @@ impl<'connector> Connector for MuxConnector<'connector> { target: Protocol, ) -> crate::core::BoxedFuture<'conn, crate::error::Result> { Box::pin(async move { - let mut transmitter = self.inner.connect(target).await?; + let mut transmitter = self.connector.connect(target).await?; let mut magic = self.conf.magic.to_le_bytes(); Rc4::new((&self.conf.secret).into()).apply_keystream(&mut magic); transmitter.send_all(&magic).await?; - Ok(transmitter) + Ok(transmitter.into()) }) } } + +impl<'c, O> TransmitterConnector<'c, O> { + pub fn new(connector: C) -> Self + where + C: Connector + Send + Sync + Unpin + 'c, + { + #[cfg(debug_assertions)] + log::debug!("using transmitter connector"); + + Self { + connector: AbstractConnector::new(connector), + } + } +} + +impl<'c, O> Connector for TransmitterConnector<'c, O> +where + O: Stream + Send + Unpin + 'static, +{ + type Output = AbstractTransmitter<'static>; + + fn connect<'conn>( + &'conn self, + target: Protocol, + ) -> crate::core::BoxedFuture<'conn, crate::error::Result> { + Box::pin(async move { self.connector.connect(target).await.map(Into::into) }) + } +} diff --git a/src/client/port_forward/mod.rs b/src/client/port_forward/mod.rs index 44d4fd9..ca523b7 100644 --- a/src/client/port_forward/mod.rs +++ b/src/client/port_forward/mod.rs @@ -36,11 +36,8 @@ where { pub fn new_with_runtime(transport: S, target: FinalTarget, connector: C) -> Self where - C: Connector> - + Sync - + Send - + Unpin - + 'static, + C: Connector>, + C: Sync + Send + Unpin + 'static, { let transport = Transport::new::(std::time::Duration::from_secs(1), transport); diff --git a/src/config/mod.rs b/src/config/mod.rs index 2155024..545ee5e 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -78,7 +78,7 @@ pub enum IP { #[serde(try_from = "String")] pub enum Expose { Kcp(IP, u16), - Tcp(IP, u16), + Tcp(IP, u16) } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/src/core/connector.rs b/src/core/connector.rs index 571fce4..21a6538 100644 --- a/src/core/connector.rs +++ b/src/core/connector.rs @@ -1,8 +1,17 @@ -use std::{future::Future, sync::Arc}; +use std::{collections::HashSet, future::Future, sync::Arc}; -use crate::error; +use crate::{ + config::{Compress, Crypto}, + error, +}; -use super::BoxedFuture; +use super::{ + io::{AsyncRead, AsyncWrite}, + stream::compress::CompressedStream, + BoxedFuture, +}; + +use crate::core::stream::{UseCompress, UseCrypto}; pub trait IntoConnector<'a, C, T, O> { fn into(self) -> AbstractConnector<'a, T, O>; @@ -31,6 +40,12 @@ pub struct MultiConnector<'a, T, O> { connectors: Vec>, } +pub struct EncryptedAndCompressedConnector { + cryptos: HashSet, + compress: HashSet, + connector: C, +} + unsafe impl Sync for AbstractConnector<'_, T, O> {} impl<'connector, T, O> AbstractConnector<'connector, T, O> { @@ -113,6 +128,15 @@ where } } +impl<'a, C, T, O> IntoConnector<'a, EncryptedAndCompressedConnector, T, O> for C +where + C: Connector + Unpin + Send + 'a, +{ + fn into(self) -> AbstractConnector<'a, T, O> { + AbstractConnector::new(self) + } +} + impl<'a, T, O> Connector for ConnectorWithFn<'a, T, O> { type Output = O; fn connect<'conn>(&'conn self, target: T) -> BoxedFuture<'conn, error::Result> { @@ -125,3 +149,46 @@ impl<'connector, T, O> Clone for ReplicableConnector<'connector, T, O> { Self(self.0.clone()) } } + +impl EncryptedAndCompressedConnector { + pub fn new( + cryptos: HashSet, + compress: HashSet, + connector: C, + ) -> Self + where + C: Connector, + Input: Send + Sync + 'static, + Output: AsyncRead + AsyncWrite + Send + Unpin, + { + Self { + cryptos, + compress, + connector, + } + } +} + +impl Connector for EncryptedAndCompressedConnector +where + Input: Send + Sync + 'static, + C: Connector + Send + Sync, + Output: AsyncRead + AsyncWrite + Send + Unpin + 'static, +{ + type Output = CompressedStream<'static>; + + fn connect<'conn>( + &'conn self, + target: Input, + ) -> BoxedFuture<'conn, error::Result> { + Box::pin(async move { + Ok({ + self.connector + .connect(target) + .await? + .use_crypto(self.cryptos.iter()) + .use_compress(self.compress.iter()) + }) + }) + } +} diff --git a/src/core/net/kcp.rs b/src/core/net/kcp.rs index 95c044f..d56a1cd 100644 --- a/src/core/net/kcp.rs +++ b/src/core/net/kcp.rs @@ -5,9 +5,12 @@ use std::{ task::{Context, Poll}, }; +use kcp_rust::KcpRuntime; + use crate::{ core::{ accepter::Accepter, + connector::Connector, future::LazyFuture, io::{AsyncRead, AsyncWrite}, }, @@ -30,6 +33,9 @@ pub struct KcpListener { >, } +#[derive(Clone)] +pub struct KcpConnector(Arc>>); + pub enum KcpStream { Client(kcp_rust::KcpStream), Server(kcp_rust::KcpStream), @@ -76,6 +82,33 @@ impl Accepter for KcpListener { } } +impl KcpConnector { + pub async fn new(conf: kcp_rust::Config, f: F) -> error::Result + where + P: KcpRuntime, + F: FnOnce() -> Fut, + Fut: std::future::Future>>, + { + let udp = f().await?; + let connector = kcp_rust::KcpConnector::new::

(udp, conf)?; + Ok(Self(Arc::new(connector))) + } +} + +impl Connector<()> for KcpConnector { + type Output = KcpStream; + + fn connect<'conn>( + &'conn self, + _: (), + ) -> crate::core::BoxedFuture<'conn, error::Result> { + Box::pin(async move { + let (_, stream) = self.0.open().await?; + Ok(KcpStream::Client(stream)) + }) + } +} + impl AsyncRead for KcpStream { fn poll_read( mut self: Pin<&mut Self>, diff --git a/src/core/net/udp.rs b/src/core/net/udp.rs index 17a74d2..8647122 100644 --- a/src/core/net/udp.rs +++ b/src/core/net/udp.rs @@ -224,6 +224,10 @@ impl<'a> Clone for UdpSocket<'a> { } impl<'a> UdpSocket<'a> { + pub fn new(udp: AbstractDatagram<'a>) -> Self { + Self { inner: udp } + } + pub async fn bind(addr: A) -> error::Result<(SocketAddr, Self)> where P: UdpProvider, diff --git a/src/core/protocol.rs b/src/core/protocol.rs index 8d45b50..003836f 100644 --- a/src/core/protocol.rs +++ b/src/core/protocol.rs @@ -51,7 +51,6 @@ pub trait AsyncPacketSend: AsyncWrite { }, } } - } impl AsyncPacketRead for T where T: AsyncRead {} @@ -106,8 +105,7 @@ impl Buffer { match Pin::new(&mut *reader).poll_read(cx, &mut self.data[off..])? { Poll::Pending => break, Poll::Ready(0) => { - log::debug!("error ................"); - return Poll::Ready(Err(io::Error::from(io::ErrorKind::UnexpectedEof).into())) + return Poll::Ready(Err(io::Error::from(io::ErrorKind::ConnectionReset).into())) } Poll::Ready(n) => { self.off += n; diff --git a/src/core/transfer.rs b/src/core/transfer.rs index 291240f..741c2ef 100644 --- a/src/core/transfer.rs +++ b/src/core/transfer.rs @@ -250,7 +250,7 @@ where Pin::new(&mut *this).poll_send(cx, buf) } - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } } diff --git a/src/error/mod.rs b/src/error/mod.rs index c63cba8..e8b67b7 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -17,6 +17,8 @@ pub enum FusoError { InvalidExposeType, NotResponse, Custom(String), + UnsupportProtocol, + KcpError(kcp_rust::KcpError), Utf8Error(std::str::Utf8Error), BadCString(std::ffi::NulError), MsgPack(MsgPack), @@ -56,6 +58,12 @@ impl From for FusoError { } } +impl From for FusoError{ + fn from(value: kcp_rust::KcpError) -> Self { + Self::KcpError(value) + } +} + impl From for FusoError { fn from(value: rmp_serde::decode::Error) -> Self { Self::MsgPack(MsgPack::Decode(value)) diff --git a/src/runtime/tokio/kcp.rs b/src/runtime/tokio/kcp.rs index 3bf9254..360a3a5 100644 --- a/src/runtime/tokio/kcp.rs +++ b/src/runtime/tokio/kcp.rs @@ -1,5 +1,6 @@ use std::{net::SocketAddr, sync::Arc, task::Poll}; +use kcp_rust::Canceler; use tokio::io::ReadBuf; use crate::{ @@ -31,6 +32,12 @@ impl KcpListener { } } +impl TokioUdpSocket { + pub fn new(udp: tokio::net::UdpSocket) -> Self { + Self(Arc::new(udp)) + } +} + impl crate::core::net::UdpProvider for UdpWithTokioRuntime { type Binder = Self; @@ -71,13 +78,24 @@ impl kcp_rust::KcpRuntime for UdpWithTokioRuntime { impl kcp_rust::Runner for UdpWithTokioRuntime { type Err = error::FusoError; - fn start(process: kcp_rust::Background) -> std::result::Result<(), Self::Err> { + fn start(process: kcp_rust::Background) -> std::result::Result { + let (canceler, sig) = Canceler::new(); + crate::runtime::tokio::TokioRuntime::spawn(async move { - if let Err(e) = process.await { - log::warn!("{:?}", e); + let kind = process.kind(); + tokio::select! { + _ = sig.recv() => { + log::debug!("stop kcp {kind:?}"); + } + e = process => { + if let Err(e) = e{ + log::debug!("kcp error {e:?}"); + } + } } }); - Ok(()) + + Ok(canceler) } } @@ -147,3 +165,9 @@ impl crate::core::net::AsyncSend for TokioUdpSocket { self.0.poll_send(cx, buf) } } + +impl<'u> From for crate::core::net::UdpSocket<'u> { + fn from(udp: tokio::net::UdpSocket) -> Self { + Self::new(Box::new(TokioUdpSocket::new(udp))) + } +} diff --git a/src/runtime/tokio/port_forward/mod.rs b/src/runtime/tokio/port_forward/mod.rs index 7a929fa..3446ade 100644 --- a/src/runtime/tokio/port_forward/mod.rs +++ b/src/runtime/tokio/port_forward/mod.rs @@ -48,11 +48,8 @@ where { pub fn new(transport: S, target: FinalTarget, connector: C) -> Self where - C: Connector> - + Sync - + Send - + Unpin - + 'static, + C: Connector>, + C: Sync + Send + Unpin + 'static, { Self::new_with_runtime(transport, target, connector) } diff --git a/src/runtime/tokio/tcp.rs b/src/runtime/tokio/tcp.rs index 8a2ba93..0818d06 100644 --- a/src/runtime/tokio/tcp.rs +++ b/src/runtime/tokio/tcp.rs @@ -126,3 +126,9 @@ impl Accepter for tokio::net::TcpListener { } } } + +impl<'a> From for AbstractStream<'a>{ + fn from(stream: tokio::net::TcpStream) -> Self { + AbstractStream::new(stream) + } +} \ No newline at end of file From b337775d0b2916dcf6d6c52b48f070b47ea12f21 Mon Sep 17 00:00:00 2001 From: yyy <37452715+editso@users.noreply.github.com> Date: Sun, 19 May 2024 23:33:50 +0800 Subject: [PATCH 29/29] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=AB=AF=E5=8F=A3?= =?UTF-8?q?=E8=BD=AC=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 1 - config/client.toml | 2 +- src/bin/client.rs | 185 ++++++++++++++++++--------------------- src/runtime/tokio/kcp.rs | 2 +- 4 files changed, 88 insertions(+), 102 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f941eb4..a4d2155 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -689,7 +689,6 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "kcp-rust" version = "0.1.2" -source = "git+https://github.com/editso/kcp-rust?branch=master#10485ea9ec1dfe5eb930c7fd5116339aea1183e5" dependencies = [ "cc", "log", diff --git a/config/client.toml b/config/client.toml index c26a50f..7f54e07 100644 --- a/config/client.toml +++ b/config/client.toml @@ -13,7 +13,7 @@ auth = { type = "secret", secret = "12345678" } type = "forward" boot = "fork" exposes = [ "8080/tcp"] -channel = [ "8002/kcp"] +channel = [ "8002/kcp", "8003/tcp"] restart = "always" cryptos = ["aes"] compress = ["lz4"] diff --git a/src/bin/client.rs b/src/bin/client.rs index 4a72360..7f683c2 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -3,38 +3,33 @@ use std::{net::SocketAddr, sync::Arc, time::Duration}; use fuso::{ cli, client::port_forward::{ - MuxTransmitterConnector, PortForwarder, Protocol, TransmitterConnector, + Linker, MuxTransmitterConnector, PortForwarder, Protocol, TransmitterConnector, }, config::{ client::{ - Config, FinalTarget, Host, ServerAddr, Service, WithBridgeService, WithForwardService, + Config, FinalTarget, Host, Service, WithBridgeService, WithForwardService, WithProxyService, }, - Compress, Crypto, Expose, Stateful, + Expose, Stateful, }, core::{ accepter::AccepterExt, - connector::{ - AbstractConnector, Connector, ConnectorWithFn, EncryptedAndCompressedConnector, - MultiConnector, - }, + connector::{Connector, EncryptedAndCompressedConnector, MultiConnector}, net::{KcpConnector, TcpListener, TcpStream}, protocol::{AsyncPacketRead, AsyncPacketSend}, rpc::{Decoder, Encoder}, stream::{ - compress::CompressedStream, fragment::Fragment, handshake::{Handshake, MuxConfig}, UseCompress, UseCrypto, }, - transfer::{AbstractTransmitter, TransmitterExt}, + transfer::TransmitterExt, AbstractStream, }, error, runner::{FnRunnable, NamedRunnable, Rise, ServiceRunner}, runtime::tokio::{TokioRuntime, UdpWithTokioRuntime}, }; -use serde::de; macro_rules! unsupport_protocol { () => { @@ -86,6 +81,7 @@ macro_rules! try_connect { fn main() { env_logger::Builder::new() .filter_level(log::LevelFilter::Debug) + .filter_module("kcp_rust", log::LevelFilter::Trace) .init(); match cli::client::parse() { @@ -233,96 +229,10 @@ async fn enter_port_forward_service_main( loop { let (linker, target) = forwarder.accept().await?; - tokio::spawn(async move { - log::debug!("target {:?}", target); - - match target { - FinalTarget::Udp { addr, port } => {} - FinalTarget::Shell { path, args } => { - let mut builder = fuso::pty::builder(path); - - builder.args(&args); - - let pty = builder.build().unwrap(); - - if let Ok(a) = linker.link(Protocol::Tcp).await { - a.transfer(pty).await.unwrap(); - }; - } - FinalTarget::Dynamic => { - let transmitter = linker.link(Protocol::Tcp).await.unwrap(); - - let udp = Arc::new(tokio::net::UdpSocket::bind("0.0.0.0:0").await.unwrap()); - - let (writer, mut reader) = transmitter.split(); - - loop { - let pkt = match reader.recv_packet().await { - Err(_) => break, - Ok(data) => data, - }; - - let pkt: Fragment = pkt.decode().unwrap(); - - let udp = udp.clone(); - let mut writer = writer.clone(); - - match pkt { - Fragment::UdpForward { - saddr, - daddr, - dport, - data, - } => { - let target = format!("{}:{dport}", daddr.to_string()); - log::debug!("udp forward {saddr} -> {target}"); - udp.send_to(&data, target).await.unwrap(); - let mut buf = [0u8; 1400]; - let (n, addr) = udp.recv_from(&mut buf).await.unwrap(); - - writer - .send_packet( - &Fragment::Udp(saddr, buf[..n].to_vec()).encode().unwrap(), - ) - .await - .unwrap(); - } - _ => {} - } - } - } - FinalTarget::Tcp { addr, port } => { - let result = addr - .try_connect(port, |host, port| async move { - match host { - Host::Ip(ip) => { - TcpStream::connect(SocketAddr::new(*ip, port)).await - } - Host::Domain(domain) => { - TcpStream::connect(format!("{domain}:{port}")).await - } - } - }) - .await; - - match result { - Ok(stream) => match linker.link(Protocol::Tcp).await { - Ok(transmitter) => { - transmitter.transfer(stream).await; - } - Err(e) => { - log::debug!("{:?}", e); - } - }, - Err(e) => { - if let Err(e) = linker.cancel(e).await { - log::warn!("{:?}", e); - }; - } - } - } - } + if let Err(e) = do_forward(linker, target).await { + log::warn!("forward: {e:?}") + }; }); } } @@ -404,3 +314,80 @@ async fn enter_bridge_service_main( }); } } + +async fn do_forward(linker: Linker, target: FinalTarget) -> error::Result<()> { + match target { + FinalTarget::Udp { .. } => {} + FinalTarget::Shell { path, args } => { + let mut builder = fuso::pty::builder(path); + + builder.args(&args); + + match builder.build() { + Err(e) => linker.cancel(e).await?, + Ok(pty) => { + linker.link(Protocol::Tcp).await?.transfer(pty).await?; + } + } + } + FinalTarget::Tcp { addr, port } => { + let result = addr + .try_connect(port, |host, port| async move { + log::debug!("connect to {host}:{port}"); + TcpStream::connect(format!("{host}:{port}")).await + }) + .await; + + match result { + Err(e) => linker.cancel(e).await?, + Ok(stream) => { + linker.link(Protocol::Kcp).await?.transfer(stream).await?; + } + } + } + FinalTarget::Dynamic => { + let transmitter = linker.link(Protocol::Tcp).await?; + + let udp = Arc::new(tokio::net::UdpSocket::bind("0.0.0.0:0").await?); + + let (writer, mut reader) = transmitter.split(); + + loop { + let pkt = reader.recv_packet().await?; + + let pkt: Fragment = pkt.decode()?; + + let udp = udp.clone(); + + let mut writer = writer.clone(); + + match pkt { + Fragment::UdpForward { + saddr, + daddr, + dport, + data, + } => { + let target = format!("{}:{dport}", daddr.to_string()); + log::debug!("udp forward {saddr} -> {target}"); + + udp.send_to(&data, target).await?; + + let mut buf = [0u8; 1400]; + + let _ = + tokio::time::timeout(std::time::Duration::from_secs(2), async move { + let (n, _) = udp.recv_from(&mut buf).await.unwrap(); + + let pkt = Fragment::Udp(saddr, buf[..n].to_vec()).encode().unwrap(); + writer.send_packet(&pkt).await.unwrap(); + }) + .await; + } + _ => {} + } + } + } + } + Ok(()) +} diff --git a/src/runtime/tokio/kcp.rs b/src/runtime/tokio/kcp.rs index 360a3a5..a135f53 100644 --- a/src/runtime/tokio/kcp.rs +++ b/src/runtime/tokio/kcp.rs @@ -89,7 +89,7 @@ impl kcp_rust::Runner for UdpWithTokioRuntime { } e = process => { if let Err(e) = e{ - log::debug!("kcp error {e:?}"); + log::debug!("kcp error {kind:?} {e:?}"); } } }

, - observer: Option>, - decorator: Option>, - ) -> Self { - Self { - provider, - observer, - decorator, - } - } - - pub fn observer(&self) -> &Option> { - &self.observer - } - - pub async fn decorate(&self, client: S) -> crate::Result { - match self.decorator.as_ref() { - None => Ok(client), - Some(decorator) => decorator.call(client).await, - } - } -} - -impl Deref for Processor, S, ()> { - type Target = Arc>; - - fn deref(&self) -> &Self::Target { - &self.provider - } -} - -impl Processor -where - P: Provider>, -{ - pub fn bind(&self, socket: Socket) -> BoxedFuture { - self.provider.call(socket) - } -} - -impl Clone for Processor { - fn clone(&self) -> Self { - Self { - provider: self.provider.clone(), - observer: self.observer.clone(), - decorator: self.decorator.clone(), - } - } -} diff --git a/src/core/protocol/local/mod.rs b/src/core/protocol/local/mod.rs deleted file mode 100644 index af99682..0000000 --- a/src/core/protocol/local/mod.rs +++ /dev/null @@ -1,473 +0,0 @@ -use std::{ - io::Read, - net::{Ipv4Addr, Ipv6Addr, SocketAddr}, -}; - -use bytes::{Buf, BufMut}; - -use crate::{Addr, Error, Kind, Result, Socket}; - -// macro_rules! invalid { -// () => { -// Err(Kind::Deserialize("invalid data".to_owned()).into()) -// }; -// } - -// pub const MAGIC: u32 = 0xFC; - -// #[derive(Debug, Clone, PartialEq, Eq)] -// pub struct Packet { -// pub magic: u32, -// pub data_len: u32, -// pub payload: Vec, -// } - -// #[derive(Debug, Clone, PartialEq, Eq)] -// pub enum Connect { -// TCP(Option), -// UDP(Addr), -// } - -// #[derive(Debug, Clone, PartialEq, Eq)] -// pub enum Bind { -// Bind(Addr), -// Failed(Addr, Vec), -// } - -// #[derive(Debug, Clone, PartialEq, Eq)] -// pub enum Auth { -// Auth(Vec), -// NoAuth, -// } - -// #[derive(Debug, Clone, PartialEq, Eq)] -// pub enum Message { -// Bind(Bind), -// Map(u32, Socket), -// Connect(Connect, Auth), -// } - -// trait BytesEx { -// /// self.len() > expect -// /// 自身剩余长度大于expect Ok 否则Err -// fn fat(&self, expect: usize) -> Result<()>; -// } - -// impl BytesEx for &[u8] { -// fn fat(&self, expect: usize) -> Result<()> { -// if self.len() < expect { -// Err(Kind::Deserialize("invalid data".to_owned()).into()) -// } else { -// Ok(()) -// } -// } -// } - -// trait GetMeta { -// fn is(magic: u8) -> Result<()> -// where -// Self: Sized, -// { -// if magic == Self::get_magic() { -// Ok(()) -// } else { -// Err(Kind::Deserialize(format!("{:x} is not {}", magic, stringify!(Self))).into()) -// } -// } - -// fn get_magic() -> u8 -// where -// Self: Sized; - -// fn get_size(&self) -> usize; -// } - -// impl GetMeta for Addr { -// fn get_magic() -> u8 -// where -// Self: Sized, -// { -// 0x1 -// } - -// fn get_size(&self) -> usize { -// 2 + match self { -// Addr::Socket(addr) => match addr { -// std::net::SocketAddr::V4(_) => 6, -// std::net::SocketAddr::V6(_) => 18, -// }, -// Addr::Domain(domain, _) => { -// // u32 + data + port -// domain.as_bytes().len() + 6 -// } -// } -// } -// } - -// impl GetMeta for Connect { -// fn get_magic() -> u8 -// where -// Self: Sized, -// { -// 0x2 -// } - -// fn get_size(&self) -> usize { -// 2 + match self { -// Connect::TCP(tcp) => tcp.get_size(), -// Connect::UDP(udp) => udp.get_size(), -// } -// } -// } - -// impl GetMeta for Bind { -// fn get_magic() -> u8 -// where -// Self: Sized, -// { -// 0x3 -// } - -// fn get_size(&self) -> usize { -// 2 + match self { -// Bind::Bind(bind) => bind.get_size(), -// Bind::Failed(addr, err) => { -// // addr + u32 + data -// addr.get_size() + err.len() + 4 -// } -// } -// } -// } - -// impl GetMeta for Auth { -// fn get_magic() -> u8 -// where -// Self: Sized, -// { -// 0x4 -// } - -// fn get_size(&self) -> usize { -// 2 + match self { -// Auth::Auth(auth) => auth.len(), -// Auth::NoAuth => 0, -// } -// } -// } - -// impl GetMeta for Message { -// fn get_magic() -> u8 -// where -// Self: Sized, -// { -// 0x5 -// } - -// fn get_size(&self) -> usize { -// 2 + match self { -// Message::Bind(bind) => bind.get_size(), -// Message::Connect(connect, auth) => connect.get_size() + auth.get_size(), -// } -// } -// } - -// impl TryFrom<&[u8]> for Message { -// type Error = Error; - -// fn try_from(data: &[u8]) -> Result { -// data.fat(2)?; - -// let mut cur = std::io::Cursor::new(data); - -// Self::is(cur.get_u8())?; - -// let magic = cur.get_u8(); -// let data = &data[2..]; - -// match magic { -// 0x01 => Ok(Self::Bind(data.try_into()?)), -// 0x02 => { -// let addr = Connect::try_from(data)?; -// let auth = &data[addr.get_size()..]; -// Ok(Self::Connect(addr, auth.try_into()?)) -// } -// _ => invalid!(), -// } -// } -// } - -// impl From<&Addr> for Vec { -// fn from(addr: &Addr) -> Self { -// let mut buf = Vec::new(); - -// buf.put_u8(Addr::get_magic()); - -// match addr { -// Addr::Socket(SocketAddr::V4(addr)) => { -// buf.put_u8(0x1); -// buf.put_slice(&addr.ip().octets()); -// buf.put_u16(addr.port()) -// } -// Addr::Socket(SocketAddr::V6(addr)) => { -// buf.put_u8(0x2); -// buf.put_slice(&addr.ip().octets()); -// buf.put_u16(addr.port()) -// } -// Addr::Domain(domain, port) => { -// let domain = domain.as_bytes(); -// buf.put_u8(0x3); -// buf.put_u32(domain.len() as u32); -// buf.put_slice(domain); -// buf.put_u16(*port); -// } -// } - -// buf -// } -// } - -// impl TryFrom<&[u8]> for Addr { -// type Error = Error; -// fn try_from(data: &[u8]) -> Result { -// data.fat(2)?; - -// let mut cur = std::io::Cursor::new(data); - -// Self::is(cur.get_u8())?; - -// let magic = cur.get_u8(); -// let data = &data[2..]; - -// match magic { -// 0x1 => { -// data.fat(6)?; - -// Ok(Self::Socket(SocketAddr::new( -// Ipv4Addr::from(cur.get_u32()).into(), -// cur.get_u16(), -// ))) -// } -// 0x2 => { -// data.fat(18)?; - -// Ok(Self::Socket(SocketAddr::new( -// Ipv6Addr::from(cur.get_u128()).into(), -// cur.get_u16(), -// ))) -// } -// 0x03 => { -// data.fat(4)?; - -// let domain_len = cur.get_u32() as usize; -// let mut buf = Vec::with_capacity(domain_len); - -// unsafe { -// buf.set_len(domain_len); -// } - -// cur.read_exact(&mut buf)?; - -// let domain = String::from_utf8(buf)?; - -// data.fat(2)?; - -// Ok(Self::Domain(domain, cur.get_u16())) -// } -// _ => { -// invalid!() -// } -// } -// } -// } - -// impl From<&Connect> for Vec { -// fn from(addr: &Connect) -> Self { -// let mut buf = Vec::new(); - -// buf.put_u8(Connect::get_magic()); - -// match addr { -// Connect::TCP(addr) => { -// buf.put_u8(0x01); -// buf.put_slice(&Vec::from(addr)); -// } -// Connect::UDP(addr) => { -// buf.put_u8(0x02); -// buf.put_slice(&Vec::from(addr)); -// } -// } - -// buf -// } -// } - -// impl TryFrom<&[u8]> for Connect { -// type Error = Error; - -// fn try_from(data: &[u8]) -> Result { -// data.fat(2)?; - -// let mut cur = std::io::Cursor::new(data); - -// Self::is(cur.get_u8())?; - -// let magic = cur.get_u8(); -// let data = &data[2..]; - -// match magic { -// 0x01 => Ok(Self::TCP(data.try_into()?)), -// 0x02 => Ok(Self::UDP(data.try_into()?)), -// _ => invalid!(), -// } -// } -// } - -// impl From<&Bind> for Vec { -// fn from(bind: &Bind) -> Self { -// let mut buf = Vec::new(); - -// buf.put_u8(Bind::get_magic()); - -// match bind { -// Bind::Bind(addr) => { -// buf.put_u8(0x01); -// buf.put_slice(&Vec::from(addr)) -// } -// Bind::Failed(addr, err) => { -// buf.put_u8(0x02); -// buf.put_slice(&Vec::from(addr)); -// buf.put_u32(err.len() as u32); -// buf.put_slice(err) -// } -// } - -// buf -// } -// } - -// impl TryFrom<&[u8]> for Bind { -// type Error = Error; -// fn try_from(data: &[u8]) -> Result { -// data.fat(2)?; - -// let mut cur = std::io::Cursor::new(data); - -// Self::is(cur.get_u8())?; - -// let magic = cur.get_u8(); - -// let data = &data[2..]; - -// match magic { -// 0x01 => Ok(Self::Bind(data.try_into()?)), -// 0x02 => { -// let addr = Addr::try_from(data)?; - -// cur.advance(addr.get_size()); - -// let data = &data[addr.get_size()..]; - -// data.fat(4)?; - -// let len = cur.get_u32() as usize; - -// let data = &data[4..]; - -// data.fat(len)?; - -// Ok(Self::Failed(addr, data[..len].to_vec())) -// } -// _ => invalid!(), -// } -// } -// } - -// impl TryFrom<&[u8]> for Auth { -// type Error = Error; - -// fn try_from(data: &[u8]) -> Result { -// data.fat(2)?; - -// let mut cur = std::io::Cursor::new(data); - -// Self::is(cur.get_u8())?; - -// let magic = cur.get_u8(); -// let data = &data[2..]; - -// match magic { -// 0x1 => { -// data.fat(4)?; -// let len = cur.get_u32() as usize; -// let data = &data[4..]; -// data.fat(len)?; - -// Ok(Self::Auth(data[..len].to_vec())) -// } -// 0x2 => Ok(Self::NoAuth), -// _ => invalid!(), -// } -// } -// } - -// impl From<&Auth> for Vec { -// fn from(auth: &Auth) -> Self { -// let mut buf = Vec::new(); - -// buf.put_u8(Auth::get_magic()); - -// match auth { -// Auth::Auth(auth) => { -// buf.put_u8(0x01); -// buf.put_u32(auth.len() as u32); -// buf.put_slice(auth); -// } -// Auth::NoAuth => { -// buf.put_u8(0x02); -// } -// } - -// buf -// } -// } - -// impl From<&Message> for Vec { -// fn from(behavior: &Message) -> Self { -// let mut buf = Vec::new(); - -// buf.put_u8(Message::get_magic()); - -// match behavior { -// Message::Bind(bind) => { -// buf.put_u8(0x01); -// buf.put_slice(&Vec::from(bind)); -// } -// Message::Connect(conn, auth) => { -// buf.put_u8(0x02); -// buf.put_slice(&Vec::from(conn)); -// buf.put_slice(&Vec::from(auth)); -// } -// } - -// buf -// } -// } - -// impl From<&Message> for Packet { -// fn from(packet: &Message) -> Self { -// let data = Vec::from(packet); -// Packet { -// magic: MAGIC, -// data_len: data.len() as u32, -// payload: data, -// } -// } -// } - -// impl TryFrom<&Packet> for Message { -// type Error = Error; - -// fn try_from(packet: &Packet) -> Result { -// Message::try_from(packet.payload.as_slice()) -// } -// } diff --git a/src/core/protocol/mod.rs b/src/core/protocol/mod.rs deleted file mode 100644 index 6a01969..0000000 --- a/src/core/protocol/mod.rs +++ /dev/null @@ -1,227 +0,0 @@ -#[cfg(feature = "fuso-serde")] -mod serde; -#[cfg(feature = "fuso-serde")] -pub use self::serde::*; - -#[cfg(not(feature = "fuso-serde"))] -mod local; - -#[cfg(not(feature = "fuso-serde"))] -pub use local::*; - -pub mod proto; -pub use proto::*; - -use std::{future::Future, pin::Pin, task::Poll}; - -use crate::{r#async::ReadBuf, AsyncRead, AsyncWrite, PacketErr, Result}; - -/// f => 0x66 -/// u => 0x75 -/// s => 0x73 -/// o => 0x6f -pub const MAGIC: [u8; 4] = [0x66, 0x75, 0x73, 0x6f]; - -pub fn head_size() -> usize { - 8 -} - -pub fn good_packet(magic_buf: &[u8; 4]) -> bool { - MAGIC.eq(magic_buf) -} - -pub fn empty_packet() -> Packet { - Packet { - magic: u32::from_be_bytes(MAGIC), - data_len: 0, - payload: vec![], - } -} - -pub fn make_packet(buf: Vec) -> Packet { - Packet { - magic: u32::from_be_bytes(MAGIC), - data_len: buf.len() as u32, - payload: buf, - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum State { - None, - Head, - Body, -} - -#[pin_project::pin_project] -pub struct RecvPacket<'a, R> -where - R: Unpin, -{ - buf: Vec, - state: State, - offset: usize, - #[pin] - reader: &'a mut R, -} - -#[pin_project::pin_project] -pub struct SendPacket<'a, W> -where - W: Unpin, -{ - buf: &'a [u8], - offset: usize, - #[pin] - writer: &'a mut W, -} - -pub trait AsyncRecvPacket: AsyncRead { - fn recv_packet<'a>(&'a mut self) -> RecvPacket<'a, Self> - where - Self: Unpin + Sized, - { - RecvPacket { - buf: Vec::new(), - state: State::None, - offset: 0, - reader: self, - } - } -} - -pub trait AsyncSendPacket: AsyncWrite { - fn send_packet<'a>(&'a mut self, buf: &'a [u8]) -> SendPacket<'a, Self> - where - Self: Unpin + Sized, - { - SendPacket { - buf, - offset: 0, - writer: self, - } - } -} - -impl AsyncSendPacket for T where T: AsyncWrite + Unpin {} - -impl AsyncRecvPacket for T where T: AsyncRead + Unpin {} - -impl<'a, T> Future for SendPacket<'a, T> -where - T: AsyncWrite + Unpin, -{ - type Output = Result<()>; - - fn poll( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - let mut this = self.project(); - let offset = this.offset; - loop { - match Pin::new(&mut **this.writer).poll_write(cx, &this.buf[*offset..]) { - Poll::Pending => break Poll::Pending, - Poll::Ready(Err(e)) => break Poll::Ready(Err(e)), - Poll::Ready(Ok(n)) => { - *offset += n; - } - }; - - if *offset == this.buf.len() { - break Poll::Ready(Ok(())); - } - } - } -} - -impl<'a, T> Future for RecvPacket<'a, T> -where - T: AsyncRead + Unpin, -{ - type Output = Result; - - fn poll( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - let mut this = self.project(); - let buf = this.buf; - let offset = this.offset; - - loop { - match this.state { - State::None => { - if *offset == head_size() { - log::trace!("packet header received"); - drop(std::mem::replace(this.state, State::Head)); - continue; - } else if buf.is_empty() { - log::trace!("initialize the header buffer"); - *offset = 0; - buf.resize(head_size(), 0); - } - } - State::Head if *offset == buf.len() => unsafe { - #[allow(unused)] - #[repr(C)] - struct Head { - magic: [u8; 4], - data_len: u32, - } - - let head = (buf.as_ptr() as *const Head).as_ref().unwrap(); - - if !self::good_packet(&head.magic) { - log::debug!("received illegal package {:x?}", head.magic); - break Poll::Ready(Err(PacketErr::Head(head.magic).into())); - } - - let len = head.data_len; - - log::trace!("received legal packet, data size {}bytes", len); - - if len == 0 { - break Poll::Ready(Ok(self::empty_packet())); - } - - drop(std::mem::replace(buf, { - let mut buf = Vec::with_capacity(len as usize); - buf.set_len(len as usize); - buf - })); - - *offset = 0; - drop(std::mem::replace(this.state, State::Body)); - }, - State::Body if *offset == buf.len() => { - log::trace!("packet reception completed {}bytes", buf.len()); - break Poll::Ready(Ok(make_packet(std::mem::replace(buf, Default::default())))); - } - _ => { - log::trace!( - "total {}bytes, received {}bytes remaining {}bytes", - buf.len(), - offset, - buf.len() - *offset - ); - } - } - - let mut buf = ReadBuf::new(&mut buf[*offset..]); - - match Pin::new(&mut **this.reader).poll_read(cx, &mut buf) { - Poll::Ready(Err(e)) => break Poll::Ready(Err(e)), - Poll::Pending => break Poll::Pending, - Poll::Ready(Ok(0)) => { - break Poll::Ready(Err({ - Into::::into(std::io::ErrorKind::ConnectionReset).into() - })) - } - Poll::Ready(Ok(n)) => { - *offset += n; - } - } - } - } -} diff --git a/src/core/protocol/proto.rs b/src/core/protocol/proto.rs deleted file mode 100644 index 8f652fe..0000000 --- a/src/core/protocol/proto.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::fmt::Display; - -use bytes::{BufMut, BytesMut}; -use serde::{Deserialize, Serialize}; - -use crate::{Addr, Address, Socket}; - -use super::make_packet; - -pub const MAGIC: u32 = 0xFC; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Packet { - pub magic: u32, - pub data_len: u32, - pub payload: Vec, -} - -#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] -pub enum Connect { - TCP(Option), - UDP(Addr), -} - -#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] -pub enum Bind { - Setup(Socket, Socket), - Success(Address, Address), - Failed(String), -} - -#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] -pub enum Auth { - Auth(Vec), - NoAuth, -} - -#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] -pub enum Poto { - Ping, - Close, - MapError(u32, String), - Bind(Bind), - Map(u32, Socket), - Connect(Connect, Auth), - Forward(Addr), -} - -impl Packet { - pub fn encode(self) -> Vec { - let mut packet = BytesMut::new(); - packet.put_u32(self.magic); - packet.put_u32_le(self.data_len); - packet.put_slice(&self.payload); - packet.to_vec() - } -} - -pub trait ToBytes { - fn bytes(self) -> Vec; -} - -pub trait IntoPacket { - fn into_packet(self) -> Packet; -} - -pub trait TryToPoto { - fn try_poto(self) -> crate::Result; -} - -impl IntoPacket for T -where - T: Serialize, -{ - fn into_packet(self) -> Packet { - let data = unsafe { bincode::serialize(&self).unwrap_unchecked() }; - make_packet(data) - } -} - -impl ToBytes for T -where - T: Serialize, -{ - fn bytes(self) -> Vec { - let data = unsafe { bincode::serialize(&self).unwrap_unchecked() }; - super::make_packet(data).encode() - } -} - -impl TryToPoto for Packet { - fn try_poto(self) -> crate::Result { - bincode::deserialize(&self.payload).map_err(Into::into) - } -} - -impl Display for Poto { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:#?}", self) - } -} diff --git a/src/core/protocol/serde/mod.rs b/src/core/protocol/serde/mod.rs deleted file mode 100644 index 8b13789..0000000 --- a/src/core/protocol/serde/mod.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/core/provider.rs b/src/core/provider.rs deleted file mode 100644 index 6a11317..0000000 --- a/src/core/provider.rs +++ /dev/null @@ -1,177 +0,0 @@ -use std::{pin::Pin, sync::Arc}; - -use crate::Socket; - -type BoxedFuture = Pin> + Send + 'static>>; - -pub struct WrappedProvider { - provider: Arc> + Send + Sync + 'static>>, -} - -pub struct DecorateProvider { - provider: Arc> + Send + Sync + 'static>>, -} - -pub struct ProviderChain { - left: DecorateProvider, - right: DecorateProvider, -} - -pub trait Provider { - type Output; - - fn call(&self, arg: C) -> Self::Output; -} - -pub struct ClientProvider { - pub server_address: Socket, - pub connect_provider: Arc, -} - -impl ClientProvider -where - C: Provider>, - O: Send + 'static, -{ - pub(crate) fn set_server_socket(mut self, socket: Socket) -> Self { - self.server_address = socket; - self - } - - pub(crate) fn default_socket(&self) -> &Socket { - &self.server_address - } - - pub fn connect>(&self, socket: A) -> BoxedFuture { - self.connect_provider.call(socket.into()) - } -} - -impl Provider for ClientProvider -where - C: Provider>, - O: Send + 'static, -{ - type Output = C::Output; - - fn call(&self, arg: Socket) -> Self::Output { - self.connect_provider.call(arg) - } -} - -impl Clone for ClientProvider { - fn clone(&self) -> Self { - Self { - server_address: self.server_address.clone(), - connect_provider: self.connect_provider.clone(), - } - } -} - -impl WrappedProvider { - pub fn wrap(provider: F) -> Self - where - F: Provider> + Send + Sync + 'static, - { - Self { - provider: Arc::new(Box::new(provider)), - } - } -} - -impl DecorateProvider { - pub fn wrap(provider: F) -> Self - where - F: Provider> + Send + Sync + 'static, - { - Self { - provider: Arc::new(Box::new(provider)), - } - } -} - -impl ProviderChain { - pub fn chain(provider: F1, next: F2) -> Self - where - F1: Provider> + Send + Sync + 'static, - F2: Provider> + Send + Sync + 'static, - { - Self { - left: DecorateProvider::wrap(provider), - right: DecorateProvider::wrap(next), - } - } -} - -impl Provider for WrappedProvider -where - A: Send + 'static, - O: Send + 'static, -{ - type Output = BoxedFuture; - - fn call(&self, cfg: A) -> Self::Output { - self.provider.call(cfg) - } -} - -impl Provider for DecorateProvider { - type Output = BoxedFuture; - - fn call(&self, cfg: A) -> Self::Output { - self.provider.call(cfg) - } -} - -impl Provider for ProviderChain -where - A: Send + 'static, -{ - type Output = BoxedFuture; - - fn call(&self, cfg: A) -> Self::Output { - let this = self.clone(); - Box::pin(async move { this.right.call(this.left.call(cfg).await?).await }) - } -} - -impl Provider for Option