mirror of
https://github.com/jkcoxson/idevice.git
synced 2026-03-02 06:26:15 +01:00
Implement software TCP stack
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,4 +3,5 @@ BuildManifest.plist
|
||||
Image.dmg
|
||||
Image.dmg.trustcache
|
||||
.DS_Store
|
||||
*.pcap
|
||||
|
||||
|
||||
365
Cargo.lock
generated
365
Cargo.lock
generated
@@ -138,12 +138,6 @@ version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.8.0"
|
||||
@@ -184,20 +178,11 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder_slice"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b294e30387378958e8bf8f4242131b930ea615ff81e8cac2440cea0a6013190"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.9.0"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
|
||||
[[package]]
|
||||
name = "c2rust-bitfields"
|
||||
@@ -205,7 +190,16 @@ version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "367e5d1b30f28be590b6b3868da1578361d29d9bfac516d22f497d28ed7c9055"
|
||||
dependencies = [
|
||||
"c2rust-bitfields-derive",
|
||||
"c2rust-bitfields-derive 0.19.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "c2rust-bitfields"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46dc7d2bffa0d0b3d47eb2dc69973466858281446c2ac9f6d8a10e92ab1017df"
|
||||
dependencies = [
|
||||
"c2rust-bitfields-derive 0.20.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -219,6 +213,17 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "c2rust-bitfields-derive"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebe1117afa5937ce280034e31fa1e84ed1824a252f75380327eed438535333f8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.10"
|
||||
@@ -380,38 +385,6 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86f6162c53f659f65d00619fe31f14556a6e9f8752ccc4a41bd177ffcf3d6130"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"defmt-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt-macros"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d135dd939bad62d7490b0002602d35b358dce5fd9233a709d3c1ef467d4bde6"
|
||||
dependencies = [
|
||||
"defmt-parser",
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt-parser"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3983b127f13995e68c1e29071e5d115cd96f215ccb5e6812e3728cd6f92653b3"
|
||||
dependencies = [
|
||||
"thiserror 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.11"
|
||||
@@ -421,17 +394,6 @@ dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive-into-owned"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c9d94d81e3819a7b06a8638f448bc6339371ca9b6076a99d4a43eece3c4c923"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
@@ -577,6 +539,21 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.31"
|
||||
@@ -584,6 +561,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -592,6 +570,17 @@ version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.31"
|
||||
@@ -608,6 +597,17 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.31"
|
||||
@@ -626,10 +626,16 @@ version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -648,7 +654,7 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ba121d81ab5ea05b0cd5858516266800bf965531a794f7ac58e3eeb804f364f"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"bitflags",
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
@@ -701,31 +707,12 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||
dependencies = [
|
||||
"hash32",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
@@ -970,27 +957,29 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "idevice"
|
||||
version = "0.1.22"
|
||||
version = "0.1.24"
|
||||
dependencies = [
|
||||
"async-recursion",
|
||||
"base64",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"env_logger",
|
||||
"futures",
|
||||
"indexmap",
|
||||
"json",
|
||||
"log",
|
||||
"ns-keyed-archive",
|
||||
"openssl",
|
||||
"pcap-file",
|
||||
"plist",
|
||||
"rand",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"smoltcp",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-openssl",
|
||||
"tun-rs 2.0.8",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
@@ -1006,7 +995,7 @@ dependencies = [
|
||||
"plist",
|
||||
"sha2",
|
||||
"tokio",
|
||||
"tun-rs",
|
||||
"tun-rs 1.5.0",
|
||||
"ureq",
|
||||
]
|
||||
|
||||
@@ -1136,12 +1125,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "managed"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
@@ -1206,7 +1189,7 @@ version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg_aliases 0.1.1",
|
||||
"libc",
|
||||
@@ -1219,7 +1202,7 @@ version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg_aliases 0.2.1",
|
||||
"libc",
|
||||
@@ -1243,7 +1226,7 @@ checksum = "f237a10fe003123daa55a74b63a0b0a65de1671b2d128711ffe5886891a8f77f"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"plist",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1273,7 +1256,7 @@ version = "0.10.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
@@ -1350,17 +1333,6 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pcap-file"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc1f139757b058f9f37b76c48501799d12c9aa0aa4c0d4c980b062ee925d1b2"
|
||||
dependencies = [
|
||||
"byteorder_slice",
|
||||
"derive-into-owned",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
@@ -1416,25 +1388,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr2"
|
||||
version = "2.0.0"
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1464,13 +1423,43 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
dependencies = [
|
||||
"getrandom 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1573,7 +1562,7 @@ version = "0.38.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
@@ -1654,7 +1643,7 @@ version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@@ -1756,22 +1745,6 @@ version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "smoltcp"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dad095989c1533c1c266d9b1e8d70a1329dd3723c3edac6d03bbd67e7bf6f4bb"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"byteorder",
|
||||
"cfg-if",
|
||||
"defmt",
|
||||
"heapless",
|
||||
"libc",
|
||||
"log",
|
||||
"managed",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.8"
|
||||
@@ -1854,7 +1827,7 @@ version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
@@ -1883,33 +1856,13 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1966,9 +1919,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.43.0"
|
||||
version = "1.44.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
|
||||
checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
@@ -2095,11 +2048,11 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53141e64197ff7e758b8152615e50bb4a3b18c970738876e7906d31f242c7d6e"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"bitflags",
|
||||
"blocking",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"c2rust-bitfields",
|
||||
"c2rust-bitfields 0.19.0",
|
||||
"cfg-if",
|
||||
"encoding_rs",
|
||||
"getifaddrs",
|
||||
@@ -2110,13 +2063,41 @@ dependencies = [
|
||||
"mac_address",
|
||||
"nix 0.29.0",
|
||||
"scopeguard",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"windows-sys 0.59.0",
|
||||
"winreg",
|
||||
"winreg 0.52.0",
|
||||
"wintun-bindings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tun-rs"
|
||||
version = "2.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "397f7515d2ab25d87dbb89af39b6813c1df6a89703003d7939c1f6bbc0fbeed9"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"blocking",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"c2rust-bitfields 0.20.0",
|
||||
"cfg-if",
|
||||
"encoding_rs",
|
||||
"getifaddrs",
|
||||
"ipnet",
|
||||
"libc",
|
||||
"libloading",
|
||||
"log",
|
||||
"mac_address",
|
||||
"nix 0.29.0",
|
||||
"scopeguard",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"widestring",
|
||||
"windows-sys 0.59.0",
|
||||
"winreg 0.55.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
@@ -2341,6 +2322,12 @@ dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@@ -2551,16 +2538,26 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.55.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wintun-bindings"
|
||||
version = "0.7.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67a02981bed4592bcd271f9bfe154228ddbd2fd69e37a7d358da5d3a1251d696"
|
||||
dependencies = [
|
||||
"c2rust-bitfields",
|
||||
"c2rust-bitfields 0.19.0",
|
||||
"libloading",
|
||||
"log",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -2570,7 +2567,7 @@ version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2609,6 +2606,26 @@ dependencies = [
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.5"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "idevice"
|
||||
description = "A Rust library to interact with services on iOS devices."
|
||||
authors = ["Jackson Coxson"]
|
||||
version = "0.1.23"
|
||||
version = "0.1.24"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
documentation = "https://docs.rs/idevice"
|
||||
@@ -32,13 +32,15 @@ json = { version = "0.12", optional = true }
|
||||
byteorder = { version = "1.5", optional = true }
|
||||
|
||||
reqwest = { version = "0.12", features = ["json"], optional = true }
|
||||
smoltcp = { version = "0.12", optional = true }
|
||||
pcap-file = { version = "2.0", optional = true }
|
||||
rand = { version = "0.9", optional = true }
|
||||
futures = { version = "0.3", optional = true }
|
||||
|
||||
sha2 = { version = "0.10", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.43", features = ["fs"] }
|
||||
tun-rs = { version = "2.0.8", features = ["async_tokio"] }
|
||||
bytes = "1.10.1"
|
||||
|
||||
[features]
|
||||
core_device_proxy = ["dep:serde_json", "dep:json", "dep:byteorder"]
|
||||
@@ -49,7 +51,7 @@ installation_proxy = []
|
||||
misagent = []
|
||||
mounter = ["dep:sha2"]
|
||||
tcp = ["tokio/net"]
|
||||
tunnel_tcp_stack = ["dep:smoltcp", "tokio/sync", "dep:pcap-file"]
|
||||
tunnel_tcp_stack = ["dep:rand", "dep:futures"]
|
||||
tss = ["dep:uuid", "dep:reqwest"]
|
||||
tunneld = ["dep:serde_json", "dep:json", "dep:reqwest"]
|
||||
usbmuxd = ["tokio/net"]
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use log::{debug, warn};
|
||||
use pcap_file::pcapng::PcapNgWriter;
|
||||
use smoltcp::{
|
||||
phy::{self, Device, DeviceCapabilities, Medium},
|
||||
time::Instant,
|
||||
};
|
||||
use tokio::sync::{
|
||||
mpsc::{error::TryRecvError, unbounded_channel, UnboundedReceiver, UnboundedSender},
|
||||
oneshot::{channel, Sender},
|
||||
};
|
||||
|
||||
use crate::IdeviceError;
|
||||
|
||||
use super::CoreDeviceProxy;
|
||||
|
||||
pub struct ProxyDevice {
|
||||
sender: UnboundedSender<(Vec<u8>, Sender<Option<IdeviceError>>)>,
|
||||
receiver: UnboundedReceiver<Result<Vec<u8>, IdeviceError>>,
|
||||
mtu: usize,
|
||||
}
|
||||
|
||||
impl ProxyDevice {
|
||||
pub fn new(mut proxy: CoreDeviceProxy) -> Self {
|
||||
let (sender, mut stack_recv) = unbounded_channel();
|
||||
let (stack_send, receiver) = unbounded_channel();
|
||||
let mtu = proxy.mtu as usize;
|
||||
|
||||
tokio::task::spawn(async move {
|
||||
loop {
|
||||
tokio::select! {
|
||||
res = proxy.recv() => {
|
||||
// debug!("stack recv: {res:02X?}");
|
||||
if stack_send.send(res).is_err() {
|
||||
warn!("Interface failed to recv");
|
||||
break;
|
||||
}
|
||||
}
|
||||
pkt = stack_recv.recv() => {
|
||||
let pkt: (Vec<u8>, Sender<Option<IdeviceError>>) = match pkt {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
warn!("Interface sender closed");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// debug!("stack send: {:02X?}", pkt.0);
|
||||
pkt.1.send(proxy.send(&pkt.0).await.err()).ok();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Self {
|
||||
sender,
|
||||
receiver,
|
||||
mtu,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for ProxyDevice {
|
||||
type RxToken<'a> = RxToken;
|
||||
type TxToken<'a> = TxToken;
|
||||
|
||||
fn capabilities(&self) -> DeviceCapabilities {
|
||||
let mut caps = DeviceCapabilities::default();
|
||||
caps.medium = Medium::Ip;
|
||||
caps.max_transmission_unit = self.mtu;
|
||||
caps
|
||||
}
|
||||
|
||||
fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
||||
match self.receiver.try_recv() {
|
||||
Ok(Ok(buffer)) => {
|
||||
let rx = RxToken { buffer };
|
||||
let tx = TxToken {
|
||||
sender: self.sender.clone(),
|
||||
};
|
||||
Some((rx, tx))
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
warn!("Failed to recv message: {e:?}");
|
||||
None
|
||||
}
|
||||
Err(TryRecvError::Disconnected) => {
|
||||
warn!("Proxy sender is closed");
|
||||
None
|
||||
}
|
||||
Err(TryRecvError::Empty) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> {
|
||||
Some(TxToken {
|
||||
sender: self.sender.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct RxToken {
|
||||
buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl phy::RxToken for RxToken {
|
||||
fn consume<R, F>(self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&[u8]) -> R,
|
||||
{
|
||||
f(&self.buffer[..])
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct TxToken {
|
||||
sender: UnboundedSender<(Vec<u8>, Sender<Option<IdeviceError>>)>,
|
||||
}
|
||||
|
||||
impl phy::TxToken for TxToken {
|
||||
fn consume<R, F>(self, len: usize, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> R,
|
||||
{
|
||||
let mut buffer = vec![0; len];
|
||||
let result = f(&mut buffer);
|
||||
let (tx, rx) = channel();
|
||||
match self.sender.send((buffer, tx)) {
|
||||
Ok(_) => {
|
||||
if let Err(e) = rx.blocking_recv() {
|
||||
warn!("Failed to send to idevice: {e:?}");
|
||||
}
|
||||
}
|
||||
Err(err) => warn!("Failed to send: {}", err),
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
@@ -1,277 +0,0 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io::ErrorKind,
|
||||
net::{IpAddr, Ipv6Addr},
|
||||
pin::Pin,
|
||||
str::FromStr,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use log::{debug, warn};
|
||||
use smoltcp::{
|
||||
iface::{Config, Interface, SocketHandle, SocketSet},
|
||||
socket::tcp,
|
||||
time::Instant,
|
||||
wire::{IpAddress, IpCidr},
|
||||
};
|
||||
use tokio::{
|
||||
io::{self, AsyncRead, AsyncWrite},
|
||||
sync::mpsc::{error::TryRecvError, unbounded_channel, UnboundedReceiver, UnboundedSender},
|
||||
};
|
||||
|
||||
use crate::IdeviceError;
|
||||
|
||||
const TCP_BUFFER_SIZE: usize = 1024 * 1024;
|
||||
|
||||
pub struct TunnelInterface {
|
||||
thread_sender: UnboundedSender<ThreadMessage>,
|
||||
}
|
||||
|
||||
enum ThreadMessage {
|
||||
NewTcp((u16, UnboundedSender<Vec<u8>>, UnboundedReceiver<Vec<u8>>)),
|
||||
// left for possible UDP/other in the future??
|
||||
}
|
||||
|
||||
struct TunnelSocketHandle {
|
||||
handle: SocketHandle,
|
||||
sender: UnboundedSender<Vec<u8>>,
|
||||
receiver: UnboundedReceiver<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Channel {
|
||||
sender: UnboundedSender<Vec<u8>>,
|
||||
receiver: UnboundedReceiver<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl TunnelInterface {
|
||||
pub fn new(proxy: super::CoreDeviceProxy) -> Result<Self, IdeviceError> {
|
||||
let ip: IpAddr = match IpAddr::from_str(&proxy.handshake.client_parameters.address) {
|
||||
Ok(i) => i,
|
||||
Err(e) => {
|
||||
warn!("Failed to parse IP as IP address: {e:?}");
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
};
|
||||
let device_ip: Ipv6Addr = match proxy.handshake.server_address.parse() {
|
||||
Ok(d) => d,
|
||||
Err(_) => {
|
||||
warn!("Device IP isn't parsable as IPv6");
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
};
|
||||
let mut device = super::device::ProxyDevice::new(proxy);
|
||||
let config = Config::new(smoltcp::wire::HardwareAddress::Ip);
|
||||
|
||||
// Create the interface
|
||||
let mut interface = Interface::new(config, &mut device, smoltcp::time::Instant::now());
|
||||
|
||||
// Add the IPv6 address to the interface
|
||||
let ip = match ip {
|
||||
IpAddr::V4(_) => {
|
||||
warn!("IP was IPv4, not 6");
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
IpAddr::V6(ipv6_addr) => ipv6_addr,
|
||||
};
|
||||
interface.update_ip_addrs(|addrs| {
|
||||
// Add the IPv6 address with a prefix length (e.g., 64 for typical IPv6 subnets)
|
||||
addrs.push(IpCidr::new(IpAddress::Ipv6(ip), 64)).unwrap();
|
||||
});
|
||||
|
||||
let (thread_sender, mut thread_rx) = unbounded_channel();
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
// based on https://github.com/smoltcp-rs/smoltcp/blob/main/examples/client.rs
|
||||
let mut sockets = SocketSet::new(vec![]);
|
||||
let mut handles: HashMap<u16, TunnelSocketHandle> = HashMap::new();
|
||||
let mut last_port = 1024; // host to bind and index by
|
||||
loop {
|
||||
let timestamp = Instant::now();
|
||||
interface.poll(timestamp, &mut device, &mut sockets);
|
||||
|
||||
let mut to_remove = Vec::new();
|
||||
let keys: Vec<u16> = handles.keys().cloned().collect();
|
||||
|
||||
for key in keys {
|
||||
if let Some(handle) = handles.get_mut(&key) {
|
||||
let socket = sockets.get_mut::<tcp::Socket>(handle.handle);
|
||||
|
||||
if socket.may_recv() {
|
||||
if let Err(e) = socket.recv(|data| {
|
||||
if !data.is_empty() && handle.sender.send(data.to_owned()).is_err()
|
||||
{
|
||||
warn!("Handle dropped");
|
||||
to_remove.push(key);
|
||||
}
|
||||
(data.len(), data)
|
||||
}) {
|
||||
warn!("Disconnected from socket: {e:?}");
|
||||
to_remove.push(key);
|
||||
}
|
||||
if socket.can_send() {
|
||||
match handle.receiver.try_recv() {
|
||||
Ok(data) => {
|
||||
let queued = socket.send_slice(&data[..]).unwrap();
|
||||
if queued < data.len() {
|
||||
log::error!("Failed to queue packet for send");
|
||||
}
|
||||
}
|
||||
Err(TryRecvError::Empty) => {} // wait
|
||||
Err(TryRecvError::Disconnected) => {
|
||||
debug!("handle is dropped");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
warn!("Can't send!!");
|
||||
}
|
||||
} else if socket.may_send() {
|
||||
debug!("close");
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the requested threads
|
||||
for t in to_remove {
|
||||
if let Some(h) = handles.remove(&t) {
|
||||
sockets.remove(h.handle);
|
||||
// When the struct is dropped, the unbounded_channel will close.
|
||||
// Because the channel is closed, the next time recv or send is called will
|
||||
// error.
|
||||
}
|
||||
}
|
||||
|
||||
match thread_rx.try_recv() {
|
||||
Ok(msg) => match msg {
|
||||
ThreadMessage::NewTcp((port, tx, rx)) => {
|
||||
// Create sockets
|
||||
let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; TCP_BUFFER_SIZE]);
|
||||
let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; TCP_BUFFER_SIZE]);
|
||||
let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer);
|
||||
let handle = sockets.add(tcp_socket);
|
||||
let socket = sockets.get_mut::<tcp::Socket>(handle);
|
||||
socket
|
||||
.connect(interface.context(), (device_ip, port), last_port)
|
||||
.unwrap();
|
||||
handles.insert(
|
||||
last_port,
|
||||
TunnelSocketHandle {
|
||||
handle,
|
||||
sender: tx,
|
||||
receiver: rx,
|
||||
},
|
||||
);
|
||||
|
||||
last_port += 1;
|
||||
}
|
||||
},
|
||||
Err(TryRecvError::Disconnected) => {
|
||||
debug!("Thread sender dropped");
|
||||
break;
|
||||
}
|
||||
Err(TryRecvError::Empty) => {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
let poll_delay = interface
|
||||
.poll_delay(timestamp, &sockets)
|
||||
.unwrap_or(smoltcp::time::Duration::from_millis(100))
|
||||
.millis();
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_millis(poll_delay));
|
||||
}
|
||||
});
|
||||
|
||||
Ok(Self { thread_sender })
|
||||
}
|
||||
|
||||
pub fn connect_tcp(&mut self, port: u16) -> Result<Channel, IdeviceError> {
|
||||
let (tx, rx) = unbounded_channel();
|
||||
let (rtx, rrx) = unbounded_channel();
|
||||
if self
|
||||
.thread_sender
|
||||
.send(ThreadMessage::NewTcp((port, rtx, rx)))
|
||||
.is_err()
|
||||
{
|
||||
return Err(IdeviceError::TunnelThreadClosed);
|
||||
}
|
||||
|
||||
Ok(Channel {
|
||||
sender: tx,
|
||||
receiver: rrx,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
pub async fn send(&self, data: Vec<u8>) -> Result<(), IdeviceError> {
|
||||
if let Err(e) = self.sender.send(data) {
|
||||
warn!("Failed to send: {e:?}");
|
||||
return Err(IdeviceError::ChannelClosed);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn recv(&mut self) -> Result<Vec<u8>, IdeviceError> {
|
||||
match self.receiver.recv().await {
|
||||
Some(d) => Ok(d),
|
||||
None => {
|
||||
warn!("Failed to recv");
|
||||
Err(IdeviceError::ChannelClosed)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for Channel {
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut tokio::io::ReadBuf<'_>,
|
||||
) -> Poll<io::Result<()>> {
|
||||
match Pin::new(&mut self.receiver).poll_recv(cx) {
|
||||
Poll::Ready(Some(data)) => {
|
||||
let len = data.len().min(buf.remaining());
|
||||
buf.put_slice(&data[..len]);
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
Poll::Ready(None) => {
|
||||
warn!("unexpected eof");
|
||||
Poll::Ready(Err(io::Error::new(
|
||||
ErrorKind::UnexpectedEof,
|
||||
"Channel closed",
|
||||
)))
|
||||
}
|
||||
Poll::Pending => Poll::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for Channel {
|
||||
fn poll_write(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
let data = buf.to_vec();
|
||||
match self.sender.send(data) {
|
||||
Ok(_) => Poll::Ready(Ok(buf.len())),
|
||||
Err(e) => {
|
||||
warn!("Failed to send data: {e:?}");
|
||||
Poll::Ready(Err(io::Error::new(ErrorKind::BrokenPipe, "Channel closed")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,5 @@
|
||||
// Jackson Coxson
|
||||
|
||||
#[cfg(feature = "tunnel_tcp_stack")]
|
||||
mod device;
|
||||
#[cfg(feature = "tunnel_tcp_stack")]
|
||||
mod interface;
|
||||
|
||||
use crate::{lockdownd::LockdowndClient, Idevice, IdeviceError, IdeviceService};
|
||||
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
@@ -167,7 +162,17 @@ impl CoreDeviceProxy {
|
||||
}
|
||||
|
||||
#[cfg(feature = "tunnel_tcp_stack")]
|
||||
pub fn create_software_tunnel(self) -> Result<interface::TunnelInterface, IdeviceError> {
|
||||
interface::TunnelInterface::new(self)
|
||||
pub fn create_software_tunnel(self) -> Result<crate::tcp::adapter::Adapter, IdeviceError> {
|
||||
let our_ip = self
|
||||
.handshake
|
||||
.client_parameters
|
||||
.address
|
||||
.parse::<std::net::IpAddr>()?;
|
||||
let their_ip = self.handshake.server_address.parse::<std::net::IpAddr>()?;
|
||||
Ok(crate::tcp::adapter::Adapter::new(
|
||||
Box::new(self.idevice.socket.unwrap()),
|
||||
our_ip,
|
||||
their_ip,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ use crate::{IdeviceError, ReadWrite};
|
||||
|
||||
pub const SERVICE_NAME: &str = "com.apple.internal.dt.remote.debugproxy";
|
||||
|
||||
pub struct DebugProxyClient {
|
||||
pub socket: Box<dyn ReadWrite>,
|
||||
pub struct DebugProxyClient<R: ReadWrite> {
|
||||
pub socket: R,
|
||||
pub noack_mode: bool,
|
||||
}
|
||||
|
||||
@@ -25,14 +25,18 @@ impl DebugserverCommand {
|
||||
}
|
||||
}
|
||||
|
||||
impl DebugProxyClient {
|
||||
pub fn new(socket: Box<dyn ReadWrite>) -> Self {
|
||||
impl<R: ReadWrite> DebugProxyClient<R> {
|
||||
pub fn new(socket: R) -> Self {
|
||||
Self {
|
||||
socket,
|
||||
noack_mode: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> R {
|
||||
self.socket
|
||||
}
|
||||
|
||||
pub async fn send_command(
|
||||
&mut self,
|
||||
command: DebugserverCommand,
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
use log::warn;
|
||||
use plist::{Dictionary, Value};
|
||||
|
||||
use crate::{dvt::message::AuxValue, IdeviceError};
|
||||
use crate::{dvt::message::AuxValue, IdeviceError, ReadWrite};
|
||||
|
||||
use super::remote_server::{Channel, RemoteServerClient};
|
||||
|
||||
const IDENTIFIER: &str = "com.apple.instruments.server.services.processcontrol";
|
||||
|
||||
pub struct ProcessControlClient<'a> {
|
||||
channel: Channel<'a>,
|
||||
pub struct ProcessControlClient<'a, R: ReadWrite> {
|
||||
channel: Channel<'a, R>,
|
||||
}
|
||||
|
||||
impl<'a> ProcessControlClient<'a> {
|
||||
pub async fn new(client: &'a mut RemoteServerClient) -> Result<Self, IdeviceError> {
|
||||
impl<'a, R: ReadWrite> ProcessControlClient<'a, R> {
|
||||
pub async fn new(client: &'a mut RemoteServerClient<R>) -> Result<Self, IdeviceError> {
|
||||
let channel = client.make_channel(IDENTIFIER).await?; // Drop `&mut client` before continuing
|
||||
|
||||
Ok(Self { channel })
|
||||
|
||||
@@ -14,20 +14,20 @@ use super::message::AuxValue;
|
||||
|
||||
pub const INSTRUMENTS_MESSAGE_TYPE: u32 = 2;
|
||||
|
||||
pub struct RemoteServerClient {
|
||||
idevice: Box<dyn ReadWrite>,
|
||||
pub struct RemoteServerClient<R: ReadWrite> {
|
||||
idevice: R,
|
||||
current_message: u32,
|
||||
new_channel: u32,
|
||||
channels: HashMap<u32, VecDeque<Message>>,
|
||||
}
|
||||
|
||||
pub struct Channel<'a> {
|
||||
client: &'a mut RemoteServerClient,
|
||||
pub struct Channel<'a, R: ReadWrite> {
|
||||
client: &'a mut RemoteServerClient<R>,
|
||||
channel: u32,
|
||||
}
|
||||
|
||||
impl RemoteServerClient {
|
||||
pub fn new(idevice: Box<dyn ReadWrite>) -> Result<Self, IdeviceError> {
|
||||
impl<R: ReadWrite> RemoteServerClient<R> {
|
||||
pub fn new(idevice: R) -> Result<Self, IdeviceError> {
|
||||
let mut channels = HashMap::new();
|
||||
channels.insert(0, VecDeque::new());
|
||||
Ok(Self {
|
||||
@@ -38,7 +38,7 @@ impl RemoteServerClient {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn root_channel(&mut self) -> Channel {
|
||||
pub fn root_channel(&mut self) -> Channel<R> {
|
||||
Channel {
|
||||
client: self,
|
||||
channel: 0,
|
||||
@@ -48,7 +48,7 @@ impl RemoteServerClient {
|
||||
pub async fn make_channel(
|
||||
&mut self,
|
||||
identifier: impl Into<String>,
|
||||
) -> Result<Channel, IdeviceError> {
|
||||
) -> Result<Channel<R>, IdeviceError> {
|
||||
let code = self.new_channel;
|
||||
self.new_channel += 1;
|
||||
|
||||
@@ -78,7 +78,7 @@ impl RemoteServerClient {
|
||||
self.build_channel(code)
|
||||
}
|
||||
|
||||
fn build_channel(&mut self, code: u32) -> Result<Channel, IdeviceError> {
|
||||
fn build_channel(&mut self, code: u32) -> Result<Channel<R>, IdeviceError> {
|
||||
Ok(Channel {
|
||||
client: self,
|
||||
channel: code,
|
||||
@@ -135,7 +135,7 @@ impl RemoteServerClient {
|
||||
}
|
||||
}
|
||||
|
||||
impl Channel<'_> {
|
||||
impl<R: ReadWrite> Channel<'_, R> {
|
||||
pub async fn read_message(&mut self) -> Result<Message, IdeviceError> {
|
||||
self.client.read_message(self.channel).await
|
||||
}
|
||||
|
||||
@@ -17,20 +17,22 @@ use h2::{
|
||||
HTTP2_MAGIC,
|
||||
};
|
||||
|
||||
use crate::ReadWrite;
|
||||
|
||||
pub type Channels = HashMap<u32, (Sender<Vec<u8>>, Receiver<Vec<u8>>)>;
|
||||
|
||||
pub struct Connection {
|
||||
stream: crate::IdeviceSocket,
|
||||
channels: Channels,
|
||||
window_size: u32,
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
pub const INIT_STREAM: u32 = 0;
|
||||
pub const ROOT_CHANNEL: u32 = 1;
|
||||
pub const REPLY_CHANNEL: u32 = 3;
|
||||
|
||||
pub async fn new(mut stream: crate::IdeviceSocket) -> Result<Self, Http2Error> {
|
||||
pub struct Connection<R: ReadWrite> {
|
||||
pub stream: R,
|
||||
channels: Channels,
|
||||
window_size: u32,
|
||||
}
|
||||
|
||||
impl<R: ReadWrite> Connection<R> {
|
||||
pub async fn new(mut stream: R) -> Result<Self, Http2Error> {
|
||||
stream.write_all(HTTP2_MAGIC).await?;
|
||||
Ok(Self {
|
||||
stream,
|
||||
@@ -181,12 +183,12 @@ mod tests {
|
||||
// apart of spec we are allowed to send frames before reading any from the server.
|
||||
// 'INIT_STREAM'/0 applies to all stream_ids.
|
||||
client
|
||||
.send_frame(WindowUpdateFrame::new(Connection::INIT_STREAM, 983041))
|
||||
.send_frame(WindowUpdateFrame::new(INIT_STREAM, 983041))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// We create stream_id '1' by sending Header frame.
|
||||
let mut frame = Frame::new(Connection::ROOT_CHANNEL, 5, FrameType::Headers);
|
||||
let mut frame = Frame::new(ROOT_CHANNEL, 5, FrameType::Headers);
|
||||
frame.set_body(
|
||||
[
|
||||
0x41, 0x89, 0x2, 0xe0, 0x5c, 0xb, 0x82, 0xe0, 0x40, 0x10, 0x7f, 0x82, 0x84, 0x86,
|
||||
@@ -199,12 +201,12 @@ mod tests {
|
||||
// when server sends 'Settings' on a streamId that the client hasn't sent one on.
|
||||
// then we must send them back one.
|
||||
client
|
||||
.send_frame(Frame::new(Connection::ROOT_CHANNEL, 1, FrameType::Settings))
|
||||
.send_frame(Frame::new(ROOT_CHANNEL, 1, FrameType::Settings))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
client
|
||||
.write_streamid(Connection::ROOT_CHANNEL, b"nibba\x00".to_vec())
|
||||
.write_streamid(ROOT_CHANNEL, b"nibba\x00".to_vec())
|
||||
.await
|
||||
.unwrap();
|
||||
// 'END_HEADERS' is sent before data.
|
||||
|
||||
@@ -19,6 +19,8 @@ pub mod misagent;
|
||||
pub mod mounter;
|
||||
pub mod pairing_file;
|
||||
pub mod provider;
|
||||
#[cfg(feature = "tunnel_tcp_stack")]
|
||||
pub mod tcp;
|
||||
#[cfg(feature = "tss")]
|
||||
pub mod tss;
|
||||
#[cfg(feature = "tunneld")]
|
||||
@@ -313,22 +315,13 @@ pub enum IdeviceError {
|
||||
#[error("unknown channel")]
|
||||
UnknownChannel(u32),
|
||||
|
||||
#[error("cannot parse string as IpAddr")]
|
||||
AddrParseError(#[from] std::net::AddrParseError),
|
||||
|
||||
#[cfg(feature = "dvt")]
|
||||
#[error("disable memory limit failed")]
|
||||
DisableMemoryLimitFailed,
|
||||
|
||||
#[cfg(feature = "tunnel_tcp_stack")]
|
||||
#[error("failed to connect to TCP socket")]
|
||||
ConnectionError(#[from] smoltcp::socket::tcp::ConnectError),
|
||||
|
||||
#[cfg(feature = "tunnel_tcp_stack")]
|
||||
#[error("tunnel thread is closed")]
|
||||
TunnelThreadClosed,
|
||||
|
||||
#[cfg(feature = "tunnel_tcp_stack")]
|
||||
#[error("channel is closed")]
|
||||
ChannelClosed,
|
||||
|
||||
#[error("not enough bytes, expected {1}, got {0}")]
|
||||
NotEnoughBytes(usize, usize),
|
||||
|
||||
|
||||
399
idevice/src/tcp/adapter.rs
Normal file
399
idevice/src/tcp/adapter.rs
Normal file
@@ -0,0 +1,399 @@
|
||||
// Jackson Coxson
|
||||
// Simpler TCP stack
|
||||
|
||||
use std::{future::Future, net::IpAddr, path::Path, sync::Arc, task::Poll};
|
||||
|
||||
use log::trace;
|
||||
use tokio::{
|
||||
io::{AsyncRead, AsyncWrite, AsyncWriteExt},
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
use crate::ReadWrite;
|
||||
|
||||
use super::packets::{Ipv4Packet, Ipv6Packet, ProtocolNumber, TcpFlags, TcpPacket};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum AdapterState {
|
||||
Connected,
|
||||
None,
|
||||
}
|
||||
|
||||
/// An extremely naive, limited and dangerous stack
|
||||
/// Only one connection can be live at a time
|
||||
/// Acks aren't tracked, and are silently ignored
|
||||
/// Only use when the underlying transport is 100% reliable
|
||||
#[derive(Debug)]
|
||||
pub struct Adapter {
|
||||
peer: Box<dyn ReadWrite>,
|
||||
host_ip: IpAddr,
|
||||
peer_ip: IpAddr,
|
||||
state: AdapterState,
|
||||
|
||||
// TCP state
|
||||
seq: u32,
|
||||
ack: u32,
|
||||
host_port: u16,
|
||||
peer_port: u16,
|
||||
|
||||
// Read buffer to cache unused bytes
|
||||
read_buffer: Vec<u8>,
|
||||
write_buffer: Vec<u8>,
|
||||
|
||||
// Logging
|
||||
pcap: Option<Arc<Mutex<tokio::fs::File>>>,
|
||||
}
|
||||
|
||||
impl Adapter {
|
||||
pub fn new(peer: Box<dyn ReadWrite>, host_ip: IpAddr, peer_ip: IpAddr) -> Self {
|
||||
Self {
|
||||
peer,
|
||||
host_ip,
|
||||
peer_ip,
|
||||
state: AdapterState::None,
|
||||
seq: 0,
|
||||
ack: 0,
|
||||
host_port: 1024,
|
||||
peer_port: 1024,
|
||||
read_buffer: Vec::new(),
|
||||
write_buffer: Vec::new(),
|
||||
pcap: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn connect(&mut self, port: u16) -> Result<(), std::io::Error> {
|
||||
self.read_buffer = Vec::new();
|
||||
|
||||
// Randomize seq
|
||||
self.seq = rand::random();
|
||||
self.ack = 0;
|
||||
|
||||
// Choose a random port
|
||||
self.host_port = rand::random();
|
||||
self.peer_port = port;
|
||||
|
||||
// Create the TCP packet
|
||||
let tcp_packet = TcpPacket::create(
|
||||
self.host_ip,
|
||||
self.peer_ip,
|
||||
self.host_port,
|
||||
self.peer_port,
|
||||
self.seq,
|
||||
self.ack,
|
||||
TcpFlags {
|
||||
syn: true,
|
||||
..Default::default()
|
||||
},
|
||||
u16::MAX - 1,
|
||||
&[],
|
||||
);
|
||||
let ip_packet = self.ip_wrap(&tcp_packet);
|
||||
self.peer.write_all(&ip_packet).await?;
|
||||
self.log_packet(&ip_packet).await?;
|
||||
|
||||
// Wait for the syn ack
|
||||
let res = self.read_tcp_packet().await?;
|
||||
if !(res.flags.syn && res.flags.ack) {
|
||||
log::error!("Didn't get syn ack");
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"No syn ack",
|
||||
));
|
||||
}
|
||||
self.seq = self.seq.wrapping_add(1);
|
||||
|
||||
// Ack back
|
||||
self.ack().await?;
|
||||
|
||||
self.state = AdapterState::Connected;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn pcap(&mut self, path: impl AsRef<Path>) -> Result<(), std::io::Error> {
|
||||
let mut file = tokio::fs::File::create(path).await?;
|
||||
|
||||
// https://wiki.wireshark.org/Development/LibpcapFileFormat
|
||||
file.write_all(&0xa1b2c3d4_u32.to_le_bytes()).await?; // magic
|
||||
file.write_all(&2_u16.to_le_bytes()).await?; // major version
|
||||
file.write_all(&4_u16.to_le_bytes()).await?; // minor
|
||||
file.write_all(&0_i32.to_le_bytes()).await?; // timezone
|
||||
file.write_all(&0_u32.to_le_bytes()).await?; // accuracy
|
||||
file.write_all(&(u16::MAX as u32).to_le_bytes()).await?; // snaplen
|
||||
// https://www.tcpdump.org/linktypes.html
|
||||
file.write_all(&101_u32.to_le_bytes()).await?; // link type
|
||||
|
||||
self.pcap = Some(Arc::new(Mutex::new(file)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn log_packet(&mut self, packet: &[u8]) -> Result<(), std::io::Error> {
|
||||
if let Some(file) = &self.pcap {
|
||||
super::log_packet(file, packet).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn close(&mut self) -> Result<(), std::io::Error> {
|
||||
let tcp_packet = TcpPacket::create(
|
||||
self.host_ip,
|
||||
self.peer_ip,
|
||||
self.host_port,
|
||||
self.peer_port,
|
||||
self.seq,
|
||||
self.ack,
|
||||
TcpFlags {
|
||||
fin: true,
|
||||
..Default::default()
|
||||
},
|
||||
u16::MAX - 1,
|
||||
&[],
|
||||
);
|
||||
let ip_packet = self.ip_wrap(&tcp_packet);
|
||||
self.peer.write_all(&ip_packet).await?;
|
||||
self.log_packet(&ip_packet).await?;
|
||||
self.state = AdapterState::None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn ack(&mut self) -> Result<(), std::io::Error> {
|
||||
let tcp_packet = TcpPacket::create(
|
||||
self.host_ip,
|
||||
self.peer_ip,
|
||||
self.host_port,
|
||||
self.peer_port,
|
||||
self.seq,
|
||||
self.ack,
|
||||
TcpFlags {
|
||||
ack: true,
|
||||
..Default::default()
|
||||
},
|
||||
u16::MAX - 1,
|
||||
&[],
|
||||
);
|
||||
let ip_packet = self.ip_wrap(&tcp_packet);
|
||||
self.peer.write_all(&ip_packet).await?;
|
||||
self.log_packet(&ip_packet).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sends a packet
|
||||
pub async fn psh(&mut self, data: &[u8]) -> Result<(), std::io::Error> {
|
||||
trace!("pshing {} bytes", data.len());
|
||||
let tcp_packet = TcpPacket::create(
|
||||
self.host_ip,
|
||||
self.peer_ip,
|
||||
self.host_port,
|
||||
self.peer_port,
|
||||
self.seq,
|
||||
self.ack,
|
||||
TcpFlags {
|
||||
psh: true,
|
||||
ack: true,
|
||||
..Default::default()
|
||||
},
|
||||
u16::MAX - 1,
|
||||
data,
|
||||
);
|
||||
let ip_packet = self.ip_wrap(&tcp_packet);
|
||||
self.peer.write_all(&ip_packet).await?;
|
||||
self.log_packet(&ip_packet).await?;
|
||||
|
||||
self.seq = self.seq.wrapping_add(data.len() as u32);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Flushes the packets
|
||||
async fn write_buffer_flush(&mut self) -> Result<(), std::io::Error> {
|
||||
if self.write_buffer.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
trace!("Flushing {} bytes", self.write_buffer.len());
|
||||
let write_buffer = self.write_buffer.clone();
|
||||
self.psh(&write_buffer).await?;
|
||||
self.write_buffer = Vec::new();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn recv(&mut self) -> Result<Vec<u8>, std::io::Error> {
|
||||
loop {
|
||||
let res = self.read_tcp_packet().await?;
|
||||
if res.flags.psh || !res.payload.is_empty() {
|
||||
self.ack().await?;
|
||||
break Ok(res.payload);
|
||||
}
|
||||
if res.flags.rst {
|
||||
self.state = AdapterState::None;
|
||||
break Err(std::io::Error::new(
|
||||
std::io::ErrorKind::ConnectionReset,
|
||||
"Connection reset",
|
||||
));
|
||||
}
|
||||
if res.flags.fin {
|
||||
self.ack().await?;
|
||||
self.state = AdapterState::None;
|
||||
break Err(std::io::Error::new(
|
||||
std::io::ErrorKind::ConnectionReset,
|
||||
"Connection reset",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a packet and returns the payload
|
||||
async fn read_ip_packet(&mut self) -> Result<Vec<u8>, std::io::Error> {
|
||||
self.write_buffer_flush().await?;
|
||||
Ok(loop {
|
||||
match self.host_ip {
|
||||
IpAddr::V4(_) => {
|
||||
let packet = Ipv4Packet::from_reader(&mut self.peer, &self.pcap).await?;
|
||||
trace!("IPv4 packet: {packet:#?}");
|
||||
if packet.protocol == 6 {
|
||||
break packet.payload;
|
||||
}
|
||||
}
|
||||
IpAddr::V6(_) => {
|
||||
let packet = Ipv6Packet::from_reader(&mut self.peer, &self.pcap).await?;
|
||||
trace!("IPv6 packet: {packet:#?}");
|
||||
if packet.next_header == 6 {
|
||||
break packet.payload;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async fn read_tcp_packet(&mut self) -> Result<TcpPacket, std::io::Error> {
|
||||
let ip_packet = self.read_ip_packet().await?;
|
||||
let tcp_packet = TcpPacket::parse(&ip_packet)?;
|
||||
trace!("TCP packet: {tcp_packet:#?}");
|
||||
self.ack = tcp_packet.sequence_number
|
||||
+ if tcp_packet.payload.is_empty() {
|
||||
1
|
||||
} else {
|
||||
tcp_packet.payload.len() as u32
|
||||
};
|
||||
Ok(tcp_packet)
|
||||
}
|
||||
|
||||
fn ip_wrap(&self, packet: &[u8]) -> Vec<u8> {
|
||||
match self.host_ip {
|
||||
IpAddr::V4(host_addr) => match self.peer_ip {
|
||||
IpAddr::V4(peer_addr) => {
|
||||
Ipv4Packet::create(host_addr, peer_addr, ProtocolNumber::Tcp, 255, packet)
|
||||
}
|
||||
IpAddr::V6(_) => {
|
||||
panic!("non matching IP versions");
|
||||
}
|
||||
},
|
||||
IpAddr::V6(host_addr) => match self.peer_ip {
|
||||
IpAddr::V4(_) => {
|
||||
panic!("non matching IP versions")
|
||||
}
|
||||
IpAddr::V6(peer_addr) => {
|
||||
Ipv6Packet::create(host_addr, peer_addr, ProtocolNumber::Tcp, 255, packet)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for Adapter {
|
||||
fn poll_read(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
buf: &mut tokio::io::ReadBuf<'_>,
|
||||
) -> std::task::Poll<std::io::Result<()>> {
|
||||
// First, check if we have any cached data
|
||||
if !self.read_buffer.is_empty() {
|
||||
let to_copy = std::cmp::min(buf.remaining(), self.read_buffer.len());
|
||||
buf.put_slice(&self.read_buffer[..to_copy]);
|
||||
|
||||
// Keep any remaining data in the buffer
|
||||
if to_copy < self.read_buffer.len() {
|
||||
self.read_buffer = self.read_buffer[to_copy..].to_vec();
|
||||
} else {
|
||||
self.read_buffer.clear();
|
||||
}
|
||||
|
||||
return std::task::Poll::Ready(Ok(()));
|
||||
}
|
||||
|
||||
// If no cached data and not connected, return error
|
||||
if self.state != AdapterState::Connected {
|
||||
return std::task::Poll::Ready(Err(std::io::Error::new(
|
||||
std::io::ErrorKind::NotConnected,
|
||||
"Adapter not connected",
|
||||
)));
|
||||
}
|
||||
|
||||
// If no cached data, try to receive new data
|
||||
let future = async {
|
||||
match self.recv().await {
|
||||
Ok(data) => {
|
||||
let len = std::cmp::min(buf.remaining(), data.len());
|
||||
buf.put_slice(&data[..len]);
|
||||
|
||||
// If we received more data than needed, cache the rest
|
||||
if len < data.len() {
|
||||
self.read_buffer = data[len..].to_vec();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
};
|
||||
|
||||
// Pin the future and poll it
|
||||
futures::pin_mut!(future);
|
||||
future.poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for Adapter {
|
||||
fn poll_write(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
_cx: &mut std::task::Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> std::task::Poll<Result<usize, std::io::Error>> {
|
||||
trace!("poll psh {}", buf.len());
|
||||
if self.state != AdapterState::Connected {
|
||||
return std::task::Poll::Ready(Err(std::io::Error::new(
|
||||
std::io::ErrorKind::NotConnected,
|
||||
"Adapter not connected",
|
||||
)));
|
||||
}
|
||||
self.write_buffer.extend_from_slice(buf);
|
||||
Poll::Ready(Ok(buf.len()))
|
||||
}
|
||||
|
||||
fn poll_flush(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Result<(), std::io::Error>> {
|
||||
let future = async {
|
||||
match self.write_buffer_flush().await {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
};
|
||||
|
||||
// Pin the future and poll it
|
||||
futures::pin_mut!(future);
|
||||
future.poll(cx)
|
||||
}
|
||||
|
||||
fn poll_shutdown(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Result<(), std::io::Error>> {
|
||||
// Create a future that can be polled
|
||||
let future = async { self.close().await };
|
||||
|
||||
// Pin the future and poll it
|
||||
futures::pin_mut!(future);
|
||||
future.poll(cx)
|
||||
}
|
||||
}
|
||||
231
idevice/src/tcp/mod.rs
Normal file
231
idevice/src/tcp/mod.rs
Normal file
@@ -0,0 +1,231 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use std::{
|
||||
sync::Arc,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use log::debug;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
pub mod adapter;
|
||||
pub mod packets;
|
||||
|
||||
pub(crate) async fn log_packet(file: &Arc<tokio::sync::Mutex<tokio::fs::File>>, packet: &[u8]) {
|
||||
debug!("Logging {} byte packet", packet.len());
|
||||
let packet = packet.to_vec();
|
||||
let file = file.to_owned();
|
||||
tokio::task::spawn(async move {
|
||||
let mut file = file.lock().await;
|
||||
file.write_all(
|
||||
&(SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs() as u32)
|
||||
.to_le_bytes(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let micros = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_micros()
|
||||
% 1_000_000_000;
|
||||
file.write_all(&(micros as u32).to_le_bytes())
|
||||
.await
|
||||
.unwrap();
|
||||
file.write_all(&(packet.len() as u32).to_le_bytes())
|
||||
.await
|
||||
.unwrap();
|
||||
file.write_all(&(packet.len() as u32).to_le_bytes())
|
||||
.await
|
||||
.unwrap();
|
||||
file.write_all(&packet).await.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{
|
||||
net::{IpAddr, Ipv6Addr},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
use adapter::Adapter;
|
||||
use std::{
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tun_rs::DeviceBuilder;
|
||||
|
||||
use bytes::BytesMut;
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use tun_rs::AsyncDevice;
|
||||
|
||||
pub struct AsyncDeviceWrapper {
|
||||
device: AsyncDevice,
|
||||
// Buffer to store unread data
|
||||
buffer: BytesMut,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for AsyncDeviceWrapper {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("AsyncDeviceWrapper")
|
||||
.field("buffer", &self.buffer)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for AsyncDeviceWrapper {
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut ReadBuf<'_>,
|
||||
) -> Poll<std::io::Result<()>> {
|
||||
// First, check if we have data in our buffer
|
||||
let this = self.get_mut();
|
||||
|
||||
if !this.buffer.is_empty() {
|
||||
// We have buffered data, copy as much as possible to the output buffer
|
||||
let bytes_to_copy = std::cmp::min(this.buffer.len(), buf.remaining());
|
||||
let data_to_copy = this.buffer.split_to(bytes_to_copy);
|
||||
buf.put_slice(&data_to_copy);
|
||||
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
|
||||
// If our buffer is empty, try to read more data
|
||||
let mut temp_buf = vec![0u8; 4096]; // Temporary buffer with reasonable size
|
||||
|
||||
match this.device.poll_recv(cx, &mut temp_buf) {
|
||||
Poll::Ready(Ok(n)) => {
|
||||
if n > 0 {
|
||||
// Got some data, first fill the output buffer
|
||||
let bytes_to_copy = std::cmp::min(n, buf.remaining());
|
||||
buf.put_slice(&temp_buf[..bytes_to_copy]);
|
||||
|
||||
// If we have more data than fits in the output buffer, store in our internal buffer
|
||||
if n > bytes_to_copy {
|
||||
this.buffer.extend_from_slice(&temp_buf[bytes_to_copy..n]);
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
} else {
|
||||
// Zero bytes read
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
|
||||
Poll::Pending => Poll::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for AsyncDeviceWrapper {
|
||||
fn poll_write(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> std::task::Poll<Result<usize, std::io::Error>> {
|
||||
self.device.poll_send(cx, buf)
|
||||
}
|
||||
|
||||
fn poll_flush(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
_cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Result<(), std::io::Error>> {
|
||||
std::task::Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn poll_shutdown(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
_cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Result<(), std::io::Error>> {
|
||||
std::task::Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
const SERVER_PORT: u16 = 5555;
|
||||
|
||||
#[tokio::test]
|
||||
async fn local_tcp() {
|
||||
env_logger::init();
|
||||
|
||||
let our_ip = Ipv6Addr::from_str("fd12:3456:789a::1").unwrap();
|
||||
let their_ip = Ipv6Addr::from_str("fd12:3456:789a::2").unwrap();
|
||||
let dev = DeviceBuilder::new()
|
||||
.ipv6(their_ip, "ffff:ffff:ffff:ffff::")
|
||||
.mtu(1420)
|
||||
.build_async()
|
||||
.expect("Failed to create tunnel. Are you root?");
|
||||
|
||||
println!("Created tunnel [{:?}] {}", dev.name(), their_ip);
|
||||
|
||||
let mut adapter = Adapter::new(
|
||||
Box::new(AsyncDeviceWrapper {
|
||||
device: dev,
|
||||
buffer: BytesMut::new(),
|
||||
}),
|
||||
IpAddr::V6(our_ip),
|
||||
IpAddr::V6(their_ip),
|
||||
);
|
||||
adapter.pcap("./local_tcp.pcap").await.expect("no pcap");
|
||||
|
||||
tokio::task::spawn(async move {
|
||||
let listener = tokio::net::TcpListener::bind(format!("[::0]:{SERVER_PORT}"))
|
||||
.await
|
||||
.unwrap();
|
||||
while let Ok((mut stream, addr)) = listener.accept().await {
|
||||
println!("Accepted connection from {addr:?}");
|
||||
|
||||
tokio::task::spawn(async move {
|
||||
loop {
|
||||
let mut buf = [0; 1024];
|
||||
let read_len = stream.read(&mut buf).await.unwrap();
|
||||
stream.write_all(&buf[..read_len]).await.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
println!("Attach Wireshark, press enter to continue\n");
|
||||
let mut buf = Vec::new();
|
||||
let _ = tokio::io::stdin().read(&mut buf).await.unwrap();
|
||||
|
||||
if let Err(e) = adapter.connect(SERVER_PORT).await {
|
||||
println!("no connect: {e:?}");
|
||||
}
|
||||
|
||||
if let Err(e) = adapter.write_all(&[1, 2, 3, 4, 5]).await {
|
||||
println!("no send: {e:?}");
|
||||
} else {
|
||||
let mut buf = [0u8; 4];
|
||||
match adapter.read_exact(&mut buf).await {
|
||||
Ok(_) => println!("recv'd {buf:?}"),
|
||||
Err(e) => println!("no recv: {e:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = adapter.write_all(&[69, 69, 42, 0, 1]).await {
|
||||
println!("no send: {e:?}");
|
||||
} else {
|
||||
let mut buf = [0u8; 6];
|
||||
match adapter.read_exact(&mut buf).await {
|
||||
Ok(_) => println!("recv'd {buf:?}"),
|
||||
Err(e) => println!("no recv: {e:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = adapter.close().await {
|
||||
println!("no close: {e:?}");
|
||||
}
|
||||
|
||||
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
||||
println!("\n\npress enter");
|
||||
let mut buf = Vec::new();
|
||||
let _ = tokio::io::stdin().read(&mut buf).await.unwrap();
|
||||
}
|
||||
}
|
||||
716
idevice/src/tcp/packets.rs
Normal file
716
idevice/src/tcp/packets.rs
Normal file
@@ -0,0 +1,716 @@
|
||||
// Jackson Coxson
|
||||
// I couldn't find a lib that parses IP/TCP, so I guess we'll write our own
|
||||
|
||||
use std::{
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use tokio::{
|
||||
io::{AsyncRead, AsyncReadExt},
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
pub enum ProtocolNumber {
|
||||
Tcp = 6,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Ipv4Packet {
|
||||
pub version: u8, // always 4 for IPv4
|
||||
pub ihl: u8, // len of header / 4
|
||||
pub tos: u8, // nobody can agree what this is for
|
||||
pub total_length: u16, // length of packet in bytes
|
||||
pub identification: u16, // ID from sender to help assemble datagram
|
||||
pub flags: u8, // 3 bits; reserved: 0, may fragment: 0/1, last fragment 0 / more fragments 1
|
||||
pub fragment_offset: u16, // where in the datagram this belongs
|
||||
// If Google can ignore fragments, so can we
|
||||
pub ttl: u8, // max amount of time this packet can live
|
||||
pub protocol: u8, // protocol number, 6 for TCP
|
||||
pub header_checksum: u16, // wrapping add all the u16 in header, then invert all bits
|
||||
pub source: Ipv4Addr,
|
||||
pub destination: Ipv4Addr,
|
||||
pub options: Vec<u8>,
|
||||
pub payload: Vec<u8>, // if smoltcp can ignore options, so can we
|
||||
}
|
||||
|
||||
impl Ipv4Packet {
|
||||
pub fn parse(packet: &[u8]) -> Option<Self> {
|
||||
if packet.len() < 20 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let version_ihl = packet[0];
|
||||
let version = version_ihl >> 4;
|
||||
let ihl = (version_ihl & 0x0F) * 4; // send help I don't understand bitwise ops
|
||||
|
||||
if version != 4 || packet.len() < ihl as usize {
|
||||
return None;
|
||||
}
|
||||
|
||||
let tos = packet[1];
|
||||
let total_length = u16::from_be_bytes([packet[2], packet[3]]);
|
||||
let identification = u16::from_be_bytes([packet[4], packet[5]]);
|
||||
let flags_fragment = u16::from_be_bytes([packet[6], packet[7]]);
|
||||
let flags = (flags_fragment >> 13) as u8;
|
||||
let fragment_offset = flags_fragment & 0x1FFF;
|
||||
let ttl = packet[8];
|
||||
let protocol = packet[9];
|
||||
let header_checksum = u16::from_be_bytes([packet[10], packet[11]]);
|
||||
let source = Ipv4Addr::new(packet[12], packet[13], packet[14], packet[15]);
|
||||
let destination = Ipv4Addr::new(packet[16], packet[17], packet[18], packet[19]);
|
||||
|
||||
let options_end = ihl as usize;
|
||||
let options = if options_end > 20 {
|
||||
packet[20..options_end].to_vec()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let payload = if total_length as usize > options_end {
|
||||
packet[options_end..total_length as usize].to_vec()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
Some(Self {
|
||||
version,
|
||||
ihl,
|
||||
tos,
|
||||
total_length,
|
||||
identification,
|
||||
flags,
|
||||
fragment_offset,
|
||||
ttl,
|
||||
protocol,
|
||||
header_checksum,
|
||||
source,
|
||||
destination,
|
||||
options,
|
||||
payload,
|
||||
})
|
||||
}
|
||||
|
||||
/// Asynchronously read an IPv4 packet from a Tokio AsyncRead source.
|
||||
pub async fn from_reader<R: AsyncRead + Unpin + AsyncReadExt>(
|
||||
reader: &mut R,
|
||||
log: &Option<Arc<Mutex<tokio::fs::File>>>,
|
||||
) -> Result<Self, std::io::Error> {
|
||||
let mut log_packet = Vec::new();
|
||||
|
||||
let mut header = [0u8; 20]; // Minimum IPv4 header size
|
||||
reader.read_exact(&mut header).await?;
|
||||
if log.is_some() {
|
||||
log_packet.extend_from_slice(&header);
|
||||
}
|
||||
|
||||
let version_ihl = header[0];
|
||||
let version = version_ihl >> 4;
|
||||
let ihl = (version_ihl & 0x0F) * 4;
|
||||
|
||||
if version != 4 || ihl < 20 {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"Invalid IPv4 header",
|
||||
));
|
||||
}
|
||||
|
||||
let tos = header[1];
|
||||
let total_length = u16::from_be_bytes([header[2], header[3]]);
|
||||
let identification = u16::from_be_bytes([header[4], header[5]]);
|
||||
let flags_fragment = u16::from_be_bytes([header[6], header[7]]);
|
||||
let flags = (flags_fragment >> 13) as u8;
|
||||
let fragment_offset = flags_fragment & 0x1FFF;
|
||||
let ttl = header[8];
|
||||
let protocol = header[9];
|
||||
let header_checksum = u16::from_be_bytes([header[10], header[11]]);
|
||||
let source = Ipv4Addr::new(header[12], header[13], header[14], header[15]);
|
||||
let destination = Ipv4Addr::new(header[16], header[17], header[18], header[19]);
|
||||
|
||||
// Read options if the header is larger than 20 bytes
|
||||
let options_len = ihl as usize - 20;
|
||||
let mut options = vec![0u8; options_len];
|
||||
if options_len > 0 {
|
||||
reader.read_exact(&mut options).await?;
|
||||
if log.is_some() {
|
||||
log_packet.extend_from_slice(&options);
|
||||
}
|
||||
}
|
||||
|
||||
// Read the payload
|
||||
let payload_len = total_length as usize - ihl as usize;
|
||||
let mut payload = vec![0u8; payload_len];
|
||||
reader.read_exact(&mut payload).await?;
|
||||
if let Some(log) = log {
|
||||
log_packet.extend_from_slice(&payload);
|
||||
super::log_packet(log, &log_packet).await;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
version,
|
||||
ihl,
|
||||
tos,
|
||||
total_length,
|
||||
identification,
|
||||
flags,
|
||||
fragment_offset,
|
||||
ttl,
|
||||
protocol,
|
||||
header_checksum,
|
||||
source,
|
||||
destination,
|
||||
options,
|
||||
payload,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create(
|
||||
source: Ipv4Addr,
|
||||
destination: Ipv4Addr,
|
||||
protocol: ProtocolNumber,
|
||||
ttl: u8,
|
||||
payload: &[u8],
|
||||
) -> Vec<u8> {
|
||||
let ihl: u8 = 5;
|
||||
let total_length = (ihl as usize * 4 + payload.len()) as u16;
|
||||
let identification: u16 = 0;
|
||||
let flags_fragment: u16 = 0;
|
||||
let header_checksum: u16 = 0;
|
||||
|
||||
let mut packet = vec![0; total_length as usize];
|
||||
packet[0] = (4 << 4) | (ihl & 0x0F);
|
||||
packet[1] = 0;
|
||||
packet[2..4].copy_from_slice(&total_length.to_be_bytes());
|
||||
packet[4..6].copy_from_slice(&identification.to_be_bytes());
|
||||
packet[6..8].copy_from_slice(&flags_fragment.to_be_bytes());
|
||||
packet[8] = ttl;
|
||||
packet[9] = protocol as u8;
|
||||
packet[10..12].copy_from_slice(&header_checksum.to_be_bytes());
|
||||
packet[12..16].copy_from_slice(&source.octets());
|
||||
packet[16..20].copy_from_slice(&destination.octets());
|
||||
packet[20..].copy_from_slice(payload);
|
||||
|
||||
Self::apply_checksum(&mut packet);
|
||||
packet
|
||||
}
|
||||
|
||||
fn apply_checksum(packet: &mut [u8]) {
|
||||
packet[10] = 0;
|
||||
packet[11] = 0;
|
||||
let mut checksum: u16 = 0;
|
||||
for i in 0..packet.len() / 2 {
|
||||
let word = u16::from_be_bytes([packet[i * 2], packet[(i * 2) + 1]]);
|
||||
checksum = checksum.wrapping_add(word);
|
||||
}
|
||||
let checksum = checksum.to_be_bytes();
|
||||
packet[10] = checksum[0];
|
||||
packet[11] = checksum[1];
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Ipv6Packet {
|
||||
pub version: u8,
|
||||
pub traffic_class: u8,
|
||||
pub flow_label: u32,
|
||||
pub payload_length: u16,
|
||||
pub next_header: u8,
|
||||
pub hop_limit: u8,
|
||||
pub source: Ipv6Addr,
|
||||
pub destination: Ipv6Addr,
|
||||
pub payload: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Ipv6Packet {
|
||||
pub fn parse(packet: &[u8]) -> Option<Self> {
|
||||
if packet.len() < 40 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let version = packet[0] >> 4;
|
||||
if version != 6 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let traffic_class = ((packet[0] & 0x0F) << 4) | (packet[1] >> 4);
|
||||
let flow_label =
|
||||
((packet[1] as u32 & 0x0F) << 16) | ((packet[2] as u32) << 8) | packet[3] as u32;
|
||||
let payload_length = u16::from_be_bytes([packet[4], packet[5]]);
|
||||
let next_header = packet[6];
|
||||
let hop_limit = packet[7];
|
||||
let source = Ipv6Addr::new(
|
||||
u16::from_be_bytes([packet[8], packet[9]]),
|
||||
u16::from_be_bytes([packet[10], packet[11]]),
|
||||
u16::from_be_bytes([packet[12], packet[13]]),
|
||||
u16::from_be_bytes([packet[14], packet[15]]),
|
||||
u16::from_be_bytes([packet[16], packet[17]]),
|
||||
u16::from_be_bytes([packet[18], packet[19]]),
|
||||
u16::from_be_bytes([packet[20], packet[21]]),
|
||||
u16::from_be_bytes([packet[22], packet[23]]),
|
||||
);
|
||||
|
||||
let destination = Ipv6Addr::new(
|
||||
u16::from_be_bytes([packet[24], packet[25]]),
|
||||
u16::from_be_bytes([packet[26], packet[27]]),
|
||||
u16::from_be_bytes([packet[28], packet[29]]),
|
||||
u16::from_be_bytes([packet[30], packet[31]]),
|
||||
u16::from_be_bytes([packet[32], packet[33]]),
|
||||
u16::from_be_bytes([packet[34], packet[35]]),
|
||||
u16::from_be_bytes([packet[36], packet[37]]),
|
||||
u16::from_be_bytes([packet[38], packet[39]]),
|
||||
);
|
||||
let payload = packet[40..].to_vec();
|
||||
|
||||
Some(Self {
|
||||
version,
|
||||
traffic_class,
|
||||
flow_label,
|
||||
payload_length,
|
||||
next_header,
|
||||
hop_limit,
|
||||
source,
|
||||
destination,
|
||||
payload,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn from_reader<R: AsyncRead + Unpin>(
|
||||
reader: &mut R,
|
||||
log: &Option<Arc<Mutex<tokio::fs::File>>>,
|
||||
) -> Result<Self, std::io::Error> {
|
||||
let mut log_packet = Vec::new();
|
||||
let mut header = [0u8; 40]; // IPv6 header size is fixed at 40 bytes
|
||||
reader.read_exact(&mut header).await?;
|
||||
if log.is_some() {
|
||||
log_packet.extend_from_slice(&header);
|
||||
}
|
||||
|
||||
let version = header[0] >> 4;
|
||||
if version != 6 {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"Invalid IPv6 header",
|
||||
));
|
||||
}
|
||||
|
||||
let traffic_class = ((header[0] & 0x0F) << 4) | ((header[1] & 0xF0) >> 4);
|
||||
let flow_label =
|
||||
((header[1] as u32 & 0x0F) << 16) | ((header[2] as u32) << 8) | (header[3] as u32);
|
||||
let payload_length = u16::from_be_bytes([header[4], header[5]]);
|
||||
let next_header = header[6];
|
||||
let hop_limit = header[7];
|
||||
let source = Ipv6Addr::new(
|
||||
u16::from_be_bytes([header[8], header[9]]),
|
||||
u16::from_be_bytes([header[10], header[11]]),
|
||||
u16::from_be_bytes([header[12], header[13]]),
|
||||
u16::from_be_bytes([header[14], header[15]]),
|
||||
u16::from_be_bytes([header[16], header[17]]),
|
||||
u16::from_be_bytes([header[18], header[19]]),
|
||||
u16::from_be_bytes([header[20], header[21]]),
|
||||
u16::from_be_bytes([header[22], header[23]]),
|
||||
);
|
||||
let destination = Ipv6Addr::new(
|
||||
u16::from_be_bytes([header[24], header[25]]),
|
||||
u16::from_be_bytes([header[26], header[27]]),
|
||||
u16::from_be_bytes([header[28], header[29]]),
|
||||
u16::from_be_bytes([header[30], header[31]]),
|
||||
u16::from_be_bytes([header[32], header[33]]),
|
||||
u16::from_be_bytes([header[34], header[35]]),
|
||||
u16::from_be_bytes([header[36], header[37]]),
|
||||
u16::from_be_bytes([header[38], header[39]]),
|
||||
);
|
||||
|
||||
// Read the payload
|
||||
let mut payload = vec![0u8; payload_length as usize];
|
||||
reader.read_exact(&mut payload).await?;
|
||||
if let Some(log) = log {
|
||||
log_packet.extend_from_slice(&payload);
|
||||
super::log_packet(log, &log_packet).await;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
version,
|
||||
traffic_class,
|
||||
flow_label,
|
||||
payload_length,
|
||||
next_header,
|
||||
hop_limit,
|
||||
source,
|
||||
destination,
|
||||
payload,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create(
|
||||
source: Ipv6Addr,
|
||||
destination: Ipv6Addr,
|
||||
next_header: ProtocolNumber,
|
||||
hop_limit: u8,
|
||||
payload: &[u8],
|
||||
) -> Vec<u8> {
|
||||
let mut packet = Vec::with_capacity(40 + payload.len());
|
||||
|
||||
// Version (6) and Traffic Class (0)
|
||||
let version_traffic_class = 6 << 4;
|
||||
packet.push(version_traffic_class);
|
||||
packet.push(0); // The rest of the Traffic Class and the start of the Flow Label
|
||||
|
||||
// Flow Label (0)
|
||||
let flow_label = 0u16;
|
||||
packet.extend_from_slice(&flow_label.to_be_bytes()[..]);
|
||||
|
||||
// Payload Length (length of the payload only)
|
||||
packet.extend_from_slice(&(payload.len() as u16).to_be_bytes());
|
||||
|
||||
// Next Header and Hop Limit
|
||||
packet.push(next_header as u8);
|
||||
packet.push(hop_limit);
|
||||
|
||||
// Source and Destination Addresses
|
||||
packet.extend_from_slice(&source.octets());
|
||||
packet.extend_from_slice(&destination.octets());
|
||||
|
||||
// Payload
|
||||
packet.extend_from_slice(payload);
|
||||
|
||||
packet
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Ipv6Packet {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Ipv6Packet")
|
||||
.field("version", &self.version)
|
||||
.field("traffic_class", &self.traffic_class)
|
||||
.field("flow_label", &self.flow_label)
|
||||
.field("payload_length", &self.payload_length)
|
||||
.field("next_header", &self.next_header)
|
||||
.field("hop_limit", &self.hop_limit)
|
||||
.field("source", &self.source)
|
||||
.field("destination", &self.destination)
|
||||
.field("payload len", &self.payload.len())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub struct TcpFlags {
|
||||
pub urg: bool, // Urgent pointer flag
|
||||
pub ack: bool, // Acknowledgment flag
|
||||
pub psh: bool, // Push flag
|
||||
pub rst: bool, // Reset flag
|
||||
pub syn: bool, // Synchronize flag
|
||||
pub fin: bool, // Finish flag
|
||||
}
|
||||
|
||||
impl TcpFlags {
|
||||
/// Create a new `TcpFlags` struct from a raw byte.
|
||||
pub fn from_byte(flags: u8) -> Self {
|
||||
Self {
|
||||
urg: (flags & 0b0010_0000) != 0, // URG flag (bit 5)
|
||||
ack: (flags & 0b0001_0000) != 0, // ACK flag (bit 4)
|
||||
psh: (flags & 0b0000_1000) != 0, // PSH flag (bit 3)
|
||||
rst: (flags & 0b0000_0100) != 0, // RST flag (bit 2)
|
||||
syn: (flags & 0b0000_0010) != 0, // SYN flag (bit 1)
|
||||
fin: (flags & 0b0000_0001) != 0, // FIN flag (bit 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the `TcpFlags` struct into a raw byte.
|
||||
pub fn to_byte(&self) -> u8 {
|
||||
let mut flags = 0u8;
|
||||
if self.urg {
|
||||
flags |= 0b0010_0000;
|
||||
}
|
||||
if self.ack {
|
||||
flags |= 0b0001_0000;
|
||||
}
|
||||
if self.psh {
|
||||
flags |= 0b0000_1000;
|
||||
}
|
||||
if self.rst {
|
||||
flags |= 0b0000_0100;
|
||||
}
|
||||
if self.syn {
|
||||
flags |= 0b0000_0010;
|
||||
}
|
||||
if self.fin {
|
||||
flags |= 0b0000_0001;
|
||||
}
|
||||
flags
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TcpPacket {
|
||||
pub source_port: u16,
|
||||
pub destination_port: u16,
|
||||
pub sequence_number: u32,
|
||||
pub acknowledgment_number: u32,
|
||||
pub data_offset: u8, // Header length in 32-bit words
|
||||
pub flags: TcpFlags, // TCP flags
|
||||
pub window_size: u16,
|
||||
pub checksum: u16,
|
||||
pub urgent_pointer: u16,
|
||||
pub options: Vec<u8>, // Optional TCP options
|
||||
pub payload: Vec<u8>, // TCP payload
|
||||
}
|
||||
|
||||
impl TcpPacket {
|
||||
pub fn parse(packet: &[u8]) -> Result<Self, std::io::Error> {
|
||||
if packet.len() < 20 {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"Not enough bytes for TCP header",
|
||||
));
|
||||
}
|
||||
|
||||
let source_port = u16::from_be_bytes([packet[0], packet[1]]);
|
||||
let destination_port = u16::from_be_bytes([packet[2], packet[3]]);
|
||||
let sequence_number = u32::from_be_bytes([packet[4], packet[5], packet[6], packet[7]]);
|
||||
let acknowledgment_number =
|
||||
u32::from_be_bytes([packet[8], packet[9], packet[10], packet[11]]);
|
||||
let data_offset = (packet[12] >> 4) * 4; // Convert from 32-bit words to bytes
|
||||
let flags = TcpFlags::from_byte(packet[13]); // Parse flags
|
||||
let window_size = u16::from_be_bytes([packet[14], packet[15]]);
|
||||
let checksum = u16::from_be_bytes([packet[16], packet[17]]);
|
||||
let urgent_pointer = u16::from_be_bytes([packet[18], packet[19]]);
|
||||
|
||||
// Parse options if the header is longer than 20 bytes
|
||||
let options_end = data_offset as usize;
|
||||
let options = if options_end > 20 {
|
||||
// packet[20..options_end].to_vec()
|
||||
Vec::new()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
// Payload starts after the header
|
||||
let payload = if packet.len() > options_end {
|
||||
packet[options_end..].to_vec()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
source_port,
|
||||
destination_port,
|
||||
sequence_number,
|
||||
acknowledgment_number,
|
||||
data_offset,
|
||||
flags,
|
||||
window_size,
|
||||
checksum,
|
||||
urgent_pointer,
|
||||
options,
|
||||
payload,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn create(
|
||||
source_ip: IpAddr,
|
||||
destination_ip: IpAddr,
|
||||
source_port: u16,
|
||||
destination_port: u16,
|
||||
sequence_number: u32,
|
||||
acknowledgment_number: u32,
|
||||
flags: TcpFlags,
|
||||
window_size: u16,
|
||||
payload: &[u8],
|
||||
) -> Vec<u8> {
|
||||
let data_offset = 5_u8; // Header length in 32-bit words
|
||||
let mut packet = Vec::with_capacity(20 + payload.len());
|
||||
|
||||
// Source and Destination Ports
|
||||
packet.extend_from_slice(&source_port.to_be_bytes());
|
||||
packet.extend_from_slice(&destination_port.to_be_bytes());
|
||||
|
||||
// Sequence and Acknowledgment Numbers
|
||||
packet.extend_from_slice(&sequence_number.to_be_bytes());
|
||||
packet.extend_from_slice(&acknowledgment_number.to_be_bytes());
|
||||
|
||||
// Data Offset and Flags
|
||||
packet.push(data_offset << 4); // Data offset (4 bits) and reserved bits (4 bits)
|
||||
packet.push(flags.to_byte()); // Flags byte
|
||||
|
||||
// Window Size, Checksum (set to zero first), and Urgent Pointer
|
||||
packet.extend_from_slice(&window_size.to_be_bytes());
|
||||
packet.extend_from_slice(&[0, 0]); // Checksum placeholder
|
||||
packet.extend_from_slice(&[0, 0]); // Urgent pointer
|
||||
|
||||
// No options, keeping it simple
|
||||
packet.extend_from_slice(payload);
|
||||
|
||||
// Compute checksum with the appropriate pseudo-header
|
||||
let checksum = match (source_ip, destination_ip) {
|
||||
(IpAddr::V4(src), IpAddr::V4(dest)) => {
|
||||
let src_bytes = src.octets();
|
||||
let dest_bytes = dest.octets();
|
||||
Self::calculate_checksum(&packet, &src_bytes, &dest_bytes, false)
|
||||
}
|
||||
(IpAddr::V6(src), IpAddr::V6(dest)) => {
|
||||
let src_bytes = src.octets();
|
||||
let dest_bytes = dest.octets();
|
||||
Self::calculate_checksum(&packet, &src_bytes, &dest_bytes, true)
|
||||
}
|
||||
_ => panic!("Source and destination IP versions must match"),
|
||||
};
|
||||
|
||||
packet[16..18].copy_from_slice(&checksum.to_be_bytes());
|
||||
|
||||
packet
|
||||
}
|
||||
|
||||
fn calculate_checksum(
|
||||
packet: &[u8],
|
||||
source_ip: &[u8],
|
||||
destination_ip: &[u8],
|
||||
is_ipv6: bool,
|
||||
) -> u16 {
|
||||
let mut sum = 0u32;
|
||||
|
||||
if is_ipv6 {
|
||||
// IPv6 pseudo-header
|
||||
// Add source and destination addresses (128 bits each)
|
||||
for chunk in source_ip.chunks(2) {
|
||||
sum += u16::from_be_bytes([chunk[0], chunk[1]]) as u32;
|
||||
}
|
||||
for chunk in destination_ip.chunks(2) {
|
||||
sum += u16::from_be_bytes([chunk[0], chunk[1]]) as u32;
|
||||
}
|
||||
|
||||
// Upper layer packet length (32 bits for IPv6)
|
||||
let tcp_length = packet.len() as u32;
|
||||
sum += (tcp_length >> 16) & 0xFFFF;
|
||||
sum += tcp_length & 0xFFFF;
|
||||
|
||||
// Next Header value (8 bits of zeros + 8 bits of protocol value)
|
||||
sum += 6u32; // TCP protocol number
|
||||
} else {
|
||||
// IPv4 pseudo-header
|
||||
// Add source and destination addresses (32 bits each)
|
||||
for chunk in source_ip.chunks(2) {
|
||||
sum += u16::from_be_bytes([chunk[0], chunk[1]]) as u32;
|
||||
}
|
||||
for chunk in destination_ip.chunks(2) {
|
||||
sum += u16::from_be_bytes([chunk[0], chunk[1]]) as u32;
|
||||
}
|
||||
|
||||
// Zero byte + Protocol byte
|
||||
sum += 6u32; // TCP protocol number
|
||||
|
||||
// TCP segment length (16 bits)
|
||||
sum += packet.len() as u32;
|
||||
}
|
||||
|
||||
// Create a copy of the packet with checksum field zeroed out
|
||||
let mut packet_copy = packet.to_vec();
|
||||
if packet_copy.len() >= 18 {
|
||||
packet_copy[16] = 0;
|
||||
packet_copy[17] = 0;
|
||||
}
|
||||
|
||||
// Sum all 16-bit words in the packet
|
||||
for chunk in packet_copy.chunks(2) {
|
||||
let word = if chunk.len() == 2 {
|
||||
u16::from_be_bytes([chunk[0], chunk[1]])
|
||||
} else {
|
||||
u16::from_be_bytes([chunk[0], 0]) // Padding for odd-length packets
|
||||
};
|
||||
sum += word as u32;
|
||||
}
|
||||
|
||||
// Fold sum to 16 bits
|
||||
while sum >> 16 != 0 {
|
||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
}
|
||||
|
||||
// One's complement
|
||||
!(sum as u16)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for TcpPacket {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("TcpPacket")
|
||||
.field("source_port", &self.source_port)
|
||||
.field("destination_port", &self.destination_port)
|
||||
.field("sequence_number", &self.sequence_number)
|
||||
.field("acknowledgment_number", &self.acknowledgment_number)
|
||||
.field("data_offset", &self.data_offset)
|
||||
.field("flags", &self.flags)
|
||||
.field("window_size", &self.window_size)
|
||||
.field("checksum", &self.checksum)
|
||||
.field("urgent_pointer", &self.urgent_pointer)
|
||||
.field("options", &self.options)
|
||||
.field("payload len", &self.payload.len())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn ipv4() {
|
||||
let b1 = Ipv4Packet::create(
|
||||
Ipv4Addr::new(127, 0, 0, 1),
|
||||
Ipv4Addr::new(1, 1, 1, 1),
|
||||
ProtocolNumber::Tcp,
|
||||
255,
|
||||
&[1, 2, 3, 4, 5],
|
||||
);
|
||||
println!("{b1:02X?}");
|
||||
|
||||
let ip1 = Ipv4Packet::parse(&b1);
|
||||
println!("{ip1:#?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ipv6() {
|
||||
let b1 = Ipv6Packet::create(
|
||||
Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8),
|
||||
Ipv6Addr::new(9, 10, 11, 12, 13, 14, 15, 16),
|
||||
ProtocolNumber::Tcp,
|
||||
255,
|
||||
&[1, 2, 3, 4, 5],
|
||||
);
|
||||
println!("{b1:02X?}");
|
||||
|
||||
let ip1 = Ipv6Packet::parse(&b1);
|
||||
println!("{ip1:#?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tcp() {
|
||||
let b1 = TcpPacket::create(
|
||||
IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)),
|
||||
IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)),
|
||||
1234,
|
||||
5678,
|
||||
420,
|
||||
6969,
|
||||
TcpFlags {
|
||||
urg: false,
|
||||
ack: false,
|
||||
psh: true,
|
||||
rst: false,
|
||||
syn: false,
|
||||
fin: false,
|
||||
},
|
||||
5555,
|
||||
&[1, 2, 3, 4, 5],
|
||||
);
|
||||
let i1 = Ipv6Packet::create(
|
||||
Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8),
|
||||
Ipv6Addr::new(9, 10, 11, 12, 13, 14, 15, 16),
|
||||
ProtocolNumber::Tcp,
|
||||
255,
|
||||
&b1,
|
||||
);
|
||||
println!("{i1:02X?}");
|
||||
|
||||
let t1 = TcpPacket::parse(&b1);
|
||||
println!("{t1:#?}");
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ use crate::{
|
||||
self,
|
||||
h2::{SettingsFrame, WindowUpdateFrame},
|
||||
},
|
||||
IdeviceError,
|
||||
IdeviceError, ReadWrite,
|
||||
};
|
||||
use error::XPCError;
|
||||
use format::{XPCFlag, XPCMessage, XPCObject};
|
||||
@@ -18,8 +18,8 @@ pub mod cdtunnel;
|
||||
pub mod error;
|
||||
pub mod format;
|
||||
|
||||
pub struct XPCDevice {
|
||||
pub connection: XPCConnection,
|
||||
pub struct XPCDevice<R: ReadWrite> {
|
||||
pub connection: XPCConnection<R>,
|
||||
pub services: HashMap<String, XPCService>,
|
||||
}
|
||||
|
||||
@@ -32,19 +32,17 @@ pub struct XPCService {
|
||||
pub service_version: Option<i64>,
|
||||
}
|
||||
|
||||
pub struct XPCConnection {
|
||||
inner: http2::Connection,
|
||||
pub struct XPCConnection<R: ReadWrite> {
|
||||
pub(crate) inner: http2::Connection<R>,
|
||||
root_message_id: u64,
|
||||
reply_message_id: u64,
|
||||
}
|
||||
|
||||
impl XPCDevice {
|
||||
pub async fn new(stream: crate::IdeviceSocket) -> Result<Self, IdeviceError> {
|
||||
impl<R: ReadWrite> XPCDevice<R> {
|
||||
pub async fn new(stream: R) -> Result<Self, IdeviceError> {
|
||||
let mut connection = XPCConnection::new(stream).await?;
|
||||
|
||||
let data = connection
|
||||
.read_message(http2::Connection::ROOT_CHANNEL)
|
||||
.await?;
|
||||
let data = connection.read_message(http2::ROOT_CHANNEL).await?;
|
||||
|
||||
let data = match data.message {
|
||||
Some(d) => match d
|
||||
@@ -132,14 +130,18 @@ impl XPCDevice {
|
||||
services,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> R {
|
||||
self.connection.inner.stream
|
||||
}
|
||||
}
|
||||
|
||||
impl XPCConnection {
|
||||
pub const ROOT_CHANNEL: u32 = http2::Connection::ROOT_CHANNEL;
|
||||
pub const REPLY_CHANNEL: u32 = http2::Connection::REPLY_CHANNEL;
|
||||
const INIT_STREAM: u32 = http2::Connection::INIT_STREAM;
|
||||
impl<R: ReadWrite> XPCConnection<R> {
|
||||
pub const ROOT_CHANNEL: u32 = http2::ROOT_CHANNEL;
|
||||
pub const REPLY_CHANNEL: u32 = http2::REPLY_CHANNEL;
|
||||
const INIT_STREAM: u32 = http2::INIT_STREAM;
|
||||
|
||||
pub async fn new(stream: crate::IdeviceSocket) -> Result<Self, XPCError> {
|
||||
pub async fn new(stream: R) -> Result<Self, XPCError> {
|
||||
let mut client = http2::Connection::new(stream).await?;
|
||||
client
|
||||
.send_frame(SettingsFrame::new(
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
use clap::{Arg, Command};
|
||||
use idevice::{
|
||||
core_device_proxy::{self},
|
||||
xpc::XPCDevice,
|
||||
IdeviceService,
|
||||
};
|
||||
use tun_rs::AbstractDevice;
|
||||
@@ -63,57 +62,51 @@ async fn main() {
|
||||
let mut tun_proxy = core_device_proxy::CoreDeviceProxy::connect(&*provider)
|
||||
.await
|
||||
.expect("Unable to connect");
|
||||
let rsd_port = tun_proxy.handshake.server_rsd_port;
|
||||
|
||||
let mut tunnel = tun_proxy.create_software_tunnel().unwrap();
|
||||
let channel = tunnel.connect_tcp(rsd_port).unwrap();
|
||||
let client = XPCDevice::new(Box::new(channel)).await.expect("no xpc??");
|
||||
todo!();
|
||||
let dev = tun_rs::create(&tun_rs::Configuration::default()).unwrap();
|
||||
dev.add_address_v6(
|
||||
tun_proxy
|
||||
.handshake
|
||||
.client_parameters
|
||||
.address
|
||||
.parse()
|
||||
.unwrap(),
|
||||
32,
|
||||
)
|
||||
.unwrap();
|
||||
dev.set_mtu(tun_proxy.handshake.client_parameters.mtu)
|
||||
.unwrap();
|
||||
dev.set_network_address(
|
||||
tun_proxy.handshake.client_parameters.address.clone(),
|
||||
tun_proxy
|
||||
.handshake
|
||||
.client_parameters
|
||||
.netmask
|
||||
.parse()
|
||||
.unwrap(),
|
||||
Some(tun_proxy.handshake.server_address.parse().unwrap()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// let dev = tun_rs::create(&tun_rs::Configuration::default()).unwrap();
|
||||
// dev.add_address_v6(
|
||||
// tun_proxy
|
||||
// .handshake
|
||||
// .client_parameters
|
||||
// .address
|
||||
// .parse()
|
||||
// .unwrap(),
|
||||
// 32,
|
||||
// )
|
||||
// .unwrap();
|
||||
// dev.set_mtu(tun_proxy.handshake.client_parameters.mtu)
|
||||
// .unwrap();
|
||||
// dev.set_network_address(
|
||||
// tun_proxy.handshake.client_parameters.address.clone(),
|
||||
// tun_proxy
|
||||
// .handshake
|
||||
// .client_parameters
|
||||
// .netmask
|
||||
// .parse()
|
||||
// .unwrap(),
|
||||
// Some(tun_proxy.handshake.server_address.parse().unwrap()),
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// let async_dev = tun_rs::AsyncDevice::new(dev).unwrap();
|
||||
// async_dev.enabled(true).unwrap();
|
||||
// println!("-----------------------------");
|
||||
// println!("tun device created: {:?}", async_dev.name());
|
||||
// println!("server address: {}", tun_proxy.handshake.server_address);
|
||||
// println!("rsd port: {}", tun_proxy.handshake.server_rsd_port);
|
||||
// println!("-----------------------------");
|
||||
//
|
||||
// let mut buf = vec![0; 1500];
|
||||
// loop {
|
||||
// tokio::select! {
|
||||
// Ok(len) = async_dev.recv(&mut buf) => {
|
||||
// println!("tun pkt: {:?}", &buf[..len]);
|
||||
// tun_proxy.send(&buf[..len]).await.unwrap();
|
||||
// }
|
||||
// Ok(res) = tun_proxy.recv() => {
|
||||
// println!("dev pkt: {:?}", &res);
|
||||
// async_dev.send(&res).await.unwrap();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
let async_dev = tun_rs::AsyncDevice::new(dev).unwrap();
|
||||
async_dev.enabled(true).unwrap();
|
||||
println!("-----------------------------");
|
||||
println!("tun device created: {:?}", async_dev.name());
|
||||
println!("server address: {}", tun_proxy.handshake.server_address);
|
||||
println!("rsd port: {}", tun_proxy.handshake.server_rsd_port);
|
||||
println!("-----------------------------");
|
||||
|
||||
let mut buf = vec![0; 1500];
|
||||
loop {
|
||||
tokio::select! {
|
||||
Ok(len) = async_dev.recv(&mut buf) => {
|
||||
println!("tun pkt: {:?}", &buf[..len]);
|
||||
tun_proxy.send(&buf[..len]).await.unwrap();
|
||||
}
|
||||
Ok(res) = tun_proxy.recv() => {
|
||||
println!("dev pkt: {:?}", &res);
|
||||
async_dev.send(&res).await.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use std::{
|
||||
io::Write,
|
||||
net::{IpAddr, SocketAddr},
|
||||
str::FromStr,
|
||||
};
|
||||
use std::io::Write;
|
||||
|
||||
use clap::{Arg, Command};
|
||||
use idevice::{debug_proxy::DebugProxyClient, tunneld::get_tunneld_devices, xpc::XPCDevice};
|
||||
use tokio::net::TcpStream;
|
||||
use idevice::{
|
||||
core_device_proxy::CoreDeviceProxy, debug_proxy::DebugProxyClient, xpc::XPCDevice,
|
||||
IdeviceService,
|
||||
};
|
||||
|
||||
mod common;
|
||||
|
||||
@@ -18,6 +16,18 @@ async fn main() {
|
||||
|
||||
let matches = Command::new("remotexpc")
|
||||
.about("Get services from RemoteXPC")
|
||||
.arg(
|
||||
Arg::new("host")
|
||||
.long("host")
|
||||
.value_name("HOST")
|
||||
.help("IP address of the device"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("pairing_file")
|
||||
.long("pairing-file")
|
||||
.value_name("PATH")
|
||||
.help("Path to the pairing file"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("udid")
|
||||
.value_name("UDID")
|
||||
@@ -39,46 +49,41 @@ async fn main() {
|
||||
}
|
||||
|
||||
let udid = matches.get_one::<String>("udid");
|
||||
let pairing_file = matches.get_one::<String>("pairing_file");
|
||||
let host = matches.get_one::<String>("host");
|
||||
|
||||
let socket = SocketAddr::new(
|
||||
IpAddr::from_str("127.0.0.1").unwrap(),
|
||||
idevice::tunneld::DEFAULT_PORT,
|
||||
);
|
||||
let mut devices = get_tunneld_devices(socket)
|
||||
.await
|
||||
.expect("Failed to get tunneld devices");
|
||||
|
||||
let (_udid, device) = match udid {
|
||||
Some(u) => (
|
||||
u.to_owned(),
|
||||
devices.remove(u).expect("Device not in tunneld"),
|
||||
),
|
||||
None => devices.into_iter().next().expect("No devices"),
|
||||
let provider =
|
||||
match common::get_provider(udid, host, pairing_file, "debug-proxy-jkcoxson").await {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
eprintln!("{e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let proxy = CoreDeviceProxy::connect(&*provider)
|
||||
.await
|
||||
.expect("no core proxy");
|
||||
let rsd_port = proxy.handshake.server_rsd_port;
|
||||
|
||||
let mut adapter = proxy.create_software_tunnel().expect("no software tunnel");
|
||||
adapter.connect(rsd_port).await.expect("no RSD connect");
|
||||
|
||||
// Make the connection to RemoteXPC
|
||||
let client = XPCDevice::new(Box::new(
|
||||
TcpStream::connect((device.tunnel_address.as_str(), device.tunnel_port))
|
||||
.await
|
||||
.unwrap(),
|
||||
))
|
||||
.await
|
||||
.unwrap();
|
||||
let client = XPCDevice::new(Box::new(adapter)).await.unwrap();
|
||||
|
||||
// Get the debug proxy
|
||||
let service = client
|
||||
.services
|
||||
.get(idevice::debug_proxy::SERVICE_NAME)
|
||||
.expect("Client did not contain debug proxy service");
|
||||
.expect("Client did not contain debug proxy service")
|
||||
.to_owned();
|
||||
|
||||
let stream = TcpStream::connect(SocketAddr::new(
|
||||
IpAddr::from_str(&device.tunnel_address).unwrap(),
|
||||
service.port,
|
||||
))
|
||||
.await
|
||||
.expect("Failed to connect");
|
||||
let mut adapter = client.into_inner();
|
||||
adapter.close().await.unwrap();
|
||||
adapter.connect(service.port).await.unwrap();
|
||||
|
||||
let mut dp = DebugProxyClient::new(Box::new(stream));
|
||||
let mut dp = DebugProxyClient::new(Box::new(adapter));
|
||||
|
||||
println!("Shell connected!");
|
||||
loop {
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{arg, value_parser, Arg, Command};
|
||||
use idevice::{
|
||||
lockdownd::LockdowndClient, misagent::MisagentClient, mounter::ImageMounter,
|
||||
pretty_print_plist, IdeviceService,
|
||||
};
|
||||
use clap::{Arg, Command};
|
||||
use idevice::{misagent::MisagentClient, pretty_print_plist, IdeviceService};
|
||||
|
||||
mod common;
|
||||
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use std::{
|
||||
net::{IpAddr, SocketAddr},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use clap::{Arg, Command};
|
||||
use idevice::{tunneld::get_tunneld_devices, xpc::XPCDevice};
|
||||
use tokio::net::TcpStream;
|
||||
use idevice::{core_device_proxy::CoreDeviceProxy, xpc::XPCDevice, IdeviceService};
|
||||
|
||||
mod common;
|
||||
|
||||
@@ -17,6 +11,18 @@ async fn main() {
|
||||
|
||||
let matches = Command::new("process_control")
|
||||
.about("Query process control")
|
||||
.arg(
|
||||
Arg::new("host")
|
||||
.long("host")
|
||||
.value_name("HOST")
|
||||
.help("IP address of the device"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("pairing_file")
|
||||
.long("pairing-file")
|
||||
.value_name("PATH")
|
||||
.help("Path to the pairing file"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("udid")
|
||||
.value_name("UDID")
|
||||
@@ -44,50 +50,44 @@ async fn main() {
|
||||
}
|
||||
|
||||
let udid = matches.get_one::<String>("udid");
|
||||
let pairing_file = matches.get_one::<String>("pairing_file");
|
||||
let host = matches.get_one::<String>("host");
|
||||
|
||||
let provider =
|
||||
match common::get_provider(udid, host, pairing_file, "heartbeat_client-jkcoxson").await {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
eprintln!("{e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let bundle_id = matches
|
||||
.get_one::<String>("bundle_id")
|
||||
.expect("No bundle ID specified");
|
||||
|
||||
let socket = SocketAddr::new(
|
||||
IpAddr::from_str("127.0.0.1").unwrap(),
|
||||
idevice::tunneld::DEFAULT_PORT,
|
||||
);
|
||||
let mut devices = get_tunneld_devices(socket)
|
||||
let proxy = CoreDeviceProxy::connect(&*provider)
|
||||
.await
|
||||
.expect("Failed to get tunneld devices");
|
||||
.expect("no core proxy");
|
||||
let rsd_port = proxy.handshake.server_rsd_port;
|
||||
|
||||
let (_udid, device) = match udid {
|
||||
Some(u) => (
|
||||
u.to_owned(),
|
||||
devices.remove(u).expect("Device not in tunneld"),
|
||||
),
|
||||
None => devices.into_iter().next().expect("No devices"),
|
||||
};
|
||||
let mut adapter = proxy.create_software_tunnel().expect("no software tunnel");
|
||||
adapter.connect(rsd_port).await.expect("no RSD connect");
|
||||
|
||||
// Make the connection to RemoteXPC
|
||||
let client = XPCDevice::new(Box::new(
|
||||
TcpStream::connect((device.tunnel_address.as_str(), device.tunnel_port))
|
||||
.await
|
||||
.unwrap(),
|
||||
))
|
||||
.await
|
||||
.unwrap();
|
||||
let client = XPCDevice::new(Box::new(adapter)).await.unwrap();
|
||||
|
||||
// Get the debug proxy
|
||||
let service = client
|
||||
.services
|
||||
.get(idevice::dvt::SERVICE_NAME)
|
||||
.expect("Client did not contain DVT service");
|
||||
.expect("Client did not contain DVT service")
|
||||
.to_owned();
|
||||
|
||||
let stream = TcpStream::connect(SocketAddr::new(
|
||||
IpAddr::from_str(&device.tunnel_address).unwrap(),
|
||||
service.port,
|
||||
))
|
||||
.await
|
||||
.expect("Failed to connect");
|
||||
let mut adapter = client.into_inner();
|
||||
adapter.connect(service.port).await.unwrap();
|
||||
|
||||
let mut rs_client =
|
||||
idevice::dvt::remote_server::RemoteServerClient::new(Box::new(stream)).unwrap();
|
||||
idevice::dvt::remote_server::RemoteServerClient::new(Box::new(adapter)).unwrap();
|
||||
rs_client.read_message(0).await.expect("no read??");
|
||||
let mut pc_client = idevice::dvt::process_control::ProcessControlClient::new(&mut rs_client)
|
||||
.await
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
// Jackson Coxson
|
||||
// Print out all the RemoteXPC services
|
||||
|
||||
use std::{
|
||||
net::{IpAddr, SocketAddr},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use clap::{Arg, Command};
|
||||
use idevice::{tunneld::get_tunneld_devices, xpc::XPCDevice};
|
||||
use tokio::net::TcpStream;
|
||||
use idevice::{core_device_proxy::CoreDeviceProxy, xpc::XPCDevice, IdeviceService};
|
||||
|
||||
mod common;
|
||||
|
||||
@@ -18,6 +12,18 @@ async fn main() {
|
||||
|
||||
let matches = Command::new("remotexpc")
|
||||
.about("Get services from RemoteXPC")
|
||||
.arg(
|
||||
Arg::new("host")
|
||||
.long("host")
|
||||
.value_name("HOST")
|
||||
.help("IP address of the device"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("pairing_file")
|
||||
.long("pairing-file")
|
||||
.value_name("PATH")
|
||||
.help("Path to the pairing file"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("udid")
|
||||
.value_name("UDID")
|
||||
@@ -39,31 +45,28 @@ async fn main() {
|
||||
}
|
||||
|
||||
let udid = matches.get_one::<String>("udid");
|
||||
let pairing_file = matches.get_one::<String>("pairing_file");
|
||||
let host = matches.get_one::<String>("host");
|
||||
|
||||
let socket = SocketAddr::new(
|
||||
IpAddr::from_str("127.0.0.1").unwrap(),
|
||||
idevice::tunneld::DEFAULT_PORT,
|
||||
);
|
||||
let mut devices = get_tunneld_devices(socket)
|
||||
.await
|
||||
.expect("Failed to get tunneld devices");
|
||||
|
||||
let (_udid, device) = match udid {
|
||||
Some(u) => (
|
||||
u.to_owned(),
|
||||
devices.remove(u).expect("Device not in tunneld"),
|
||||
),
|
||||
None => devices.into_iter().next().expect("No devices"),
|
||||
let provider = match common::get_provider(udid, host, pairing_file, "remotexpc-jkcoxson").await
|
||||
{
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
eprintln!("{e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let proxy = CoreDeviceProxy::connect(&*provider)
|
||||
.await
|
||||
.expect("no core proxy");
|
||||
let rsd_port = proxy.handshake.server_rsd_port;
|
||||
|
||||
let mut adapter = proxy.create_software_tunnel().expect("no software tunnel");
|
||||
adapter.connect(rsd_port).await.expect("no RSD connect");
|
||||
|
||||
// Make the connection to RemoteXPC
|
||||
let client = XPCDevice::new(Box::new(
|
||||
TcpStream::connect((device.tunnel_address.as_str(), device.tunnel_port))
|
||||
.await
|
||||
.unwrap(),
|
||||
))
|
||||
.await
|
||||
.unwrap();
|
||||
let client = XPCDevice::new(Box::new(adapter)).await.unwrap();
|
||||
|
||||
println!("{:#?}", client.services);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user