close to finish a tun/smoltcp

This commit is contained in:
2026-03-07 20:17:49 +07:00
parent 4a789dd128
commit b1cc4c14f4
14 changed files with 912 additions and 279 deletions
Generated
+501 -2
View File
@@ -22,6 +22,12 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]] [[package]]
name = "android_log-sys" name = "android_log-sys"
version = "0.3.2" version = "0.3.2"
@@ -114,6 +120,17 @@ version = "4.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
[[package]]
name = "async-trait"
version = "0.1.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "atomic-waker" name = "atomic-waker"
version = "1.1.2" version = "1.1.2"
@@ -186,6 +203,16 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "cc"
version = "1.2.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]] [[package]]
name = "cesu8" name = "cesu8"
version = "1.1.0" version = "1.1.0"
@@ -333,6 +360,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "critical-section"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.21" version = "0.8.21"
@@ -376,6 +409,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "data-encoding"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
[[package]] [[package]]
name = "defmt" name = "defmt"
version = "0.3.100" version = "0.3.100"
@@ -428,6 +467,29 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "enum-as-inner"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "env_logger" name = "env_logger"
version = "0.10.2" version = "0.10.2"
@@ -487,12 +549,33 @@ version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]] [[package]]
name = "foldhash" name = "foldhash"
version = "0.1.5" version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "form_urlencoded"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
dependencies = [
"percent-encoding",
]
[[package]] [[package]]
name = "futures" name = "futures"
version = "0.3.32" version = "0.3.32"
@@ -612,6 +695,18 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.4.1" version = "0.4.1"
@@ -641,7 +736,7 @@ version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [ dependencies = [
"foldhash", "foldhash 0.1.5",
] ]
[[package]] [[package]]
@@ -649,6 +744,11 @@ name = "hashbrown"
version = "0.16.1" version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash 0.2.0",
]
[[package]] [[package]]
name = "heapless" name = "heapless"
@@ -672,6 +772,31 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hickory-proto"
version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502"
dependencies = [
"async-trait",
"cfg-if",
"data-encoding",
"enum-as-inner",
"futures-channel",
"futures-io",
"futures-util",
"idna",
"ipnet",
"once_cell",
"rand 0.9.2",
"ring",
"thiserror 2.0.18",
"tinyvec",
"tokio",
"tracing",
"url",
]
[[package]] [[package]]
name = "hkdf" name = "hkdf"
version = "0.12.4" version = "0.12.4"
@@ -690,12 +815,114 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "icu_collections"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
dependencies = [
"displaydoc",
"potential_utf",
"yoke",
"zerofrom",
"zerovec",
]
[[package]]
name = "icu_locale_core"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
dependencies = [
"displaydoc",
"litemap",
"tinystr",
"writeable",
"zerovec",
]
[[package]]
name = "icu_normalizer"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
dependencies = [
"icu_collections",
"icu_normalizer_data",
"icu_properties",
"icu_provider",
"smallvec",
"zerovec",
]
[[package]]
name = "icu_normalizer_data"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a"
[[package]]
name = "icu_properties"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec"
dependencies = [
"icu_collections",
"icu_locale_core",
"icu_properties_data",
"icu_provider",
"zerotrie",
"zerovec",
]
[[package]]
name = "icu_properties_data"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af"
[[package]]
name = "icu_provider"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
dependencies = [
"displaydoc",
"icu_locale_core",
"writeable",
"yoke",
"zerofrom",
"zerotrie",
"zerovec",
]
[[package]] [[package]]
name = "id-arena" name = "id-arena"
version = "2.3.0" version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "idna"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
dependencies = [
"idna_adapter",
"smallvec",
"utf8_iter",
]
[[package]]
name = "idna_adapter"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
dependencies = [
"icu_normalizer",
"icu_properties",
]
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.13.0" version = "2.13.0"
@@ -785,6 +1012,12 @@ dependencies = [
"windows-link", "windows-link",
] ]
[[package]]
name = "litemap"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.14" version = "0.4.14"
@@ -800,6 +1033,15 @@ version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "lru"
version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
dependencies = [
"hashbrown 0.16.1",
]
[[package]] [[package]]
name = "managed" name = "managed"
version = "0.8.0" version = "0.8.0"
@@ -838,9 +1080,11 @@ version = "0.1.0"
dependencies = [ dependencies = [
"android_logger", "android_logger",
"bytes", "bytes",
"hickory-proto",
"jni", "jni",
"lazy_static", "lazy_static",
"log", "log",
"lru",
"netrunner-common", "netrunner-common",
"smoltcp", "smoltcp",
"thiserror 2.0.18", "thiserror 2.0.18",
@@ -860,7 +1104,7 @@ dependencies = [
"hex", "hex",
"hkdf", "hkdf",
"hmac", "hmac",
"rand", "rand 0.10.0",
"sha2", "sha2",
"tokio", "tokio",
"tracing", "tracing",
@@ -903,6 +1147,10 @@ name = "once_cell"
version = "1.21.3" version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
dependencies = [
"critical-section",
"portable-atomic",
]
[[package]] [[package]]
name = "once_cell_polyfill" name = "once_cell_polyfill"
@@ -945,6 +1193,12 @@ dependencies = [
"windows-link", "windows-link",
] ]
[[package]]
name = "percent-encoding"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.16" version = "0.2.16"
@@ -973,6 +1227,30 @@ dependencies = [
"universal-hash", "universal-hash",
] ]
[[package]]
name = "portable-atomic"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
name = "potential_utf"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77"
dependencies = [
"zerovec",
]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]] [[package]]
name = "prettyplease" name = "prettyplease"
version = "0.2.37" version = "0.2.37"
@@ -1029,6 +1307,16 @@ version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha",
"rand_core 0.9.5",
]
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.10.0" version = "0.10.0"
@@ -1040,6 +1328,16 @@ dependencies = [
"rand_core 0.10.0", "rand_core 0.10.0",
] ]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core 0.9.5",
]
[[package]] [[package]]
name = "rand_core" name = "rand_core"
version = "0.6.4" version = "0.6.4"
@@ -1049,6 +1347,15 @@ dependencies = [
"getrandom 0.2.17", "getrandom 0.2.17",
] ]
[[package]]
name = "rand_core"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
dependencies = [
"getrandom 0.3.4",
]
[[package]] [[package]]
name = "rand_core" name = "rand_core"
version = "0.10.0" version = "0.10.0"
@@ -1093,6 +1400,20 @@ version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
[[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 = "rustc_version" name = "rustc_version"
version = "0.4.1" version = "0.4.1"
@@ -1186,6 +1507,12 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.8" version = "1.4.8"
@@ -1262,6 +1589,17 @@ dependencies = [
"unicode-ident", "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",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.69" version = "1.0.69"
@@ -1311,6 +1649,31 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "tinystr"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
dependencies = [
"displaydoc",
"zerovec",
]
[[package]]
name = "tinyvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.49.0" version = "1.49.0"
@@ -1462,6 +1825,30 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "url"
version = "2.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
"serde",
]
[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.2" version = "0.2.2"
@@ -1572,6 +1959,15 @@ dependencies = [
"windows-targets 0.42.2", "windows-targets 0.42.2",
] ]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.59.0" version = "0.59.0"
@@ -1899,6 +2295,12 @@ dependencies = [
"wasmparser", "wasmparser",
] ]
[[package]]
name = "writeable"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
[[package]] [[package]]
name = "x25519-dalek" name = "x25519-dalek"
version = "2.0.1" version = "2.0.1"
@@ -1911,6 +2313,70 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "yoke"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954"
dependencies = [
"stable_deref_trait",
"yoke-derive",
"zerofrom",
]
[[package]]
name = "yoke-derive"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "zerocopy"
version = "0.8.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zerofrom"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
dependencies = [
"zerofrom-derive",
]
[[package]]
name = "zerofrom-derive"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]] [[package]]
name = "zeroize" name = "zeroize"
version = "1.8.2" version = "1.8.2"
@@ -1931,6 +2397,39 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "zerotrie"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851"
dependencies = [
"displaydoc",
"yoke",
"zerofrom",
]
[[package]]
name = "zerovec"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
dependencies = [
"yoke",
"zerofrom",
"zerovec-derive",
]
[[package]]
name = "zerovec-derive"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "zmij" name = "zmij"
version = "1.0.21" version = "1.0.21"
+2
View File
@@ -34,3 +34,5 @@ lazy_static = "1.4.0"
tracing = "0.1" tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
thiserror = "2.0" thiserror = "2.0"
hickory-proto = "0.25.2"
lru = "0.16.3"
+35
View File
@@ -0,0 +1,35 @@
use hickory_proto::op::{Message, MessageType, ResponseCode};
use hickory_proto::rr::{RData, Record};
use crate::connections::ip_store::FakeIpStore;
pub fn handle_dns_query(data: &[u8], store: &mut FakeIpStore) -> Option<Vec<u8>> {
// 1. Парсим входящий запрос
let request = Message::from_vec(data).ok()?;
// 2. Берем первый вопрос (обычно запрос только один)
let query = request.queries().first()?;
let name = query.name().to_string().trim_end_matches('.').to_string();
// 3. Получаем Fake IP
let fake_ip = store.get_or_assign(&name);
// 4. Формируем ответ
let mut response = Message::new();
response
.set_id(request.id())
.set_message_type(MessageType::Response)
.set_response_code(ResponseCode::NoError)
.add_query(query.clone());
// Создаем запись A (IPv4)
let record = Record::from_rdata(
query.name().clone(),
1, // TTL = 1 сек
RData::A(fake_ip.into()),
);
response.add_answer(record);
// 5. Сериализуем обратно в байты
response.to_vec().ok()
}
+36
View File
@@ -0,0 +1,36 @@
use lru::LruCache;
use std::net::Ipv4Addr;
use std::num::NonZeroUsize;
pub struct FakeIpStore {
cache: LruCache<String, Ipv4Addr>,
pub rev_cache: LruCache<Ipv4Addr, String>,
next_ip: u32,
}
const START_IP: u32 = 0x64400001; // 100.64.0.1 (IP for Service Networks/Shared Address Space)
impl FakeIpStore {
pub fn new() -> Self {
Self {
cache: LruCache::new(NonZeroUsize::new(2000).unwrap()),
rev_cache: LruCache::new(NonZeroUsize::new(2000).unwrap()),
next_ip: START_IP,
}
}
pub fn get_or_assign(&mut self, host: &str) -> Ipv4Addr {
if let Some(&ip) = self.cache.get(host) {
return ip;
}
let ip = Ipv4Addr::from(self.next_ip);
self.next_ip += 1;
self.cache.put(host.to_string(), ip);
self.rev_cache.put(ip, host.to_string());
ip
}
pub fn lookup_by_ip(&self, ip: &Ipv4Addr) -> Option<String> {
self.rev_cache.peek(ip).cloned()
}
}
+3
View File
@@ -1 +1,4 @@
pub mod dns;
pub mod ip_store;
pub mod tcp_connection; pub mod tcp_connection;
pub mod udp_connection;
+98 -30
View File
@@ -1,18 +1,15 @@
use netrunner_common::protocol::codec::socks::SocksRequest; use netrunner_common::protocol::codec::socks::{SocksRequest, TargetAddress};
use smoltcp::iface::SocketHandle; use smoltcp::iface::SocketHandle;
use smoltcp::socket::tcp; use smoltcp::socket::tcp;
use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream; // Твой код парсера use tokio::net::TcpStream; // Твой код парсера
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tracing::{debug, info, trace};
pub enum ConnectionState { pub enum ConnectionState {
/// Сокет только что принял соединение (SYN-ACK завершен)
Established, Established,
/// Мы в процессе SOCKS5 рукопожатия с локальным прокси
Handshaking, Handshaking,
/// Соединение готово к передаче данных (Bridge)
Active, Active,
/// Ошибка или закрытие
Closed, Closed,
} }
@@ -21,33 +18,46 @@ pub struct TcpConnection {
state: ConnectionState, state: ConnectionState,
tx: mpsc::UnboundedSender<Vec<u8>>, tx: mpsc::UnboundedSender<Vec<u8>>,
rx: mpsc::UnboundedReceiver<Vec<u8>>, rx: mpsc::UnboundedReceiver<Vec<u8>>,
pending_data: Option<Vec<u8>>,
} }
impl TcpConnection { impl TcpConnection {
pub fn new( pub fn new(handle: SocketHandle, proxy_addr: String, target_addr: TargetAddress) -> Self {
handle: SocketHandle,
proxy_addr: String,
target_addr: std::net::SocketAddr,
) -> Self {
let (tx_to_proxy, mut rx_from_smol) = mpsc::unbounded_channel::<Vec<u8>>(); let (tx_to_proxy, mut rx_from_smol) = mpsc::unbounded_channel::<Vec<u8>>();
let (tx_to_smol, rx_from_proxy) = mpsc::unbounded_channel::<Vec<u8>>(); let (tx_to_smol, rx_from_proxy) = mpsc::unbounded_channel::<Vec<u8>>();
// Запускаем асинхронную логику в фоне let proxy_addr_clone = proxy_addr.clone();
let target_addr_clone = target_addr.clone();
tokio::spawn(async move { tokio::spawn(async move {
// 1. Подключаемся debug!(
%handle,
target = ?target_addr_clone,
proxy = %proxy_addr_clone,
"Attempting to connect through proxy"
);
let mut stream = match TcpStream::connect(&proxy_addr).await { let mut stream = match TcpStream::connect(&proxy_addr).await {
Ok(s) => s, Ok(s) => {
Err(_) => return, // Тут можно добавить логирование debug!(%handle, "Connected to proxy successfully");
s
}
Err(e) => {
debug!(%handle, error = %e, "Failed to connect to proxy");
return;
} // Тут можно добавить логирование
}; };
// 2. SOCKS Handshake // 2. SOCKS Handshake
if SocksRequest::perform_client_handshake(&mut stream, &target_addr) if let Err(e) =
.await SocksRequest::perform_client_handshake(&mut stream, &target_addr_clone).await
.is_err()
{ {
debug!(%handle, error = %e, "SOCKS handshake failed");
return; return;
} }
debug!(%handle, "SOCKS handshake successful, starting data bridge");
// 3. Копирование данных (Bridge) // 3. Копирование данных (Bridge)
let (mut reader, mut writer) = stream.into_split(); let (mut reader, mut writer) = stream.into_split();
@@ -83,29 +93,87 @@ impl TcpConnection {
Self { Self {
handle, handle,
state: ConnectionState::Active, // Сразу переходим в Active, т.к. задача пошла state: ConnectionState::Handshaking, // Сразу переходим в Active, т.к. задача пошла
tx: tx_to_proxy, tx: tx_to_proxy,
rx: rx_from_proxy, rx: rx_from_proxy,
pending_data: None,
} }
} }
pub fn poll_and_process(&mut self, socket: &mut tcp::Socket) { pub fn tick(&mut self, socket: &mut tcp::Socket) -> bool {
// 1. Из smoltcp -> В канал задачи trace!(handle=%self.handle, state=?socket.state(), "Tick");
match self.state {
ConnectionState::Handshaking => {
info!("Connection handshaking");
return true;
}
ConnectionState::Active => {
self.poll_and_process(socket);
// Проверяем условия закрытия
if self.is_finished(socket) {
self.state = ConnectionState::Closed;
socket.abort(); // Принудительно разрываем стек
return false; // Сигнализируем, что соединение мертво
}
}
ConnectionState::Closed => return false,
_ => {}
}
true
}
pub fn is_finished(&self, socket: &tcp::Socket) -> bool {
use tcp::State;
let socket_closed = matches!(
socket.state(),
State::Closed | State::TimeWait | State::FinWait1 | State::FinWait2
);
let task_finished = self.rx.is_closed();
socket_closed || task_finished
}
pub fn is_active(&self) -> bool {
matches!(self.state, ConnectionState::Active)
}
fn poll_and_process(&mut self, socket: &mut tcp::Socket) {
if let Some(data) = self.pending_data.take() {
if socket.send_slice(&data).is_ok() {
} else {
self.pending_data = Some(data);
return;
}
}
if socket.can_recv() { if socket.can_recv() {
let mut buf = [0u8; 4096]; let _ = socket.recv(|data| {
if let Ok(n) = socket.recv_slice(&mut buf) { let len = data.len();
if n > 0 { if len > 0 {
let _ = self.tx.send(buf[..n].to_vec()); let _ = self.tx.send(data.to_vec());
}
(len, ())
});
}
if socket.can_send() {
while let Ok(data) = self.rx.try_recv() {
match socket.send_slice(&data) {
Ok(_) => { /* Успешно */ }
Err(_) => {
// Стек полон, запоминаем данные для следующего вызова
self.pending_data = Some(data);
break;
}
} }
} }
} }
// 2. Из канала задачи -> В smoltcp if socket.state() == tcp::State::CloseWait {
if socket.can_send() { debug!(handle=%self.handle, "!!! Triggering socket.close() for CLOSE-WAIT");
// try_recv не блокирует поток, что нам и нужно socket.close();
if let Ok(data) = self.rx.try_recv() {
let _ = socket.send_slice(&data);
}
} }
} }
} }
+33
View File
@@ -0,0 +1,33 @@
use crate::connections::dns::handle_dns_query;
use crate::connections::ip_store::FakeIpStore;
use smoltcp::socket::udp;
pub struct UdpConnection;
impl UdpConnection {
pub fn process_incoming(socket: &mut udp::Socket, store: &mut FakeIpStore) {
// Мы используем peek или recv, чтобы понять, куда идти дальше
while socket.can_recv() {
let (data, metadata) = match socket.recv() {
Ok(res) => res,
Err(_) => break,
};
// Диспетчер: определяем тип трафика по порту
match metadata.endpoint.port {
53 => {
// Это DNS
if let Some(response) = handle_dns_query(&data, store) {
let _ = socket.send_slice(&response, metadata);
}
}
// Тут в будущем можно добавить обработку других протоколов
// 443 => process_quic(...),
_ => {
// Можно логировать неизвестный трафик или просто дропать
// trace!("Received UDP on unknown port: {}", endpoint.port);
}
}
}
}
}
+7 -5
View File
@@ -1,3 +1,5 @@
use std::net::Ipv4Addr;
use netrunner_client::{tun::engine::Engine, tun::tun::Tun}; use netrunner_client::{tun::engine::Engine, tun::tun::Tun};
use netrunner_common::{ use netrunner_common::{
logger_init, logger_init,
@@ -21,8 +23,7 @@ async fn main() {
}) })
.expect("Failed to initialize TUN device"); .expect("Failed to initialize TUN device");
tun_device.clear_default_route(); tun_device.setup_routing();
tun_device.setup_linux_routes();
info!("TUN interface is UP: 10.0.0.1/24"); info!("TUN interface is UP: 10.0.0.1/24");
@@ -35,7 +36,7 @@ async fn main() {
let network = Network::new(8080, ConnectionRole::Client, Some(remote_address)); let network = Network::new(8080, ConnectionRole::Client, Some(remote_address));
let socks_addr = network.get_self_local_address(); let proxy_ip = network.get_self_local_address();
// 2. ВЫНОСИМ СЕТЬ В ОТДЕЛЬНЫЙ ЦИКЛ // 2. ВЫНОСИМ СЕТЬ В ОТДЕЛЬНЫЙ ЦИКЛ
// Это запустит SOCKS5 сервер и TLS туннель параллельно движку // Это запустит SOCKS5 сервер и TLS туннель параллельно движку
@@ -44,10 +45,11 @@ async fn main() {
network.run().await; network.run().await;
}); });
let mut engine = Engine::new(config, caps, socks_addr); let mut engine = Engine::new(config, caps, proxy_ip);
engine.set_any_ip(true); engine.set_any_ip(true);
engine.set_transparent_mode(); engine.set_transparent_mode();
engine.set_default_gateway(Ipv4Addr::new(10, 0, 0, 2)); // to smoltcp
engine.activate();
info!("Stack IP initialized: 10.0.0.2"); info!("Stack IP initialized: 10.0.0.2");
// 5. Запуск // 5. Запуск
+102 -142
View File
@@ -1,19 +1,26 @@
use netrunner_common::protocol::codec::socks::TargetAddress;
use smoltcp::{ use smoltcp::{
iface::{SocketHandle, SocketSet}, iface::{SocketHandle, SocketSet},
socket::{AnySocket, icmp, tcp, udp}, socket::{AnySocket, icmp, tcp, udp},
wire::IpListenEndpoint, wire::{IpAddress, IpListenEndpoint},
}; };
use std::{ use std::{
collections::HashMap, collections::HashMap,
time::{Duration, Instant as StdInstant}, time::{Duration, Instant as StdInstant},
}; };
use tracing::{debug, info}; use tracing::{debug, info, warn};
use crate::{connections::tcp_connection::TcpConnection, tun::engine::START_TIME}; use crate::{
connections::{
ip_store::FakeIpStore, tcp_connection::TcpConnection, udp_connection::UdpConnection,
},
tun::engine::START_TIME,
};
pub struct ConnectionManager { pub struct ConnectionManager {
last_activity: HashMap<SocketHandle, StdInstant>, last_activity: HashMap<SocketHandle, StdInstant>,
active_tcp_sessions: HashMap<SocketHandle, TcpConnection>, active_tcp_sessions: HashMap<SocketHandle, TcpConnection>,
//active_udp_sessions: HashMap<SocketHandle, UdpSession>, //active_udp_sessions: HashMap<SocketHandle, UdpSession>,
fake_ip_store: FakeIpStore,
proxy_ip: String, proxy_ip: String,
} }
@@ -23,79 +30,113 @@ impl ConnectionManager {
last_activity: HashMap::new(), last_activity: HashMap::new(),
active_tcp_sessions: HashMap::new(), active_tcp_sessions: HashMap::new(),
proxy_ip: ip, proxy_ip: ip,
fake_ip_store: FakeIpStore::new(),
} }
} }
/// Основной метод, который вызывается в цикле Engine pub fn start_listening(&mut self, socket_set: &mut SocketSet) {
for (_, socket) in socket_set.iter_mut() {
if let Some(tcp) = tcp::Socket::downcast_mut(socket) {
if !tcp.is_open() {
let endpoint = IpListenEndpoint {
addr: None,
port: 443,
};
match tcp.listen(endpoint) {
Ok(_) => debug!("Socket is now listening"),
Err(e) => warn!(error=?e, "Failed to listen on socket"),
}
}
}
}
}
fn resolve_target(&self, socket: &tcp::Socket) -> TargetAddress {
// Безопасно получаем эндпоинт
let remote_endpoint = match socket.remote_endpoint() {
Some(ep) => ep,
None => {
warn!(handle=?socket, "Attempted to resolve target for an unconnected socket");
// Возвращаем дефолт, чтобы не падать
return TargetAddress::Domain("disconnected".to_string(), 0);
}
};
let port = remote_endpoint.port;
let ip = remote_endpoint.addr;
match ip {
smoltcp::wire::IpAddress::Ipv4(ipv4_addr) => {
let std_ip = std::net::Ipv4Addr::from(ipv4_addr);
// Ищем домен в нашем FakeIpStore
if let Some(domain) = self.fake_ip_store.lookup_by_ip(&std_ip) {
debug!(target=%domain, port=%port, "Resolved fake IP to domain");
return TargetAddress::Domain(domain, port);
}
debug!(ip=%std_ip, port=%port, "Using raw IP target");
TargetAddress::Ipv4(std_ip, port)
}
smoltcp::wire::IpAddress::Ipv6(ipv6_addr) => {
let std_ip = std::net::Ipv6Addr::from(ipv6_addr);
debug!(ip=%std_ip, port=%port, "Using IPv6 target");
TargetAddress::Ipv6(std_ip, port)
}
}
}
pub fn process_sockets(&mut self, socket_set: &mut SocketSet) { pub fn process_sockets(&mut self, socket_set: &mut SocketSet) {
for (handle, socket) in socket_set.iter_mut() { for (handle, socket) in socket_set.iter_mut() {
// 1. Пытаемся даункастить сокет до TCP
if let Some(tcp) = tcp::Socket::downcast_mut(socket) { if let Some(tcp) = tcp::Socket::downcast_mut(socket) {
self.handle_tcp(handle, tcp); self.handle_tcp(handle, tcp);
continue; } else if let Some(udp) = udp::Socket::downcast_mut(socket) {
}
// 2. До UDP
if let Some(udp) = udp::Socket::downcast_mut(socket) {
self.handle_udp(handle, udp); self.handle_udp(handle, udp);
continue; } else if let Some(icmp) = icmp::Socket::downcast_mut(socket) {
}
// 3. До ICMP
if let Some(icmp) = icmp::Socket::downcast_mut(socket) {
self.handle_icmp(handle, icmp); self.handle_icmp(handle, icmp);
continue;
} }
} }
} }
fn handle_tcp(&mut self, handle: SocketHandle, socket: &mut tcp::Socket) { fn handle_tcp(&mut self, handle: SocketHandle, socket: &mut tcp::Socket) {
if socket.state() == tcp::State::Established { if socket.state() == tcp::State::Established {
let proxy_ip = self.proxy_ip.clone(); // Берем из конфига менеджера let target = self.resolve_target(socket);
debug!(
handle=%handle,
local=?socket.local_endpoint(),
remote=?socket.remote_endpoint(),
"Socket details"
);
let conn = self.active_tcp_sessions.entry(handle).or_insert_with(|| { let mut conn = TcpConnection::new(handle, self.proxy_ip.clone(), target);
// 1. Получаем endpoint и безопасно распаковываем его
let endpoint = socket
.remote_endpoint()
.expect("TCP socket in Established state must have a remote endpoint");
// 2. Конвертируем smoltcp::wire::IpAddress в std::net::IpAddr conn.tick(socket);
let ip: std::net::IpAddr = match endpoint.addr {
smoltcp::wire::IpAddress::Ipv4(v4) => std::net::IpAddr::V4(v4.into()),
smoltcp::wire::IpAddress::Ipv6(v6) => std::net::IpAddr::V6(v6.into()),
};
// 3. Собираем финальный SocketAddr if conn.is_active() {
let target_addr = std::net::SocketAddr::new(ip, endpoint.port); self.active_tcp_sessions.insert(handle, conn);
} else {
info!(handle=%handle, target=%target_addr, "Creating new TcpConnection bridge"); socket.abort();
TcpConnection::new(handle, proxy_ip, target_addr) }
});
conn.poll_and_process(socket);
} }
if socket.state() == tcp::State::Closed {
self.active_tcp_sessions.remove(&handle); if let Some(conn) = self.active_tcp_sessions.get_mut(&handle) {
if !conn.tick(socket) {
self.active_tcp_sessions.remove(&handle);
}
} }
} }
fn handle_udp(&mut self, handle: SocketHandle, socket: &mut udp::Socket) { fn handle_udp(&mut self, handle: SocketHandle, socket: &mut udp::Socket) {
if socket.can_recv() { self.last_activity.insert(handle, StdInstant::now());
match socket.recv() {
Ok((data, endpoint)) => { UdpConnection::process_incoming(socket, &mut self.fake_ip_store);
info!(handle=%handle, from=?endpoint, len=data.len(), "UDP: packet received");
// МОК: Обработка UDP датаграммы
}
Err(_) => {}
}
}
} }
fn handle_icmp(&mut self, handle: SocketHandle, socket: &mut icmp::Socket) { fn handle_icmp(&mut self, handle: SocketHandle, socket: &mut icmp::Socket) {
if socket.can_recv() { if socket.can_recv() {
match socket.recv() { match socket.recv() {
Ok((data, endpoint)) => { Ok((data, endpoint)) => {
debug!(handle=%handle, from=?endpoint, "ICMP: packet received"); debug!(handle=%handle, from=?endpoint, data=?data, "ICMP: packet received");
// МОК: Ответ на пинг или обработка ошибок
} }
Err(_) => {} Err(_) => {}
} }
@@ -126,106 +167,25 @@ impl ConnectionManager {
icmp::Socket::new(icmp_rx_buffer, icmp_tx_buffer) icmp::Socket::new(icmp_rx_buffer, icmp_tx_buffer)
} }
pub fn refill_sockets(&mut self, socket_set: &mut SocketSet) { pub fn setup_sockets(n_tcp: usize, n_udp: usize, n_icmp: usize) -> SocketSet<'static> {
self.prune_sockets(socket_set); // Создаем хранилище с запасом на все типы сокетов
const TARGET_FREE_TCP: usize = 16; let mut sockets = SocketSet::new(Vec::with_capacity(n_tcp + n_udp + n_icmp));
const TARGET_FREE_UDP: usize = 8;
for &port in &[80, 443, 8080] { // 1. Добавляем TCP сокеты
let current_port_sockets = socket_set for _ in 0..n_tcp {
.iter() sockets.add(Self::create_tcp_socket());
.filter(|(_, s)| {
if let Some(tcp) = tcp::Socket::downcast(s) {
// Если эндпоинт есть - сверяем порт
if let Some(endpoint) = tcp.local_endpoint() {
return endpoint.port == port;
}
// КЛЮЧЕВОЙ МОМЕНТ:
// Если эндпоинта НЕТ, но сокет НЕ в состоянии CLOSED,
// или он только что был создан для прослушивания.
// В smoltcp после listen() сокет переходит в LISTEN,
// но local_endpoint может появиться чуть позже.
tcp.state() == tcp::State::Listen
} else {
false
}
})
.count();
if current_port_sockets < 5 {
let mut s = Self::create_tcp_socket();
let endpoint = IpListenEndpoint { addr: None, port };
// Попробуй сначала listen, а потом добавлять
if s.listen(endpoint).is_ok() {
socket_set.add(s);
info!(
"--- REAL ADD --- Port: {}, Sockets for this port: {}",
port,
current_port_sockets + 1
);
}
}
} }
let udp_active = socket_set // 2. Добавляем UDP сокеты
.iter() for _ in 0..n_udp {
.filter(|(_, s)| udp::Socket::downcast(s).is_some()) sockets.add(Self::create_udp_socket());
.count();
let has_icmp = socket_set
.iter()
.any(|(_, s)| icmp::Socket::downcast(s).is_some());
if udp_active < TARGET_FREE_UDP {
let diff = TARGET_FREE_UDP - udp_active;
debug!("Refilling UDP pool: adding {} sockets", diff);
for _ in 0..diff {
let s = Self::create_udp_socket();
socket_set.add(s);
}
} }
if !has_icmp { // 3. Добавляем ICMP сокет
debug!("Adding ICMP socket for echo requests"); for _ in 0..n_icmp {
let s = Self::create_icmp_socket(); sockets.add(Self::create_icmp_socket());
socket_set.add(s);
}
}
fn prune_sockets(&mut self, socket_set: &mut SocketSet) {
let now = StdInstant::now();
let udp_timeout = Duration::from_secs(60); // 1 минута для UDP
let mut to_remove = Vec::new();
for (handle, socket) in socket_set.iter() {
if let Some(tcp) = tcp::Socket::downcast(socket) {
if tcp.state() == tcp::State::Closed {
to_remove.push(handle);
continue;
}
}
if let Some(udp) = udp::Socket::downcast(socket) {
if udp.endpoint().port == 0 {
continue;
}
let last = self.last_activity.get(&handle).unwrap_or(&START_TIME);
if now.duration_since(*last) > udp_timeout {
debug!(handle=%handle, "UDP socket timeout reached");
to_remove.push(handle);
}
}
if let Some(_icmp) = icmp::Socket::downcast(socket) {
continue;
}
} }
for handle in to_remove { sockets
socket_set.remove(handle);
self.last_activity.remove(&handle);
}
} }
} }
+2
View File
@@ -16,6 +16,7 @@ use smoltcp::{
time::Instant, time::Instant,
}; };
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tracing::trace;
pub struct VirtTunDevice { pub struct VirtTunDevice {
capabilities: DeviceCapabilities, capabilities: DeviceCapabilities,
@@ -63,6 +64,7 @@ impl Device for VirtTunDevice {
fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
if let Ok(buffer) = self.in_buf.try_recv() { if let Ok(buffer) = self.in_buf.try_recv() {
trace!("--- TUN RX: {} bytes ---", buffer.len()); // Посмотри, что именно прилетает
let rx = Self::RxToken { let rx = Self::RxToken {
buffer, buffer,
phantom_device: PhantomData, phantom_device: PhantomData,
+35 -38
View File
@@ -1,4 +1,5 @@
use smoltcp::time::Instant; use smoltcp::time::Instant;
use smoltcp::wire::{IpAddress, IpCidr};
use smoltcp::{ use smoltcp::{
iface::{Config, Interface, SocketSet}, iface::{Config, Interface, SocketSet},
phy::DeviceCapabilities, phy::DeviceCapabilities,
@@ -35,9 +36,10 @@ impl Engine {
pub fn new(config: Config, caps: DeviceCapabilities, ip: String) -> Self { pub fn new(config: Config, caps: DeviceCapabilities, ip: String) -> Self {
let now = Engine::current_time(); let now = Engine::current_time();
let (mut device, bridge_rx, bridge_tx, avail) = VirtTunDevice::new(caps); let (mut device, bridge_rx, bridge_tx, avail) = VirtTunDevice::new(caps);
let mut interface = Interface::new(config, &mut device, now); let interface = Interface::new(config, &mut device, now);
let socket_set = SocketSet::new(vec![]); let mut socket_set = ConnectionManager::setup_sockets(16, 4, 1);
let mut manager = ConnectionManager::new(ip);
Self { Self {
interface, interface,
socket_set, socket_set,
@@ -45,11 +47,12 @@ impl Engine {
bridge_tx, bridge_tx,
bridge_rx, bridge_rx,
avail, avail,
manager: ConnectionManager::new(ip), manager,
} }
} }
pub async fn run(&mut self, tun: Tun) { pub async fn run(&mut self, tun: Tun) {
info!("Current routes: {:?}", self.interface.routes());
//Bridge from tun to stack and stack to tun //Bridge from tun to stack and stack to tun
let (writer, reader) = tun.split().expect("Failed to split TUN"); let (writer, reader) = tun.split().expect("Failed to split TUN");
// Забираем bridge_rx, так как он нам нужен только в одной задаче // Забираем bridge_rx, так как он нам нужен только в одной задаче
@@ -58,25 +61,27 @@ impl Engine {
Self::spawn_engine_to_tun(writer, from_engine); Self::spawn_engine_to_tun(writer, from_engine);
loop { loop {
self.manager.refill_sockets(&mut self.socket_set); loop {
self.poll(); self.poll();
self.manager.process_sockets(&mut self.socket_set);
if self.avail.load(std::sync::atomic::Ordering::Acquire) { self.manager.process_sockets(&mut self.socket_set);
tokio::task::yield_now().await;
continue; if self.avail.load(std::sync::atomic::Ordering::Acquire) {
tokio::task::yield_now().await;
continue;
}
self.poll_delay().await;
} }
self.poll_delay().await;
} }
} }
pub fn poll(&mut self) { fn poll(&mut self) {
let now = Self::current_time(); let now = Self::current_time();
// Передаем девайс и сокеты в интерфейс
self.interface self.interface
.poll(now, &mut self.device, &mut self.socket_set); .poll(now, &mut self.device, &mut self.socket_set);
} }
pub async fn poll_delay(&mut self) { async fn poll_delay(&mut self) {
let timestamp = Self::current_time(); let timestamp = Self::current_time();
let delay = self.interface.poll_delay(timestamp, &self.socket_set); let delay = self.interface.poll_delay(timestamp, &self.socket_set);
let sleep_duration = match delay { let sleep_duration = match delay {
@@ -99,6 +104,13 @@ impl Engine {
break; break;
} }
if n >= 20 {
let dst_ip = std::net::Ipv4Addr::new(buf[16], buf[17], buf[18], buf[19]);
let dst_port = u16::from_be_bytes([buf[22], buf[23]]); // Порт для TCP
debug!("Сырой пакет: назначение {}:{}", dst_ip, dst_port);
}
let mut token = TokenBuffer::with_capacity(n); let mut token = TokenBuffer::with_capacity(n);
token.extend_from_slice(&buf[..n]); token.extend_from_slice(&buf[..n]);
@@ -132,42 +144,20 @@ impl Engine {
Instant::from_micros(duration.as_micros() as i64) Instant::from_micros(duration.as_micros() as i64)
} }
pub fn add_address(&mut self, address: smoltcp::wire::IpCidr) {
self.interface.update_ip_addrs(|addrs| {
addrs
.push(address)
.expect("Failed to add IP: address list is full");
});
self.interface
.routes_mut()
.add_default_ipv4_route(smoltcp::wire::Ipv4Address::new(10, 0, 0, 1))
.ok();
}
pub fn set_any_ip(&mut self, state: bool) -> () { pub fn set_any_ip(&mut self, state: bool) -> () {
self.interface.set_any_ip(state) self.interface.set_any_ip(state)
} }
pub fn set_transparent_mode(&mut self) { pub fn set_transparent_mode(&mut self) {
info!("Switching engine to transparent mode (AnyIP + 0.0.0.0/0)");
self.interface.set_any_ip(true);
self.interface.update_ip_addrs(|addrs| { self.interface.update_ip_addrs(|addrs| {
addrs.clear(); addrs.clear();
//accept all internet
addrs addrs
.push(smoltcp::wire::IpCidr::new( .push(IpCidr::new(IpAddress::v4(10, 0, 0, 2), 24))
smoltcp::wire::IpAddress::v4(0, 0, 0, 0), .unwrap();
0,
))
.expect("Failed to set 0.0.0.0/0");
}); });
// Устанавливаем дефолтный маршрут "в никуда" (локально)
self.interface.routes_mut().remove_default_ipv4_route(); self.interface.routes_mut().remove_default_ipv4_route();
self.interface
.routes_mut()
.add_default_ipv4_route(smoltcp::wire::Ipv4Address::new(0, 0, 0, 0))
.ok();
} }
pub fn set_default_gateway(&mut self, gateway: smoltcp::wire::Ipv4Address) { pub fn set_default_gateway(&mut self, gateway: smoltcp::wire::Ipv4Address) {
@@ -178,4 +168,11 @@ impl Engine {
.add_default_ipv4_route(gateway) .add_default_ipv4_route(gateway)
.expect("Failed to set default gateway"); .expect("Failed to set default gateway");
} }
pub fn activate(&mut self) {
let now = Self::current_time();
self.interface
.poll(now, &mut self.device, &mut self.socket_set);
self.manager.start_listening(&mut self.socket_set);
}
} }
+9 -27
View File
@@ -43,37 +43,19 @@ impl Tun {
Ok((writer, reader)) Ok((writer, reader))
} }
pub fn setup_linux_routes(&self) -> io::Result<()> { pub fn setup_routing(&self) -> io::Result<()> {
use std::process::Command; use std::process::Command;
// Направляем трафик через IP, который слушает твой smoltcp Engine // Теперь, когда мы знаем, что tun0 имеет IP 10.0.0.1,
// Предположим, smoltcp настроен на 10.0.0.2 // мы можем смело ставить его шлюзом.
let status = Command::new("ip")
.args(&["route", "add", "default", "via", "10.0.0.2", "dev", "tun0"])
.status()?;
if !status.success() { // 1. Устанавливаем маршрут через созданный интерфейс tun0
return Err(io::Error::new( let _ = Command::new("sudo")
io::ErrorKind::Other, .args(&[
"Failed to setup routes", "ip", "route", "add", "default", "via", "10.0.0.2", "dev", "tun0", "metric", "1",
)); ])
} .status();
Ok(())
}
pub fn clear_default_route(&self) -> io::Result<()> {
use std::process::Command;
info!("Removing existing default route...");
// Удаляем текущий маршрут по умолчанию
let status = Command::new("sudo")
.args(&["ip", "route", "del", "default"])
.status()?;
if !status.success() {
warn!("Could not delete default route (maybe it doesn't exist?)");
}
Ok(()) Ok(())
} }
} }
+29 -21
View File
@@ -63,7 +63,7 @@ impl SocksRequest {
pub async fn perform_client_handshake<S>( pub async fn perform_client_handshake<S>(
stream: &mut S, stream: &mut S,
target_addr: &std::net::SocketAddr, target_addr: &TargetAddress,
) -> Result<(), String> ) -> Result<(), String>
where where
S: tokio::io::AsyncReadExt + tokio::io::AsyncWriteExt + Unpin, S: tokio::io::AsyncReadExt + tokio::io::AsyncWriteExt + Unpin,
@@ -90,22 +90,31 @@ impl SocksRequest {
} }
// 3. Формируем CONNECT запрос // 3. Формируем CONNECT запрос
let mut connect_req = BytesMut::with_capacity(10); let mut connect_req = BytesMut::with_capacity(32);
connect_req.put_u8(SOCKS5_VERSION); connect_req.put_u8(SOCKS5_VERSION);
connect_req.put_u8(0x01); // CMD: Connect connect_req.put_u8(0x01); // CMD: Connect
connect_req.put_u8(0x00); // RSV connect_req.put_u8(0x00); // RSV
match target_addr { match target_addr {
std::net::SocketAddr::V4(a) => { TargetAddress::Ipv4(ip, port) => {
connect_req.put_u8(ATYP_IPV4); connect_req.put_u8(ATYP_IPV4);
connect_req.put_slice(&a.ip().octets()); connect_req.put_slice(&ip.octets());
connect_req.put_u16(*port);
} }
std::net::SocketAddr::V6(a) => { TargetAddress::Ipv6(ip, port) => {
connect_req.put_u8(ATYP_IPV6); connect_req.put_u8(ATYP_IPV6);
connect_req.put_slice(&a.ip().octets()); connect_req.put_slice(&ip.octets());
connect_req.put_u16(*port);
}
TargetAddress::Domain(host, port) => {
connect_req.put_u8(ATYP_DOMAIN);
// SOCKS5 для домена требует: [1 байт длина] + [строка]
let host_bytes = host.as_bytes();
connect_req.put_u8(host_bytes.len() as u8);
connect_req.put_slice(host_bytes);
connect_req.put_u16(*port);
} }
} }
connect_req.put_u16(target_addr.port());
stream stream
.write_all(&connect_req) .write_all(&connect_req)
@@ -163,17 +172,16 @@ pub enum SocksReply {
}, },
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum TargetAddress { pub enum TargetAddress {
Ipv4(std::net::Ipv4Addr), Ipv4(std::net::Ipv4Addr, u16), // Теперь 2 поля
Domain(String), Domain(String, u16), // Теперь 2 поля
Ipv6(std::net::Ipv6Addr), Ipv6(std::net::Ipv6Addr, u16), // Теперь 2 поля
} }
#[derive(Debug)] #[derive(Debug)]
pub struct SocksTarget { pub struct SocksTarget {
pub addr: TargetAddress, pub addr: TargetAddress,
pub port: u16,
} }
impl SocksReply { impl SocksReply {
@@ -203,20 +211,20 @@ impl SocksReply {
impl SocksTarget { impl SocksTarget {
pub fn to_string(&self) -> String { pub fn to_string(&self) -> String {
match &self.addr { match &self.addr {
// Если это IPv4, у нас уже есть объект Ipv4Addr // Теперь в каждом варианте TargetAddress уже есть порт (port)
TargetAddress::Ipv4(ip) => { TargetAddress::Ipv4(ip, port) => {
format!("{}:{}", ip, self.port) format!("{}:{}", ip, port)
} }
// Если это IPv6, объект Ipv6Addr (автоматически добавит скобки при надобности или используй формат) TargetAddress::Ipv6(ip, port) => {
TargetAddress::Ipv6(ip) => { // IPv6 адреса принято заключать в квадратные скобки при наличии порта
format!("[{}]:{}", ip, self.port) format!("[{}]:{}", ip, port)
} }
// Если это домен, то это String. Вычищаем нули на всякий случай TargetAddress::Domain(domain, port) => {
TargetAddress::Domain(domain) => { // Вычищаем нулевые байты, если они случайно попали в строку
let clean_domain = domain.replace('\0', ""); let clean_domain = domain.replace('\0', "");
format!("{}:{}", clean_domain, self.port) format!("{}:{}", clean_domain, port)
} }
} }
} }
+19 -13
View File
@@ -6,21 +6,25 @@ impl Parser for SocksTarget {
type Error = String; type Error = String;
fn can_parse(bytes: &BytesMut) -> bool { fn can_parse(bytes: &BytesMut) -> bool {
if bytes.len() < SOCKS5_MIN_HEADER { // Минимальная длина SOCKS5 CONNECT (VER, CMD, RSV, ATYP) = 4 байта
if bytes.len() < 4 {
return false; return false;
} }
let atyp = bytes[3]; let atyp = bytes[3];
match atyp { match atyp {
ATYP_IPV4 => bytes.len() >= SOCKS5_MIN_HEADER + IPV4_SIZE + PORT_SIZE, // IPv4: 4 байта IP + 2 байта порт
ATYP_IPV4 => bytes.len() >= 4 + 4 + 2,
// IPv6: 16 байт IP + 2 байта порт
ATYP_IPV6 => bytes.len() >= 4 + 16 + 2,
// Domain: 1 байт длины + N байт домена + 2 байта порт
ATYP_DOMAIN => { ATYP_DOMAIN => {
if bytes.len() < SOCKS5_MIN_HEADER + 1 { if bytes.len() < 5 {
return false; return false;
} }
let domain_len = bytes[4] as usize; let domain_len = bytes[4] as usize;
bytes.len() >= SOCKS5_MIN_HEADER + 1 + domain_len + PORT_SIZE bytes.len() >= 4 + 1 + domain_len + 2
} }
ATYP_IPV6 => bytes.len() >= SOCKS5_MIN_HEADER + IPV6_SIZE + PORT_SIZE,
_ => false, _ => false,
} }
} }
@@ -31,8 +35,7 @@ impl Parser for SocksTarget {
} }
let atyp = bytes[3]; let atyp = bytes[3];
// Вычисляем длину (включая ATYP и порт)
// Вычисляем длину еще раз для split_to (либо можно вынести в хелпер)
let total_len = match atyp { let total_len = match atyp {
ATYP_IPV4 => SOCKS5_MIN_HEADER + IPV4_SIZE + PORT_SIZE, ATYP_IPV4 => SOCKS5_MIN_HEADER + IPV4_SIZE + PORT_SIZE,
ATYP_DOMAIN => SOCKS5_MIN_HEADER + 1 + (bytes[4] as usize) + PORT_SIZE, ATYP_DOMAIN => SOCKS5_MIN_HEADER + 1 + (bytes[4] as usize) + PORT_SIZE,
@@ -41,29 +44,32 @@ impl Parser for SocksTarget {
}; };
let mut packet = bytes.split_to(total_len); let mut packet = bytes.split_to(total_len);
packet.advance(SOCKS5_MIN_HEADER); packet.advance(4); // Пропускаем [VER, CMD, RSV, ATYP]
let addr = match atyp { let addr = match atyp {
ATYP_IPV4 => { ATYP_IPV4 => {
let octets: [u8; 4] = packet.split_to(4)[..].try_into().unwrap(); let octets: [u8; 4] = packet.split_to(4)[..].try_into().unwrap();
TargetAddress::Ipv4(octets.into()) let port = packet.get_u16(); // Достаем порт сразу
TargetAddress::Ipv4(octets.into(), port)
} }
ATYP_DOMAIN => { ATYP_DOMAIN => {
let len = packet.get_u8() as usize; let len = packet.get_u8() as usize;
let domain_bytes = packet.split_to(len); let domain_bytes = packet.split_to(len);
let domain = String::from_utf8(domain_bytes.to_vec()) let domain = String::from_utf8(domain_bytes.to_vec())
.map_err(|_| "Invalid UTF-8 domain".to_string())?; .map_err(|_| "Invalid UTF-8 domain".to_string())?;
TargetAddress::Domain(domain) let port = packet.get_u16(); // Достаем порт сразу
TargetAddress::Domain(domain, port)
} }
ATYP_IPV6 => { ATYP_IPV6 => {
let octets: [u8; 16] = packet.split_to(16)[..].try_into().unwrap(); let octets: [u8; 16] = packet.split_to(16)[..].try_into().unwrap();
TargetAddress::Ipv6(octets.into()) let port = packet.get_u16(); // Достаем порт сразу
TargetAddress::Ipv6(octets.into(), port)
} }
_ => unreachable!(), _ => unreachable!(),
}; };
let port = packet.get_u16(); // Теперь SocksTarget — это просто оболочка над новым TargetAddress
Ok(Some(SocksTarget { addr, port })) Ok(Some(SocksTarget { addr }))
} }
} }