made the server and the client/server communication functionality work.
This commit is contained in:
Generated
+334
-2
@@ -142,9 +142,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.102"
|
version = "1.0.103"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayref"
|
name = "arrayref"
|
||||||
@@ -173,6 +173,45 @@ dependencies = [
|
|||||||
"libloading",
|
"libloading",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "asn1-rs"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8"
|
||||||
|
dependencies = [
|
||||||
|
"asn1-rs-derive",
|
||||||
|
"asn1-rs-impl",
|
||||||
|
"displaydoc",
|
||||||
|
"nom",
|
||||||
|
"num-traits",
|
||||||
|
"rusticata-macros",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "asn1-rs-derive"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
"synstructure",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "asn1-rs-impl"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-broadcast"
|
name = "async-broadcast"
|
||||||
version = "0.7.2"
|
version = "0.7.2"
|
||||||
@@ -325,6 +364,29 @@ version = "1.5.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aws-lc-rs"
|
||||||
|
version = "1.17.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00bdb5da18dac48ca2cc7cd4a98e533e8635a58e2361d13a1a4ee3888e0d72f1"
|
||||||
|
dependencies = [
|
||||||
|
"aws-lc-sys",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aws-lc-sys"
|
||||||
|
version = "0.43.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43103168cc76fe62678a375e722fc9cb3a0146159ac5828bc4f0dfd755c2224c"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cmake",
|
||||||
|
"dunce",
|
||||||
|
"fs_extra",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.22.1"
|
version = "0.22.1"
|
||||||
@@ -361,6 +423,15 @@ version = "0.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit-vec"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
@@ -614,6 +685,15 @@ dependencies = [
|
|||||||
"x11rb",
|
"x11rb",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cmake"
|
||||||
|
version = "0.1.58"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codespan-reporting"
|
name = "codespan-reporting"
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
@@ -936,12 +1016,32 @@ dependencies = [
|
|||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "data-encoding"
|
||||||
|
version = "2.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deltae"
|
name = "deltae"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4"
|
checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "der-parser"
|
||||||
|
version = "10.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6"
|
||||||
|
dependencies = [
|
||||||
|
"asn1-rs",
|
||||||
|
"displaydoc",
|
||||||
|
"nom",
|
||||||
|
"num-bigint",
|
||||||
|
"num-traits",
|
||||||
|
"rusticata-macros",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deranged"
|
name = "deranged"
|
||||||
version = "0.5.8"
|
version = "0.5.8"
|
||||||
@@ -999,6 +1099,17 @@ dependencies = [
|
|||||||
"objc2 0.6.4",
|
"objc2 0.6.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "displaydoc"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 3.0.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dlib"
|
name = "dlib"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
@@ -1035,6 +1146,12 @@ version = "0.8.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "edf234dd1594d6dd434a8fb8cada51ddbbc593e40e4a01556a0b31c62da2775b"
|
checksum = "edf234dd1594d6dd434a8fb8cada51ddbbc593e40e4a01556a0b31c62da2775b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dunce"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.16.0"
|
version = "1.16.0"
|
||||||
@@ -2287,6 +2404,16 @@ dependencies = [
|
|||||||
"minimal-lexical",
|
"minimal-lexical",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-bigint"
|
||||||
|
version = "0.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
|
||||||
|
dependencies = [
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-conv"
|
name = "num-conv"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@@ -2304,6 +2431,15 @@ dependencies = [
|
|||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.46"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
@@ -2704,6 +2840,15 @@ dependencies = [
|
|||||||
"objc2-foundation 0.2.2",
|
"objc2-foundation 0.2.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "oid-registry"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7"
|
||||||
|
dependencies = [
|
||||||
|
"asn1-rs",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.21.4"
|
version = "1.21.4"
|
||||||
@@ -2801,6 +2946,16 @@ version = "1.0.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pem"
|
||||||
|
version = "3.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.2"
|
version = "2.3.2"
|
||||||
@@ -3200,6 +3355,20 @@ dependencies = [
|
|||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rcgen"
|
||||||
|
version = "0.14.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57f6d249aad744e274e682777a50283a225a32705394ee6d5fcc01efa25e4055"
|
||||||
|
dependencies = [
|
||||||
|
"pem",
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"time",
|
||||||
|
"x509-parser",
|
||||||
|
"yasna",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "read-fonts"
|
name = "read-fonts"
|
||||||
version = "0.35.0"
|
version = "0.35.0"
|
||||||
@@ -3314,6 +3483,20 @@ dependencies = [
|
|||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ring"
|
||||||
|
version = "0.17.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"getrandom 0.2.17",
|
||||||
|
"libc",
|
||||||
|
"untrusted",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roxmltree"
|
name = "roxmltree"
|
||||||
version = "0.20.0"
|
version = "0.20.0"
|
||||||
@@ -3341,6 +3524,15 @@ dependencies = [
|
|||||||
"semver",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rusticata-macros"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.44"
|
version = "0.38.44"
|
||||||
@@ -3367,6 +3559,51 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.23.43"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06"
|
||||||
|
dependencies = [
|
||||||
|
"aws-lc-rs",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pemfile"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
|
||||||
|
dependencies = [
|
||||||
|
"rustls-pki-types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pki-types"
|
||||||
|
version = "1.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-webpki"
|
||||||
|
version = "0.103.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
|
||||||
|
dependencies = [
|
||||||
|
"aws-lc-rs",
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.22"
|
version = "1.0.22"
|
||||||
@@ -3772,6 +4009,12 @@ dependencies = [
|
|||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "svg_fmt"
|
name = "svg_fmt"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
@@ -3811,6 +4054,28 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "3.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "synstructure"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sys-locale"
|
name = "sys-locale"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@@ -3909,6 +4174,7 @@ dependencies = [
|
|||||||
name = "tetanus"
|
name = "tetanus"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"fs_extra",
|
"fs_extra",
|
||||||
@@ -3916,9 +4182,13 @@ dependencies = [
|
|||||||
"ipnet",
|
"ipnet",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
"rcgen",
|
||||||
"rhai",
|
"rhai",
|
||||||
"rustc-hash 2.1.2",
|
"rustc-hash 2.1.2",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pemfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3978,12 +4248,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
|
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deranged",
|
"deranged",
|
||||||
|
"itoa",
|
||||||
"libc",
|
"libc",
|
||||||
"num-conv",
|
"num-conv",
|
||||||
"num_threads",
|
"num_threads",
|
||||||
"powerfmt",
|
"powerfmt",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
"time-core",
|
"time-core",
|
||||||
|
"time-macros",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3992,6 +4264,16 @@ version = "0.1.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
|
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-macros"
|
||||||
|
version = "0.2.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
|
||||||
|
dependencies = [
|
||||||
|
"num-conv",
|
||||||
|
"time-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tiny-keccak"
|
name = "tiny-keccak"
|
||||||
version = "2.0.2"
|
version = "2.0.2"
|
||||||
@@ -4082,6 +4364,16 @@ dependencies = [
|
|||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-rustls"
|
||||||
|
version = "0.26.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
|
||||||
|
dependencies = [
|
||||||
|
"rustls",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "1.1.1+spec-1.1.0"
|
version = "1.1.1+spec-1.1.0"
|
||||||
@@ -4229,6 +4521,12 @@ version = "0.2.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@@ -5275,6 +5573,24 @@ version = "0.13.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
|
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "x509-parser"
|
||||||
|
version = "0.18.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202"
|
||||||
|
dependencies = [
|
||||||
|
"asn1-rs",
|
||||||
|
"data-encoding",
|
||||||
|
"der-parser",
|
||||||
|
"lazy_static",
|
||||||
|
"nom",
|
||||||
|
"oid-registry",
|
||||||
|
"ring",
|
||||||
|
"rusticata-macros",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"time",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xcursor"
|
name = "xcursor"
|
||||||
version = "0.3.10"
|
version = "0.3.10"
|
||||||
@@ -5306,6 +5622,16 @@ version = "0.8.28"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
|
checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yasna"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5f6765e852b9b4dc8e2a76843e4d64d1cea8e79bcde0b6901aea8e7c7f08282"
|
||||||
|
dependencies = [
|
||||||
|
"bit-vec 0.9.1",
|
||||||
|
"time",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yazi"
|
name = "yazi"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -5399,6 +5725,12 @@ dependencies = [
|
|||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zmij"
|
name = "zmij"
|
||||||
version = "1.0.21"
|
version = "1.0.21"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0.103"
|
||||||
clap = { version = "4.6.1", features = ["derive"] }
|
clap = { version = "4.6.1", features = ["derive"] }
|
||||||
crossterm = "0.29.0"
|
crossterm = "0.29.0"
|
||||||
fs_extra = "1.3.0"
|
fs_extra = "1.3.0"
|
||||||
@@ -11,7 +12,11 @@ iced = { version = "0.14.0", features = ["advanced", "tokio"] }
|
|||||||
ipnet = "2.12.0"
|
ipnet = "2.12.0"
|
||||||
ratatui = "0.30.0"
|
ratatui = "0.30.0"
|
||||||
rayon = "1.12.0"
|
rayon = "1.12.0"
|
||||||
|
rcgen = "0.14.8"
|
||||||
rhai = { version = "1.24.0", features = ["metadata", "sync"] }
|
rhai = { version = "1.24.0", features = ["metadata", "sync"] }
|
||||||
rustc-hash = "2.1.2"
|
rustc-hash = "2.1.2"
|
||||||
|
rustls = "0.23.43"
|
||||||
|
rustls-pemfile = "2.2.0"
|
||||||
tokio = { version = "1.52.3", features = ["full"] }
|
tokio = { version = "1.52.3", features = ["full"] }
|
||||||
|
tokio-rustls = "0.26.4"
|
||||||
walkdir = "2.5.0"
|
walkdir = "2.5.0"
|
||||||
|
|||||||
+140
-203
@@ -1,31 +1,20 @@
|
|||||||
use crate::{ToolMessage::Input, *};
|
use crate::*;
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
cursor,
|
cursor,
|
||||||
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind},
|
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind},
|
||||||
execute,
|
execute,
|
||||||
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
|
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
|
||||||
};
|
};
|
||||||
|
use iced::keyboard::key::Code::Sleep;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
widgets::{Block, Borders, List, ListItem, ListState, Paragraph},
|
widgets::{Block, Borders, List, ListItem, ListState, Paragraph},
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{error::Error, time::Instant};
|
use std::{error::Error, time::Instant};
|
||||||
use std::{
|
use std::{io::Write, usize};
|
||||||
io::{Read, Write},
|
use std::{sync::Arc, thread::sleep};
|
||||||
usize,
|
use std::{sync::Mutex, thread::spawn};
|
||||||
};
|
|
||||||
use std::{net::TcpStream, thread};
|
|
||||||
|
|
||||||
pub enum ServerBrokerCmd {
|
|
||||||
ConnectServer(usize),
|
|
||||||
RegisterActionOutput((Server, String)),
|
|
||||||
Disconnect(Server),
|
|
||||||
DisconnectAll,
|
|
||||||
Exit,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_user_input(prompt: &str) -> Result<String, Box<dyn Error>> {
|
pub fn get_user_input(prompt: &str) -> Result<String, Box<dyn Error>> {
|
||||||
println!("{}", prompt);
|
println!("{}", prompt);
|
||||||
@@ -34,158 +23,6 @@ pub fn get_user_input(prompt: &str) -> Result<String, Box<dyn Error>> {
|
|||||||
return Ok(response.trim().to_string());
|
return Ok(response.trim().to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn server_broker(
|
|
||||||
rx: Receiver<ServerBrokerCmd>,
|
|
||||||
tx: Sender<ToolMessage>,
|
|
||||||
servers: Arc<Mutex<Vec<Server>>>,
|
|
||||||
) {
|
|
||||||
loop {
|
|
||||||
if let Ok(msg) = rx.try_recv() {
|
|
||||||
match msg {
|
|
||||||
ServerBrokerCmd::ConnectServer(id) => {
|
|
||||||
let _ = tx.send(ToolMessage::Output((
|
|
||||||
0,
|
|
||||||
format!("attempting to connect to server {}", id),
|
|
||||||
)));
|
|
||||||
if let Ok(mut lock) = servers.lock() {
|
|
||||||
lock.iter_mut().for_each(|s| {
|
|
||||||
let _ =
|
|
||||||
tx.send(ToolMessage::Output((0, format!("got server list lock!"))));
|
|
||||||
if s.id == id {
|
|
||||||
let _ = tx.send(ToolMessage::Output((
|
|
||||||
0,
|
|
||||||
format!("Found the server for {}: {}", s.id, s.address),
|
|
||||||
)));
|
|
||||||
if let Ok(mut stream) = TcpStream::connect(s.address.clone()) {
|
|
||||||
let _ = tx.send(ToolMessage::Output((
|
|
||||||
0,
|
|
||||||
String::from("TCP successful to server!"),
|
|
||||||
)));
|
|
||||||
let _ = stream.write(format!("HELLO|{}\n", s.id).as_bytes());
|
|
||||||
let _ = tx.send(ToolMessage::Output((
|
|
||||||
0,
|
|
||||||
String::from("Hello message sent!"),
|
|
||||||
)));
|
|
||||||
let mut buf = [0; 1024];
|
|
||||||
if let Ok(bytes_read) = stream.read(&mut buf) {
|
|
||||||
let response = String::from_utf8_lossy(&buf[..bytes_read]);
|
|
||||||
if let Some((_, id)) = response.split_once("|") {
|
|
||||||
if let Ok(id) = id.trim().parse::<usize>() {
|
|
||||||
s.client_id = id;
|
|
||||||
s.connected = true;
|
|
||||||
let _ = tx.send(ToolMessage::Output((
|
|
||||||
0,
|
|
||||||
String::from("got client ID from server!"),
|
|
||||||
)));
|
|
||||||
} else {
|
|
||||||
let _ = tx.send(ToolMessage::Output((
|
|
||||||
0,
|
|
||||||
String::from(
|
|
||||||
"failed to get response from server!",
|
|
||||||
),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ServerBrokerCmd::Disconnect(server) => {
|
|
||||||
if let Ok(mut lock) = servers.lock() {
|
|
||||||
let mut rid = 0;
|
|
||||||
let mut found = false;
|
|
||||||
lock.iter().enumerate().for_each(|(id, s)| {
|
|
||||||
if s.id == server.id {
|
|
||||||
rid = id;
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if found {
|
|
||||||
lock.remove(rid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ServerBrokerCmd::DisconnectAll => {
|
|
||||||
println!("todo");
|
|
||||||
}
|
|
||||||
ServerBrokerCmd::Exit => {
|
|
||||||
println!("todo");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ServerBrokerCmd::RegisterActionOutput(text) => {
|
|
||||||
if let Ok(mut lock) = servers.lock() {
|
|
||||||
lock.iter_mut().for_each(|s| {
|
|
||||||
if s.id == text.0.id {
|
|
||||||
s.message_que.push(text.1.clone());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Ok(mut lock) = servers.try_lock() {
|
|
||||||
lock.iter_mut().for_each(|s| {
|
|
||||||
if s.connected {
|
|
||||||
if Instant::now().duration_since(s.last_check) >= s.timer {
|
|
||||||
if let Ok(mut stream) = TcpStream::connect(s.address.clone()) {
|
|
||||||
s.last_check = Instant::now();
|
|
||||||
let mut success_ids = Vec::new();
|
|
||||||
if s.message_que.len() > 0 {
|
|
||||||
s.message_que.iter().enumerate().for_each(|(id, msg)| {
|
|
||||||
if let Ok(_) = stream.write(
|
|
||||||
format!("{}|{}\n", s.client_id.clone(), msg).as_bytes(),
|
|
||||||
) {
|
|
||||||
success_ids.push(id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
let _ = stream.write(format!("{}|PING", s.client_id).as_bytes());
|
|
||||||
}
|
|
||||||
for id in success_ids {
|
|
||||||
s.message_que.remove(id);
|
|
||||||
}
|
|
||||||
let mut buffer = [0; 1024];
|
|
||||||
if let Ok(bytes_read) = stream.read(&mut buffer) {
|
|
||||||
let response =
|
|
||||||
String::from_utf8_lossy(&buffer[..bytes_read]).to_string();
|
|
||||||
let (_, action_string) =
|
|
||||||
response.split_once("|").unwrap_or(("ACTIONS", "NONE"));
|
|
||||||
let actions: Vec<&str> = action_string.split(",").collect();
|
|
||||||
for action in actions {
|
|
||||||
let (action, data) =
|
|
||||||
action.split_once("|").unwrap_or(("PING", "NONE"));
|
|
||||||
match action {
|
|
||||||
"OUTPUT" => {
|
|
||||||
let _ = tx.send(ToolMessage::Output((
|
|
||||||
0,
|
|
||||||
format!("FROM SERVER: {}", data),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
"CMD" => {
|
|
||||||
let (rid_str, cmd) =
|
|
||||||
data.split_once("|").unwrap_or(("0", "NONE"));
|
|
||||||
if let Ok(rid) = rid_str.parse::<usize>() {
|
|
||||||
let _ = tx.send(ToolMessage::Input((
|
|
||||||
rid,
|
|
||||||
cmd.to_string(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
thread::sleep(Duration::from_millis(10));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_tui(
|
pub fn run_tui(
|
||||||
mut state: AppState,
|
mut state: AppState,
|
||||||
main_rx: Receiver<ToolMessage>,
|
main_rx: Receiver<ToolMessage>,
|
||||||
@@ -198,12 +35,6 @@ pub fn run_tui(
|
|||||||
cursor::Hide,
|
cursor::Hide,
|
||||||
EnableMouseCapture
|
EnableMouseCapture
|
||||||
)?;
|
)?;
|
||||||
let (server_tx, server_rx) = channel();
|
|
||||||
let main_tx = state.main_tx.clone();
|
|
||||||
let server_clone = state.servers.clone();
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
server_broker(server_rx, main_tx, server_clone);
|
|
||||||
});
|
|
||||||
let backend = CrosstermBackend::new(&stdout);
|
let backend = CrosstermBackend::new(&stdout);
|
||||||
let mut terminal = Terminal::new(backend)?;
|
let mut terminal = Terminal::new(backend)?;
|
||||||
let (event_tx, event_rx) = channel::<AppEvent>();
|
let (event_tx, event_rx) = channel::<AppEvent>();
|
||||||
@@ -250,6 +81,7 @@ pub fn run_tui(
|
|||||||
project_list_state.select(Some(0));
|
project_list_state.select(Some(0));
|
||||||
}
|
}
|
||||||
let mut history_index = state.history.len();
|
let mut history_index = state.history.len();
|
||||||
|
let mut handles = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
terminal.draw(|f| {
|
terminal.draw(|f| {
|
||||||
let main_chunks = Layout::default()
|
let main_chunks = Layout::default()
|
||||||
@@ -402,17 +234,22 @@ pub fn run_tui(
|
|||||||
}
|
}
|
||||||
ToolMessage::Input(cmd) => {
|
ToolMessage::Input(cmd) => {
|
||||||
state.log.push(cmd.1.clone());
|
state.log.push(cmd.1.clone());
|
||||||
|
if cmd.1.contains(" ") {
|
||||||
|
let (gcmd, args) = cmd.1.split_once(" ").unwrap();
|
||||||
|
state.execute_command(gcmd, Some(args.to_string()), cmd.0)?;
|
||||||
|
} else {
|
||||||
|
state.execute_command(cmd.1.as_str(), None, cmd.0)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ToolMessage::Output((rid, txt)) => {
|
ToolMessage::Output((rid, txt)) => {
|
||||||
state.log.push(txt.clone());
|
state.log.push(txt.clone());
|
||||||
state.output.push(txt.clone());
|
state.output.push(txt.clone());
|
||||||
state.output_scroll = u16::MAX;
|
state.output_scroll = u16::MAX;
|
||||||
if rid != 0 {
|
if rid != 0 {
|
||||||
if let Some(server) = state.selected_server.clone() {
|
if let Some(server) = state.servers.get(state.selected_server) {
|
||||||
let _ = server_tx.send(ServerBrokerCmd::RegisterActionOutput((
|
if let Ok(mut server) = server.lock() {
|
||||||
server,
|
server.message_que.push(format!("OUTPUT|{}", txt));
|
||||||
format!("OUTPUT|{}|{}", rid, txt),
|
}
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -482,12 +319,121 @@ pub fn run_tui(
|
|||||||
terminal.clear()?;
|
terminal.clear()?;
|
||||||
}
|
}
|
||||||
ToolMessage::ConnectServer => {
|
ToolMessage::ConnectServer => {
|
||||||
if let Some(server) = state.selected_server.clone() {
|
if let Some(server) = state.servers.get_mut(state.selected_server) {
|
||||||
let _ = server_tx.send(ServerBrokerCmd::ConnectServer(server.id));
|
let mut address = String::new();
|
||||||
|
let lock_res = server.try_lock();
|
||||||
|
let mut connected = false;
|
||||||
|
match lock_res {
|
||||||
|
Ok(mut locked_server) => match locked_server.connect() {
|
||||||
|
Ok(id) => {
|
||||||
|
state.output.push(format!(
|
||||||
|
"Server Connected! ID:{} ADDRESS:{}",
|
||||||
|
id,
|
||||||
|
locked_server.address.clone()
|
||||||
|
));
|
||||||
|
address = locked_server.address.clone();
|
||||||
|
connected = true;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
state
|
||||||
|
.output
|
||||||
|
.push(format!("Error connecting to server {e}"));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
state
|
||||||
|
.output
|
||||||
|
.push(format!("Error locking server state! {e}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if connected {
|
||||||
|
let server_clone = server.clone();
|
||||||
|
let tx_clone = state.main_tx.clone();
|
||||||
|
let mut timer = Duration::from_secs(5);
|
||||||
|
let mut last_check = Instant::now();
|
||||||
|
let server_handle = spawn(move || {
|
||||||
|
let server = server_clone;
|
||||||
|
loop {
|
||||||
|
let mut stop = false;
|
||||||
|
if Instant::now().duration_since(last_check) >= timer {
|
||||||
|
let lock_res = server.try_lock();
|
||||||
|
match lock_res {
|
||||||
|
Ok(mut locked_server) => {
|
||||||
|
locked_server.checkin();
|
||||||
|
last_check = locked_server.last_check.clone();
|
||||||
|
for action in locked_server.action_que.clone() {
|
||||||
|
if action.contains("|") {
|
||||||
|
let (action, data) =
|
||||||
|
action.split_once("|").unwrap();
|
||||||
|
match action.trim() {
|
||||||
|
"OUTPUT" => {
|
||||||
|
let _ = tx_clone.send(
|
||||||
|
ToolMessage::Output((
|
||||||
|
0,
|
||||||
|
data.trim().to_string(),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"CMD" => {
|
||||||
|
let _ = tx_clone.send(
|
||||||
|
ToolMessage::Input((
|
||||||
|
locked_server
|
||||||
|
.client_id
|
||||||
|
.clone(),
|
||||||
|
data.trim().to_string(),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match action.as_str() {
|
||||||
|
"disconnect" => {
|
||||||
|
locked_server.connected = false;
|
||||||
|
stop = true;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let _ = tx_clone.send(
|
||||||
|
ToolMessage::Output((
|
||||||
|
0,
|
||||||
|
action.clone(),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timer = locked_server.timer.clone();
|
||||||
|
locked_server.action_que.clear();
|
||||||
|
if locked_server.message_que.is_empty() {
|
||||||
|
let id = locked_server.client_id.clone();
|
||||||
|
locked_server
|
||||||
|
.message_que
|
||||||
|
.push(format!("{}|||PING\n", id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if stop {
|
||||||
|
println!("{} disconnected!", address);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sleep(Duration::from_secs(1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
handles.push(server_handle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToolMessage::SendServer(data) => {
|
ToolMessage::SendServer(data) => {
|
||||||
let _ = server_tx.send(ServerBrokerCmd::RegisterActionOutput(data));
|
if let Some(server) = state.servers.get(state.selected_server) {
|
||||||
|
if let Ok(mut server) = server.lock() {
|
||||||
|
server
|
||||||
|
.message_que
|
||||||
|
.push(format!("{}|||{}\n", data.0, data.1));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ToolMessage::DisconnectAllServers => {
|
ToolMessage::DisconnectAllServers => {
|
||||||
println!("todo");
|
println!("todo");
|
||||||
@@ -530,22 +476,6 @@ pub fn run_tui(
|
|||||||
}
|
}
|
||||||
let (command, args) =
|
let (command, args) =
|
||||||
trimmed.split_once(' ').unwrap_or((&trimmed, ""));
|
trimmed.split_once(' ').unwrap_or((&trimmed, ""));
|
||||||
if state.selected_client != 0 {
|
|
||||||
if let Some(server) = state.selected_server.clone() {
|
|
||||||
let _ = server_tx.send(
|
|
||||||
ServerBrokerCmd::RegisterActionOutput((
|
|
||||||
server.clone(),
|
|
||||||
format!(
|
|
||||||
"CMD|{}|{}|{} {}",
|
|
||||||
server.client_id,
|
|
||||||
state.selected_client,
|
|
||||||
command,
|
|
||||||
args
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match command {
|
match command {
|
||||||
"exit" | "quit" => break,
|
"exit" | "quit" => break,
|
||||||
"reload-modules" => {
|
"reload-modules" => {
|
||||||
@@ -582,10 +512,9 @@ pub fn run_tui(
|
|||||||
Some(args.to_string()),
|
Some(args.to_string()),
|
||||||
0,
|
0,
|
||||||
) {
|
) {
|
||||||
state.output.push(format!(
|
state
|
||||||
"[Error] Pipeline fail: {}",
|
.output
|
||||||
e
|
.push(format!("[Error] Pipeline fail: {}", e));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if args == "" {
|
if args == "" {
|
||||||
@@ -600,7 +529,6 @@ pub fn run_tui(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
state.curent_intput.clear();
|
state.curent_intput.clear();
|
||||||
}
|
}
|
||||||
history_index = state.history.len();
|
history_index = state.history.len();
|
||||||
@@ -700,5 +628,14 @@ pub fn run_tui(
|
|||||||
cursor::Show,
|
cursor::Show,
|
||||||
DisableMouseCapture
|
DisableMouseCapture
|
||||||
)?;
|
)?;
|
||||||
|
println!("disconnecting from servers...");
|
||||||
|
state.servers.iter().for_each(|server| {
|
||||||
|
if let Ok(mut server_lock) = server.lock() {
|
||||||
|
server_lock.action_que.push("disconnect".to_string());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for handle in handles {
|
||||||
|
handle.join().unwrap();
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ pub fn client_install() -> Result<(), Box<dyn Error>> {
|
|||||||
let term_cmd = get_user_input(
|
let term_cmd = get_user_input(
|
||||||
"for example: konsole -e |||COMMAND||| or konsole --profile profile_name ENV_NAME=CURRENT_PROJECT_BOX",
|
"for example: konsole -e |||COMMAND||| or konsole --profile profile_name ENV_NAME=CURRENT_PROJECT_BOX",
|
||||||
)?;
|
)?;
|
||||||
|
let tools_folder =
|
||||||
|
get_user_input("path where your tools are installed ex /opt or /home/user/tools")?;
|
||||||
config_path.push("client.conf");
|
config_path.push("client.conf");
|
||||||
projects_path.push("default");
|
projects_path.push("default");
|
||||||
create_dir_all(&projects_path)?;
|
create_dir_all(&projects_path)?;
|
||||||
@@ -100,6 +102,7 @@ pub fn client_install() -> Result<(), Box<dyn Error>> {
|
|||||||
client_config_file.write(format!("module_path: {}\n", module_path.display()).as_bytes())?;
|
client_config_file.write(format!("module_path: {}\n", module_path.display()).as_bytes())?;
|
||||||
client_config_file.write(format!("template_box: {}\n", template_box).as_bytes())?;
|
client_config_file.write(format!("template_box: {}\n", template_box).as_bytes())?;
|
||||||
client_config_file.write(format!("term_cmd: {}\n", term_cmd).as_bytes())?;
|
client_config_file.write(format!("term_cmd: {}\n", term_cmd).as_bytes())?;
|
||||||
|
client_config_file.write(format!("tools: {}\n", tools_folder).as_bytes())?;
|
||||||
println!("\ndefault config files written!");
|
println!("\ndefault config files written!");
|
||||||
println!("downloading default notes and modules...");
|
println!("downloading default notes and modules...");
|
||||||
create_dir_all("./temp")?;
|
create_dir_all("./temp")?;
|
||||||
|
|||||||
+250
-140
@@ -2,27 +2,72 @@ use fs_extra::dir::{CopyOptions, copy};
|
|||||||
use ipnet::IpNet;
|
use ipnet::IpNet;
|
||||||
use ratatui::crossterm::event;
|
use ratatui::crossterm::event;
|
||||||
use rhai::{AST, Dynamic, Engine, Scope};
|
use rhai::{AST, Dynamic, Engine, Scope};
|
||||||
|
use rustls::{ClientConfig, ClientConnection, RootCertStore, StreamOwned, pki_types::ServerName};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs::{self, File, create_dir_all, read_dir, read_to_string, remove_dir, remove_dir_all};
|
use std::fs::{self, File, create_dir_all, read_dir, read_to_string, remove_dir, remove_dir_all};
|
||||||
use std::io::{self, BufRead, BufReader, Write};
|
use std::io::{self, BufRead, BufReader, Read, Write};
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
use std::net::TcpStream;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::Receiver;
|
||||||
use std::sync::{Arc, Mutex, mpsc::Sender, mpsc::channel};
|
use std::sync::{Arc, Mutex, mpsc::Sender, mpsc::channel};
|
||||||
|
|
||||||
use std::time;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
use std::time::{self, Instant};
|
||||||
use tokio::net::TcpStream;
|
|
||||||
|
|
||||||
pub mod funcs;
|
pub mod funcs;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
|
||||||
|
enum AppEvent {
|
||||||
|
Key(event::KeyEvent),
|
||||||
|
Worker(ToolMessage),
|
||||||
|
Mouse(event::MouseEvent),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Destination {
|
||||||
|
Server,
|
||||||
|
Victim,
|
||||||
|
Attacker,
|
||||||
|
Control,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum ToolMessage {
|
||||||
|
Input((usize, String)),
|
||||||
|
Output((usize, String)),
|
||||||
|
UpdateHost(Host),
|
||||||
|
RebuildDB,
|
||||||
|
DestroyDB(DistroBox),
|
||||||
|
StopDB(DistroBox),
|
||||||
|
StopTemplate(DistroBox),
|
||||||
|
UpdateProject(usize, Project),
|
||||||
|
RemoveProject,
|
||||||
|
AddServer,
|
||||||
|
EndPrompt,
|
||||||
|
ConnectServer,
|
||||||
|
SelectServer,
|
||||||
|
SendServer((usize, String)),
|
||||||
|
ServerBrokerExit,
|
||||||
|
AppStateExit,
|
||||||
|
DisconnectServer,
|
||||||
|
DisconnectAllServers,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum ToolArg {
|
||||||
|
Project(Project),
|
||||||
|
Projects(Vec<Project>),
|
||||||
|
Host(Host),
|
||||||
|
Hosts(Vec<Host>),
|
||||||
|
Config(HashMap<String, String>),
|
||||||
|
Path(PathBuf),
|
||||||
|
}
|
||||||
|
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub projects: Vec<Project>,
|
pub projects: Vec<Project>,
|
||||||
pub servers: Arc<Mutex<Vec<Server>>>,
|
pub servers: Vec<Arc<Mutex<Server>>>,
|
||||||
pub config: HashMap<String, String>,
|
pub config: HashMap<String, String>,
|
||||||
pub config_file: PathBuf,
|
pub config_file: PathBuf,
|
||||||
pub workers: rayon::ThreadPool,
|
pub workers: rayon::ThreadPool,
|
||||||
@@ -31,9 +76,8 @@ pub struct AppState {
|
|||||||
pub history: Vec<String>,
|
pub history: Vec<String>,
|
||||||
pub log: Vec<String>,
|
pub log: Vec<String>,
|
||||||
pub output: Vec<String>,
|
pub output: Vec<String>,
|
||||||
pub selected_server: Option<Server>,
|
pub selected_server: usize,
|
||||||
pub selected_project: usize,
|
pub selected_project: usize,
|
||||||
pub selected_client: usize,
|
|
||||||
pub curent_intput: String,
|
pub curent_intput: String,
|
||||||
pub module_loader: ModuleLoader,
|
pub module_loader: ModuleLoader,
|
||||||
pub output_scroll: u16,
|
pub output_scroll: u16,
|
||||||
@@ -51,7 +95,7 @@ impl AppState {
|
|||||||
(
|
(
|
||||||
Self {
|
Self {
|
||||||
projects: Vec::new(),
|
projects: Vec::new(),
|
||||||
servers: Arc::new(Mutex::new(Vec::new())),
|
servers: Vec::new(),
|
||||||
config: HashMap::new(),
|
config: HashMap::new(),
|
||||||
config_file: PathBuf::new(),
|
config_file: PathBuf::new(),
|
||||||
workers: rayon::ThreadPoolBuilder::new()
|
workers: rayon::ThreadPoolBuilder::new()
|
||||||
@@ -63,9 +107,8 @@ impl AppState {
|
|||||||
history: Vec::new(),
|
history: Vec::new(),
|
||||||
log: Vec::new(),
|
log: Vec::new(),
|
||||||
output: Vec::new(),
|
output: Vec::new(),
|
||||||
selected_server: None,
|
selected_server: 0,
|
||||||
selected_project: 0,
|
selected_project: 0,
|
||||||
selected_client: 0,
|
|
||||||
curent_intput: String::new(),
|
curent_intput: String::new(),
|
||||||
module_loader: ModuleLoader::new(),
|
module_loader: ModuleLoader::new(),
|
||||||
output_scroll: 0,
|
output_scroll: 0,
|
||||||
@@ -100,14 +143,14 @@ impl AppState {
|
|||||||
address: address.trim().to_string(),
|
address: address.trim().to_string(),
|
||||||
connected: false,
|
connected: false,
|
||||||
timer: Duration::from_secs(60),
|
timer: Duration::from_secs(60),
|
||||||
id: 0,
|
|
||||||
last_check: time::Instant::now(),
|
last_check: time::Instant::now(),
|
||||||
message_que: Vec::new(),
|
message_que: Vec::new(),
|
||||||
|
action_que: Vec::new(),
|
||||||
client_id: 0,
|
client_id: 0,
|
||||||
|
selected_client: 0,
|
||||||
|
config: None,
|
||||||
};
|
};
|
||||||
if let Ok(mut lock) = self.servers.lock() {
|
self.servers.push(Arc::new(Mutex::new(new_server)));
|
||||||
lock.push(new_server);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.config
|
self.config
|
||||||
.insert("servers".to_string(), line.trim().to_string());
|
.insert("servers".to_string(), line.trim().to_string());
|
||||||
@@ -223,6 +266,27 @@ impl AppState {
|
|||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), Box<dyn Error>> {
|
||||||
let tx = self.main_tx.clone();
|
let tx = self.main_tx.clone();
|
||||||
match command_name {
|
match command_name {
|
||||||
|
"remote" => {
|
||||||
|
if let Some(server) = self.servers.get(self.selected_server) {
|
||||||
|
if let Ok(mut server) = server.lock() {
|
||||||
|
if server.selected_client != 0 {
|
||||||
|
if let Some(args) = command_args.clone() {
|
||||||
|
let args = args.trim();
|
||||||
|
if args.contains(" ") {
|
||||||
|
let (cmd, iargs) = args.split_once(" ").unwrap();
|
||||||
|
server.message_que.push(format!("CMD|{} {}", cmd, iargs));
|
||||||
|
} else {
|
||||||
|
server.message_que.push(format!("CMD|{}", args));
|
||||||
|
}
|
||||||
|
let _ = self.main_tx.send(ToolMessage::Output((
|
||||||
|
0,
|
||||||
|
format!("Tasked {} to run {}", server.selected_client, args),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
"new_project" | "np" => {
|
"new_project" | "np" => {
|
||||||
let args = command_args.unwrap();
|
let args = command_args.unwrap();
|
||||||
let (org, name) = args.split_once(" ").unwrap();
|
let (org, name) = args.split_once(" ").unwrap();
|
||||||
@@ -407,17 +471,14 @@ impl AppState {
|
|||||||
address: format!("{}:{}", self.prompt.responses[0], self.prompt.responses[1]),
|
address: format!("{}:{}", self.prompt.responses[0], self.prompt.responses[1]),
|
||||||
connected: false,
|
connected: false,
|
||||||
timer: Duration::from_secs(5),
|
timer: Duration::from_secs(5),
|
||||||
id: 0,
|
|
||||||
last_check: time::Instant::now(),
|
last_check: time::Instant::now(),
|
||||||
message_que: Vec::new(),
|
message_que: Vec::new(),
|
||||||
|
action_que: Vec::new(),
|
||||||
client_id: 0,
|
client_id: 0,
|
||||||
|
selected_client: 0,
|
||||||
|
config: None,
|
||||||
};
|
};
|
||||||
if let Ok(mut lock) = self.servers.lock() {
|
self.servers.push(Arc::new(Mutex::new(new_server)));
|
||||||
if lock.len() == 0 {
|
|
||||||
self.selected_server = Some(new_server.clone());
|
|
||||||
}
|
|
||||||
lock.push(new_server);
|
|
||||||
}
|
|
||||||
self.prompt.reset();
|
self.prompt.reset();
|
||||||
}
|
}
|
||||||
"connect_server" => {
|
"connect_server" => {
|
||||||
@@ -440,72 +501,65 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"list_servers" => {
|
"list_servers" => {
|
||||||
let mut selected_addr = String::new();
|
self.servers.iter().enumerate().for_each(|(id, m)| {
|
||||||
if let Some(selected) = self.selected_server.clone() {
|
if let Ok(s) = m.lock() {
|
||||||
selected_addr = selected.address.clone();
|
if id == self.selected_server {
|
||||||
}
|
|
||||||
if let Ok(lock) = self.servers.lock() {
|
|
||||||
lock.iter().for_each(|s| {
|
|
||||||
if selected_addr == s.address {
|
|
||||||
let _ = self.main_tx.send(ToolMessage::Output((
|
let _ = self.main_tx.send(ToolMessage::Output((
|
||||||
rid,
|
rid,
|
||||||
format!("{} - Currently Selected", s.address),
|
format!(
|
||||||
|
"Server ID:{} Address:{} ClientID:{} Sleep:{} Last:{} - Currently Selected",
|
||||||
|
id, s.address, s.client_id, s.timer.as_secs(), s.last_check.elapsed().as_secs()
|
||||||
|
),
|
||||||
)));
|
)));
|
||||||
} else {
|
} else {
|
||||||
let _ = self
|
let _ = self.main_tx.send(ToolMessage::Output((
|
||||||
.main_tx
|
rid,
|
||||||
.send(ToolMessage::Output((rid, s.address.clone())));
|
format!(
|
||||||
|
"Server ID:{} Address:{} ClientID:{} Sleep:{} Last:{}",
|
||||||
|
id, s.address, s.client_id, s.timer.as_secs(), s.last_check.elapsed().as_secs()
|
||||||
|
),
|
||||||
|
)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"select_server" => {
|
"select_server" => {
|
||||||
self.prompt.action = Some(ToolMessage::SelectServer);
|
self.prompt.action = Some(ToolMessage::SelectServer);
|
||||||
self.prompt.num_responses = 1;
|
self.prompt.num_responses = 1;
|
||||||
self.prompt.prompts.push(String::from("Selection?"));
|
self.prompt.prompts.push(String::from("Selection?"));
|
||||||
self.prompt.execute_command = String::from("server_selected");
|
self.prompt.execute_command = String::from("server_selected");
|
||||||
let mut counter: usize = 0;
|
self.servers.iter().enumerate().for_each(|(id, servermut)| {
|
||||||
let mut selected_addr = String::new();
|
if let Ok(server) = servermut.lock() {
|
||||||
if let Some(selected) = self.selected_server.clone() {
|
if id == self.selected_server {
|
||||||
selected_addr = selected.address.clone();
|
|
||||||
}
|
|
||||||
if let Ok(lock) = self.servers.lock() {
|
|
||||||
lock.iter().for_each(|server| {
|
|
||||||
if server.address == selected_addr {
|
|
||||||
let _ = self.main_tx.send(ToolMessage::Output((
|
let _ = self.main_tx.send(ToolMessage::Output((
|
||||||
rid,
|
rid,
|
||||||
format!(
|
format!("{}:{} - Currently Selected", id, server.address.clone()),
|
||||||
"{}:{} - Currently Selected",
|
|
||||||
counter,
|
|
||||||
server.address.clone()
|
|
||||||
),
|
|
||||||
)));
|
)));
|
||||||
} else {
|
} else {
|
||||||
let _ = self.main_tx.send(ToolMessage::Output((
|
let _ = self.main_tx.send(ToolMessage::Output((
|
||||||
rid,
|
rid,
|
||||||
format!("{}: {}", counter, server.address.clone()),
|
format!("{}: {}", id, server.address.clone()),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
counter += 1;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
"server_selected" => {
|
"server_selected" => {
|
||||||
if self.prompt.responses.len() == 1 {
|
if self.prompt.responses.len() == 1 {
|
||||||
if let Ok(lock) = self.servers.lock() {
|
|
||||||
if let Ok(id) = self.prompt.responses[0].parse::<usize>() {
|
if let Ok(id) = self.prompt.responses[0].parse::<usize>() {
|
||||||
self.selected_server = Some(lock[id].clone());
|
self.selected_server = id;
|
||||||
|
if let Ok(server) = self.servers[id].lock() {
|
||||||
let _ = self.main_tx.send(ToolMessage::Output((
|
let _ = self.main_tx.send(ToolMessage::Output((
|
||||||
rid,
|
rid,
|
||||||
format!("{} Selected!", lock[id].address),
|
format!("{} Selected!", server.address),
|
||||||
)));
|
)));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let _ = self.main_tx.send(ToolMessage::Output((
|
let _ = self.main_tx.send(ToolMessage::Output((
|
||||||
rid,
|
rid,
|
||||||
String::from("Invalid selection."),
|
String::from("Invalid selection."),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let _ = self.main_tx.send(ToolMessage::Output((
|
let _ = self.main_tx.send(ToolMessage::Output((
|
||||||
rid,
|
rid,
|
||||||
@@ -514,47 +568,82 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
let _ = self.main_tx.send(ToolMessage::EndPrompt);
|
let _ = self.main_tx.send(ToolMessage::EndPrompt);
|
||||||
}
|
}
|
||||||
|
"test_server" => {
|
||||||
|
if let Some(server) = self.servers.get(self.selected_server) {
|
||||||
|
if let Ok(mut server) = server.lock() {
|
||||||
|
server.message_que.push("TEST|NONE".to_string());
|
||||||
|
} else {
|
||||||
|
self.output.push(String::from("error getting server lock!"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.output
|
||||||
|
.push(String::from("Error getting selected server!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"server_sleep" => {
|
||||||
|
if let Some(args) = command_args {
|
||||||
|
if let Ok(seconds) = args.trim().parse::<u64>() {
|
||||||
|
if let Some(server) = self.servers.get(self.selected_server) {
|
||||||
|
if let Ok(mut server) = server.lock() {
|
||||||
|
server.timer = Duration::from_secs(seconds);
|
||||||
|
self.output
|
||||||
|
.push(format!("Server timer set to {} seconds", seconds));
|
||||||
|
} else {
|
||||||
|
self.output
|
||||||
|
.push(String::from("error locking server object!"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.output
|
||||||
|
.push(String::from("error not a valid server selected!"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.output
|
||||||
|
.push(String::from("invalid second count provided!"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.output.push(String::from(
|
||||||
|
"Error no seconds provided! please use server_sleep number_of_seconds",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
"save_all" => {
|
"save_all" => {
|
||||||
self.save_all()?;
|
self.save_all()?;
|
||||||
}
|
}
|
||||||
"save_projects" => {
|
"save_projects" => {
|
||||||
self.save_projects()?;
|
self.save_projects()?;
|
||||||
}
|
}
|
||||||
"test_server" => {
|
|
||||||
if let Some(server) = self.selected_server.clone() {
|
|
||||||
let _ = self
|
|
||||||
.main_tx
|
|
||||||
.send(ToolMessage::SendServer((server, "TEST".to_string())));
|
|
||||||
} else {
|
|
||||||
let _ = self.main_tx.send(ToolMessage::Output((
|
|
||||||
rid,
|
|
||||||
"You need to select a server first!".to_string(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"list_clients" => {
|
"list_clients" => {
|
||||||
if let Some(server) = self.selected_server.clone() {
|
if let Some(server) = self.servers.get(self.selected_server) {
|
||||||
let _ = self.main_tx.send(ToolMessage::SendServer((
|
if let Ok(mut server) = server.lock() {
|
||||||
server,
|
server.message_que.push("LIST_CLIENTS|NONE".to_string());
|
||||||
"LIST_CLIENTS".to_string(),
|
|
||||||
)));
|
|
||||||
} else {
|
} else {
|
||||||
let _ = self.main_tx.send(ToolMessage::Output((
|
self.output.push(String::from("error getting server lock!"));
|
||||||
rid,
|
}
|
||||||
"You need to select a server first!".to_string(),
|
} else {
|
||||||
)));
|
self.output
|
||||||
|
.push(String::from("Error getting selected server!"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"select_client" => {
|
"select_client" => {
|
||||||
if let Some(arg) = command_args {
|
if let Some(arg) = command_args {
|
||||||
|
if let Some(server) = self.servers.get(self.selected_server) {
|
||||||
|
if let Ok(mut server) = server.lock() {
|
||||||
if let Ok(id) = arg.parse::<usize>() {
|
if let Ok(id) = arg.parse::<usize>() {
|
||||||
self.selected_client = id;
|
server.message_que.push(format!("STOP_CONTROL"));
|
||||||
|
server.message_que.push(format!("CONTROL|{}", id.clone()));
|
||||||
|
server.selected_client = id;
|
||||||
|
let _ = self.main_tx.send(ToolMessage::Output((
|
||||||
|
0,
|
||||||
|
format!("client {} selected!", id),
|
||||||
|
)));
|
||||||
} else {
|
} else {
|
||||||
let _ = self.main_tx.send(ToolMessage::Output((
|
let _ = self.main_tx.send(ToolMessage::Output((
|
||||||
rid,
|
rid,
|
||||||
format!("error parsing client id from arg: {}", arg),
|
format!("error parsing client id from arg: {}", arg),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let _ = self.main_tx.send(ToolMessage::Output((
|
let _ = self.main_tx.send(ToolMessage::Output((
|
||||||
rid,
|
rid,
|
||||||
@@ -562,6 +651,17 @@ impl AppState {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"deselect_client" => {
|
||||||
|
if let Some(server) = self.servers.get(self.selected_server) {
|
||||||
|
if let Ok(mut server) = server.lock() {
|
||||||
|
server.message_que.push(format!("STOP_CONTROL"));
|
||||||
|
server.selected_client = 0;
|
||||||
|
let _ = self
|
||||||
|
.main_tx
|
||||||
|
.send(ToolMessage::Output((0, format!("client deselected!"))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
"add_scope" => {
|
"add_scope" => {
|
||||||
if let Some(args) = command_args {
|
if let Some(args) = command_args {
|
||||||
args.split_whitespace().into_iter().for_each(|host| {
|
args.split_whitespace().into_iter().for_each(|host| {
|
||||||
@@ -902,17 +1002,17 @@ impl AppState {
|
|||||||
),
|
),
|
||||||
connected: false,
|
connected: false,
|
||||||
timer: Duration::from_secs(60),
|
timer: Duration::from_secs(60),
|
||||||
id: 0,
|
|
||||||
last_check: time::Instant::now(),
|
last_check: time::Instant::now(),
|
||||||
message_que: Vec::new(),
|
message_que: Vec::new(),
|
||||||
|
action_que: Vec::new(),
|
||||||
client_id: 0,
|
client_id: 0,
|
||||||
|
selected_client: 0,
|
||||||
|
config: None,
|
||||||
};
|
};
|
||||||
if let Ok(mut lock) = self.servers.lock() {
|
self.servers.push(Arc::new(Mutex::new(new_server)));
|
||||||
lock.push(new_server);
|
|
||||||
self.prompt.action = None;
|
self.prompt.action = None;
|
||||||
self.prompt.responses.clear();
|
self.prompt.responses.clear();
|
||||||
self.prompt.num_responses = 0;
|
self.prompt.num_responses = 0;
|
||||||
}
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else {
|
} else {
|
||||||
match prompt.responses.len() {
|
match prompt.responses.len() {
|
||||||
@@ -940,34 +1040,90 @@ pub struct Server {
|
|||||||
pub address: String,
|
pub address: String,
|
||||||
pub connected: bool,
|
pub connected: bool,
|
||||||
pub timer: Duration,
|
pub timer: Duration,
|
||||||
pub id: usize,
|
pub config: Option<Arc<ClientConfig>>,
|
||||||
pub last_check: time::Instant,
|
pub last_check: time::Instant,
|
||||||
pub message_que: Vec<String>,
|
pub message_que: Vec<String>,
|
||||||
|
pub action_que: Vec<String>,
|
||||||
pub client_id: usize,
|
pub client_id: usize,
|
||||||
|
pub selected_client: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
pub async fn checkin(&mut self, outputs: Vec<String>) -> (String, String) {
|
pub fn donwload_certificate(&self) -> Result<String, Box<dyn Error>> {
|
||||||
let mut out_string = (String::new(), String::new());
|
let mut stream = TcpStream::connect(self.address.clone())?;
|
||||||
if let Ok(mut stream) = TcpStream::connect(self.address.clone()).await {
|
stream.write_all("CERT_REQ".as_bytes())?;
|
||||||
let payload = format!("{}:{}\n", self.id.clone(), outputs.join(":"));
|
let mut buf = [0u8; 8192];
|
||||||
if let Err(e) = stream.write_all(payload.as_bytes()).await {
|
let bytes_read = stream.read(&mut buf)?;
|
||||||
out_string.0 = format!("Error sending response output to server! {e}:");
|
let response = String::from_utf8_lossy(&buf[..bytes_read]);
|
||||||
} else {
|
if let Some((_, cert)) = response.split_once("|") {
|
||||||
out_string.0 = String::from("Success");
|
return Ok(cert.to_string());
|
||||||
}
|
}
|
||||||
|
Err("Server did not send certificate".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn connect(&mut self) -> Result<usize, Box<dyn Error>> {
|
||||||
|
if let Ok(cert) = self.donwload_certificate() {
|
||||||
|
self.config = Some(self.build_tls_config(cert.as_str())?);
|
||||||
|
}
|
||||||
|
let mut stream = self.tls_connect()?;
|
||||||
|
stream.write("HELLO".as_bytes())?;
|
||||||
|
let mut buf = [0; 8192];
|
||||||
|
let bytes_read = stream.read(&mut buf)?;
|
||||||
|
let response = String::from_utf8_lossy(&buf[..bytes_read]);
|
||||||
|
if let Some((_, id)) = response.split_once("|") {
|
||||||
|
self.client_id = id.trim().parse()?;
|
||||||
|
}
|
||||||
|
self.connected = true;
|
||||||
|
self.last_check = Instant::now();
|
||||||
|
return Ok(self.client_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn checkin(&mut self) {
|
||||||
|
if let Ok(mut stream) = self.tls_connect() {
|
||||||
|
self.last_check = Instant::now();
|
||||||
|
let payload = format!(
|
||||||
|
"{}|||{}\n",
|
||||||
|
self.client_id.clone(),
|
||||||
|
self.message_que.join("||")
|
||||||
|
);
|
||||||
|
if let Err(e) = stream.write_all(payload.as_bytes()) {
|
||||||
|
self.action_que
|
||||||
|
.push(format!("Error|sending reponse output to server! {e}"));
|
||||||
|
}
|
||||||
|
self.message_que.clear();
|
||||||
let mut buffer = [0; 4096];
|
let mut buffer = [0; 4096];
|
||||||
if let Ok(bytes_read) = stream.read(&mut buffer).await {
|
if let Ok(bytes_read) = stream.read(&mut buffer) {
|
||||||
if bytes_read > 0 {
|
if bytes_read > 0 {
|
||||||
let response = String::from_utf8_lossy(&buffer[..bytes_read]);
|
let response = String::from_utf8_lossy(&buffer[..bytes_read]);
|
||||||
if response.starts_with("ACTIONS:") {
|
response.split("||").into_iter().for_each(|action| {
|
||||||
let actions_str = response.trim_start_matches("ACTIONS:").trim();
|
self.action_que.push(action.trim().to_string());
|
||||||
out_string.1.push_str(actions_str);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out_string;
|
|
||||||
|
pub fn build_tls_config(&self, pem: &str) -> Result<Arc<ClientConfig>, Box<dyn Error>> {
|
||||||
|
let mut roots = RootCertStore::empty();
|
||||||
|
let mut reader = BufReader::new(pem.as_bytes());
|
||||||
|
let certs = rustls_pemfile::certs(&mut reader).collect::<Result<Vec<_>, _>>()?;
|
||||||
|
for cert in certs {
|
||||||
|
roots.add(cert)?;
|
||||||
|
}
|
||||||
|
let config = ClientConfig::builder()
|
||||||
|
.with_root_certificates(roots)
|
||||||
|
.with_no_client_auth();
|
||||||
|
Ok(Arc::new(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tls_connect(&self) -> Result<StreamOwned<ClientConnection, TcpStream>, Box<dyn Error>> {
|
||||||
|
let config = self.config.clone().ok_or("TLS config not loaded")?;
|
||||||
|
let tcp = TcpStream::connect(&self.address)?;
|
||||||
|
let (host, _) = self.address.split_once(':').ok_or("Invalid address")?;
|
||||||
|
let server_name = ServerName::try_from(host.to_string())?;
|
||||||
|
let connection = ClientConnection::new(config, server_name)?;
|
||||||
|
let tls_stream = StreamOwned::new(connection, tcp);
|
||||||
|
Ok(tls_stream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1314,14 +1470,6 @@ impl Host {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Destination {
|
|
||||||
Server,
|
|
||||||
Victim,
|
|
||||||
Attacker,
|
|
||||||
Control,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@@ -1343,38 +1491,6 @@ impl User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum ToolMessage {
|
|
||||||
Input((usize, String)),
|
|
||||||
Output((usize, String)),
|
|
||||||
UpdateHost(Host),
|
|
||||||
RebuildDB,
|
|
||||||
DestroyDB(DistroBox),
|
|
||||||
StopDB(DistroBox),
|
|
||||||
StopTemplate(DistroBox),
|
|
||||||
UpdateProject(usize, Project),
|
|
||||||
RemoveProject,
|
|
||||||
AddServer,
|
|
||||||
EndPrompt,
|
|
||||||
ConnectServer,
|
|
||||||
SelectServer,
|
|
||||||
SendServer((Server, String)),
|
|
||||||
ServerBrokerExit,
|
|
||||||
AppStateExit,
|
|
||||||
DisconnectServer,
|
|
||||||
DisconnectAllServers,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum ToolArg {
|
|
||||||
Project(Project),
|
|
||||||
Projects(Vec<Project>),
|
|
||||||
Host(Host),
|
|
||||||
Hosts(Vec<Host>),
|
|
||||||
Config(HashMap<String, String>),
|
|
||||||
Path(PathBuf),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ToolCommand {
|
pub struct ToolCommand {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@@ -1549,12 +1665,6 @@ impl ModuleLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AppEvent {
|
|
||||||
Key(event::KeyEvent),
|
|
||||||
Worker(ToolMessage),
|
|
||||||
Mouse(event::MouseEvent),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DistroBox {
|
pub struct DistroBox {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|||||||
+37
-17
@@ -1,9 +1,10 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use std::sync::Arc;
|
use rcgen::generate_simple_self_signed;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
use std::{env, path::PathBuf, process::exit};
|
use std::{env, path::PathBuf, process::exit};
|
||||||
use tetanus::AppState;
|
|
||||||
use tetanus::funcs::*;
|
use tetanus::funcs::*;
|
||||||
use tokio::sync::Mutex;
|
use tetanus::server::start_server;
|
||||||
|
use tetanus::{AppState, server};
|
||||||
|
|
||||||
mod install;
|
mod install;
|
||||||
|
|
||||||
@@ -29,21 +30,13 @@ struct Args {
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let args = Args::parse();
|
|
||||||
if args.server {
|
|
||||||
let server = Arc::new(Mutex::new(tetanus::server::ServerState {
|
|
||||||
id: 0,
|
|
||||||
running: true,
|
|
||||||
address: String::from("127.0.0.1:31337"),
|
|
||||||
clients: Vec::new(),
|
|
||||||
key: String::new(),
|
|
||||||
}));
|
|
||||||
tetanus::server::start_server(server).await.unwrap();
|
|
||||||
} else {
|
|
||||||
println!("checking for server or client config files...");
|
println!("checking for server or client config files...");
|
||||||
let mut config_path = env::home_dir().unwrap();
|
let mut config_path = PathBuf::new();
|
||||||
config_path.push(".config/tetanus");
|
let mut config_path_opt = env::home_dir();
|
||||||
if !config_path.exists() {
|
if let Some(home_config_path) = config_path_opt.as_mut() {
|
||||||
|
println!("made it to home_config_path");
|
||||||
|
home_config_path.push(".config/tetanus");
|
||||||
|
if !home_config_path.exists() {
|
||||||
println!("no config directory found in home directory...");
|
println!("no config directory found in home directory...");
|
||||||
config_path = PathBuf::from("/etc/tetanus");
|
config_path = PathBuf::from("/etc/tetanus");
|
||||||
if !config_path.exists() {
|
if !config_path.exists() {
|
||||||
@@ -55,6 +48,10 @@ async fn main() {
|
|||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
println!("found home config_path.");
|
||||||
|
config_path = home_config_path.clone();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
let mut client_config_path = config_path.clone();
|
let mut client_config_path = config_path.clone();
|
||||||
@@ -76,7 +73,30 @@ async fn main() {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
appstate.initialize_modules();
|
appstate.initialize_modules();
|
||||||
|
println!("entering tui...");
|
||||||
let _res = run_tui(appstate, rx).unwrap();
|
let _res = run_tui(appstate, rx).unwrap();
|
||||||
|
} else if args.server {
|
||||||
|
let address = "127.0.0.1:31337".to_string();
|
||||||
|
let names = vec!["127.0.0.1".to_string(), "localhost".to_string()];
|
||||||
|
let mut certificate_path = config_path.clone();
|
||||||
|
certificate_path.push("server");
|
||||||
|
let mut key_path = certificate_path.clone();
|
||||||
|
certificate_path.push("cert.pem");
|
||||||
|
key_path.push("key.pem");
|
||||||
|
println!("key paht: {}", key_path.display());
|
||||||
|
println!("cert path: {}", certificate_path.display());
|
||||||
|
let new_server = server::Server {
|
||||||
|
address,
|
||||||
|
clients: Vec::new(),
|
||||||
|
certificate_path,
|
||||||
|
key_path,
|
||||||
|
};
|
||||||
|
let res = start_server(Arc::new(Mutex::new(new_server))).await;
|
||||||
|
match res {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
println!("error running server {e}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+317
@@ -0,0 +1,317 @@
|
|||||||
|
use rcgen::generate_simple_self_signed;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::fs::write;
|
||||||
|
use std::io::BufReader;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::usize;
|
||||||
|
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||||
|
use tokio::net::{TcpListener, TcpStream};
|
||||||
|
use tokio_rustls::TlsAcceptor;
|
||||||
|
use tokio_rustls::rustls::ServerConfig;
|
||||||
|
use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
enum ClientAction {
|
||||||
|
Output(String),
|
||||||
|
Cmd(String),
|
||||||
|
Ping,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Client {
|
||||||
|
address: String,
|
||||||
|
id: usize,
|
||||||
|
hostname: Option<String>,
|
||||||
|
actions: Vec<ClientAction>,
|
||||||
|
controlling: usize,
|
||||||
|
controlled: usize,
|
||||||
|
output_que: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Server {
|
||||||
|
pub address: String,
|
||||||
|
pub clients: Vec<Client>,
|
||||||
|
pub certificate_path: PathBuf,
|
||||||
|
pub key_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn start_server(server: Arc<Mutex<Server>>) -> Result<(), Box<dyn Error>> {
|
||||||
|
let lock = server.lock().unwrap();
|
||||||
|
if !lock.certificate_path.exists() || !lock.key_path.exists() {
|
||||||
|
let (server_ip, _) = lock.address.split_once(':').unwrap();
|
||||||
|
|
||||||
|
let cert = generate_simple_self_signed(vec![
|
||||||
|
server_ip.to_string(),
|
||||||
|
"127.0.0.1".to_string(),
|
||||||
|
"localhost".to_string(),
|
||||||
|
])?;
|
||||||
|
|
||||||
|
write(&lock.certificate_path, cert.cert.pem())?;
|
||||||
|
|
||||||
|
write(&lock.key_path, cert.signing_key.serialize_pem())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let certs = load_certs(&lock.certificate_path)?;
|
||||||
|
let key = load_key(&lock.key_path)?;
|
||||||
|
|
||||||
|
let tls_config = ServerConfig::builder()
|
||||||
|
.with_no_client_auth()
|
||||||
|
.with_single_cert(certs, key)?;
|
||||||
|
|
||||||
|
let acceptor = TlsAcceptor::from(Arc::new(tls_config));
|
||||||
|
|
||||||
|
let listener = TcpListener::bind(lock.address.clone()).await?;
|
||||||
|
|
||||||
|
println!("Listening on {}", lock.address);
|
||||||
|
|
||||||
|
drop(lock);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let (stream, addr) = listener.accept().await?;
|
||||||
|
|
||||||
|
let acceptor = acceptor.clone();
|
||||||
|
let server = server.clone();
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let mut peek = [0u8; 8];
|
||||||
|
if let Ok(n) = stream.peek(&mut peek).await {
|
||||||
|
if n >= 8 && &peek[..8] == b"CERT_REQ" {
|
||||||
|
handle_bootstrap(server, stream).await;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match acceptor.accept(stream).await {
|
||||||
|
Ok(tls_stream) => {
|
||||||
|
handle_connection_stat9ic(server, tls_stream, addr).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("TLS handshake failed: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_certs(path: &std::path::Path) -> Result<Vec<CertificateDer<'static>>, Box<dyn Error>> {
|
||||||
|
let mut reader = BufReader::new(File::open(path)?);
|
||||||
|
|
||||||
|
Ok(rustls_pemfile::certs(&mut reader).collect::<Result<Vec<_>, _>>()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_key(path: &std::path::Path) -> Result<PrivateKeyDer<'static>, Box<dyn Error>> {
|
||||||
|
let mut reader = BufReader::new(File::open(path)?);
|
||||||
|
|
||||||
|
let key = rustls_pemfile::private_key(&mut reader)?.ok_or("No private key found")?;
|
||||||
|
|
||||||
|
Ok(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_bootstrap(server: Arc<Mutex<Server>>, mut stream: TcpStream) {
|
||||||
|
let mut buf = [0u8; 1024];
|
||||||
|
let n = match stream.read(&mut buf).await {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
if n == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let msg = String::from_utf8_lossy(&buf[..n]);
|
||||||
|
if msg.trim() != "CERT_REQ" {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let cert_path = {
|
||||||
|
let lock = server.lock().unwrap();
|
||||||
|
lock.certificate_path.clone()
|
||||||
|
};
|
||||||
|
let cert_pem = match std::fs::read_to_string(cert_path) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let response = format!("CERT|{}\n", cert_pem);
|
||||||
|
let _ = stream.write_all(response.as_bytes()).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_connection_stat9ic<S>(server: Arc<Mutex<Server>>, mut stream: S, addr: SocketAddr)
|
||||||
|
where
|
||||||
|
S: AsyncRead + AsyncWrite + Unpin,
|
||||||
|
{
|
||||||
|
let mut buf = vec![0u8; 4096];
|
||||||
|
let n = match stream.read(&mut buf).await {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Read Error: {e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if n == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let msg = String::from_utf8_lossy(&buf[..n]);
|
||||||
|
if msg.contains("HELLO") {
|
||||||
|
let mut connected = false;
|
||||||
|
let mut id = 0;
|
||||||
|
if let Ok(mut lock) = server.lock() {
|
||||||
|
let new_client = Client {
|
||||||
|
address: addr.to_string(),
|
||||||
|
id: lock.clients.len() + 1,
|
||||||
|
hostname: None,
|
||||||
|
actions: Vec::new(),
|
||||||
|
controlled: 0,
|
||||||
|
controlling: 0,
|
||||||
|
output_que: Vec::new(),
|
||||||
|
};
|
||||||
|
connected = true;
|
||||||
|
id = new_client.id.clone();
|
||||||
|
lock.clients.push(new_client);
|
||||||
|
}
|
||||||
|
if connected {
|
||||||
|
stream
|
||||||
|
.write_all(format!("HELLO|{}\n", id).as_bytes())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
println!("client connected! ID:{}", id,);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Some((source, data)) = msg.split_once("|||") {
|
||||||
|
if let Ok(source_id) = source.trim().parse::<usize>() {
|
||||||
|
let mut responses = Vec::new();
|
||||||
|
if let Ok(mut lock) = server.lock() {
|
||||||
|
if let Some(source_client) = lock.clients.iter_mut().find(|c| c.id == source_id)
|
||||||
|
{
|
||||||
|
responses = source_client.actions.clone();
|
||||||
|
}
|
||||||
|
data.split("||").into_iter().for_each(|action| {
|
||||||
|
if let Some((cmd, data)) = action.split_once("|") {
|
||||||
|
match cmd.trim() {
|
||||||
|
"OUTPUT" => {
|
||||||
|
if let Some(dest_client) =
|
||||||
|
lock.clients.iter_mut().find(|c| c.controlling == source_id)
|
||||||
|
{
|
||||||
|
dest_client.actions.push(ClientAction::Output(format!(
|
||||||
|
"from {}: {}",
|
||||||
|
dest_client.id,
|
||||||
|
data.trim()
|
||||||
|
)));
|
||||||
|
} else {
|
||||||
|
if let Some(source_client) =
|
||||||
|
lock.clients.iter_mut().find(|c| c.id == source_id)
|
||||||
|
{
|
||||||
|
source_client.output_que.push(data.trim().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"CMD" => {
|
||||||
|
if let Some(dest_client) =
|
||||||
|
lock.clients.iter_mut().find(|c| c.controlled == source_id)
|
||||||
|
{
|
||||||
|
dest_client
|
||||||
|
.actions
|
||||||
|
.push(ClientAction::Cmd(data.to_string()));
|
||||||
|
responses.push(ClientAction::Output(format!(
|
||||||
|
"tasked client {} to run {}",
|
||||||
|
dest_client.id, cmd
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"CONTROL" => {
|
||||||
|
let new_control_id = data.trim().parse::<usize>().unwrap();
|
||||||
|
if let Some(controlling_client) =
|
||||||
|
lock.clients.iter_mut().find(|c| c.id == source_id)
|
||||||
|
{
|
||||||
|
controlling_client.controlling = new_control_id.clone();
|
||||||
|
}
|
||||||
|
if let Some(contlled_client) =
|
||||||
|
lock.clients.iter_mut().find(|c| c.id == new_control_id)
|
||||||
|
{
|
||||||
|
contlled_client.controlled = source_id;
|
||||||
|
} else {
|
||||||
|
if let Some(controlling_client) =
|
||||||
|
lock.clients.iter_mut().find(|c| c.id == source_id)
|
||||||
|
{
|
||||||
|
controlling_client.controlling = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"STOP_CONTROL" => {
|
||||||
|
if let Some(source_client) =
|
||||||
|
lock.clients.iter_mut().find(|c| c.id == source_id)
|
||||||
|
{
|
||||||
|
if source_client.controlling != 0 {
|
||||||
|
let dest_client_id = source_client.controlling.clone();
|
||||||
|
if let Some(dest_client) = lock
|
||||||
|
.clients
|
||||||
|
.iter_mut()
|
||||||
|
.find(|c| c.id == dest_client_id)
|
||||||
|
{
|
||||||
|
dest_client.controlled = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"BREAK_CONTROL" => {
|
||||||
|
if let Some(source_client) =
|
||||||
|
lock.clients.iter_mut().find(|c| c.id == source_id)
|
||||||
|
{
|
||||||
|
if source_client.controlled != 0 {
|
||||||
|
let dest_id = source_client.controlled.clone();
|
||||||
|
source_client.controlled = 0;
|
||||||
|
if let Some(dest_client) =
|
||||||
|
lock.clients.iter_mut().find(|c| c.id == dest_id)
|
||||||
|
{
|
||||||
|
dest_client.controlling = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"LIST_CLIENTS" => {
|
||||||
|
lock.clients.iter().for_each(|c| {
|
||||||
|
let out = format!("{}: {}", c.id, c.address);
|
||||||
|
println!("client list requested!");
|
||||||
|
println!("adding {} to response...", out);
|
||||||
|
responses.push(ClientAction::Output(out));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
"TEST" => {
|
||||||
|
responses.push(ClientAction::Output("TEST BACK".to_string()));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if responses.len() > 0 {
|
||||||
|
let mut messages = Vec::new();
|
||||||
|
for r in responses {
|
||||||
|
match r {
|
||||||
|
ClientAction::Cmd(cmd) => {
|
||||||
|
messages.push(format!("CMD|{}", cmd));
|
||||||
|
}
|
||||||
|
ClientAction::Output(text) => {
|
||||||
|
println!("adding {} to output", text);
|
||||||
|
messages.push(format!("OUTPUT|{}", text));
|
||||||
|
}
|
||||||
|
ClientAction::Ping => {
|
||||||
|
messages.push(format!("PONG"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let full_message = messages.join("||");
|
||||||
|
println!("attempting to send {}", full_message);
|
||||||
|
let buf = full_message.as_bytes();
|
||||||
|
stream.write(buf).await.unwrap();
|
||||||
|
println!("buffer written!");
|
||||||
|
if let Ok(mut lock) = server.lock() {
|
||||||
|
if let Some(source_client) =
|
||||||
|
lock.clients.iter_mut().find(|c| c.id == source_id)
|
||||||
|
{
|
||||||
|
source_client.actions.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user