Implement software TCP stack

This commit is contained in:
Jackson Coxson
2025-03-21 23:29:16 -06:00
parent c3395b032a
commit e1bfcd39ff
20 changed files with 1768 additions and 818 deletions

1
.gitignore vendored
View File

@@ -3,4 +3,5 @@ BuildManifest.plist
Image.dmg Image.dmg
Image.dmg.trustcache Image.dmg.trustcache
.DS_Store .DS_Store
*.pcap

365
Cargo.lock generated
View File

@@ -138,12 +138,6 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.8.0" version = "2.8.0"
@@ -184,20 +178,11 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 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]] [[package]]
name = "bytes" name = "bytes"
version = "1.9.0" version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]] [[package]]
name = "c2rust-bitfields" name = "c2rust-bitfields"
@@ -205,7 +190,16 @@ version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "367e5d1b30f28be590b6b3868da1578361d29d9bfac516d22f497d28ed7c9055" checksum = "367e5d1b30f28be590b6b3868da1578361d29d9bfac516d22f497d28ed7c9055"
dependencies = [ 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]] [[package]]
@@ -219,6 +213,17 @@ dependencies = [
"syn 1.0.109", "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]] [[package]]
name = "cc" name = "cc"
version = "1.2.10" version = "1.2.10"
@@ -380,38 +385,6 @@ dependencies = [
"typenum", "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]] [[package]]
name = "deranged" name = "deranged"
version = "0.3.11" version = "0.3.11"
@@ -421,17 +394,6 @@ dependencies = [
"powerfmt", "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]] [[package]]
name = "digest" name = "digest"
version = "0.10.7" version = "0.10.7"
@@ -577,6 +539,21 @@ dependencies = [
"percent-encoding", "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]] [[package]]
name = "futures-channel" name = "futures-channel"
version = "0.3.31" version = "0.3.31"
@@ -584,6 +561,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-sink",
] ]
[[package]] [[package]]
@@ -592,6 +570,17 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 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]] [[package]]
name = "futures-io" name = "futures-io"
version = "0.3.31" version = "0.3.31"
@@ -608,6 +597,17 @@ dependencies = [
"pin-project-lite", "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]] [[package]]
name = "futures-sink" name = "futures-sink"
version = "0.3.31" version = "0.3.31"
@@ -626,10 +626,16 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [ dependencies = [
"futures-channel",
"futures-core", "futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task", "futures-task",
"memchr",
"pin-project-lite", "pin-project-lite",
"pin-utils", "pin-utils",
"slab",
] ]
[[package]] [[package]]
@@ -648,7 +654,7 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ba121d81ab5ea05b0cd5858516266800bf965531a794f7ac58e3eeb804f364f" checksum = "7ba121d81ab5ea05b0cd5858516266800bf965531a794f7ac58e3eeb804f364f"
dependencies = [ dependencies = [
"bitflags 2.8.0", "bitflags",
"libc", "libc",
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
@@ -701,31 +707,12 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "hash32"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
dependencies = [
"byteorder",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.2" version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 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]] [[package]]
name = "heck" name = "heck"
version = "0.5.0" version = "0.5.0"
@@ -970,27 +957,29 @@ dependencies = [
[[package]] [[package]]
name = "idevice" name = "idevice"
version = "0.1.22" version = "0.1.24"
dependencies = [ dependencies = [
"async-recursion", "async-recursion",
"base64", "base64",
"byteorder", "byteorder",
"bytes",
"env_logger", "env_logger",
"futures",
"indexmap", "indexmap",
"json", "json",
"log", "log",
"ns-keyed-archive", "ns-keyed-archive",
"openssl", "openssl",
"pcap-file",
"plist", "plist",
"rand",
"reqwest", "reqwest",
"serde", "serde",
"serde_json", "serde_json",
"sha2", "sha2",
"smoltcp", "thiserror",
"thiserror 2.0.11",
"tokio", "tokio",
"tokio-openssl", "tokio-openssl",
"tun-rs 2.0.8",
"uuid", "uuid",
] ]
@@ -1006,7 +995,7 @@ dependencies = [
"plist", "plist",
"sha2", "sha2",
"tokio", "tokio",
"tun-rs", "tun-rs 1.5.0",
"ureq", "ureq",
] ]
@@ -1136,12 +1125,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "managed"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.4" version = "2.7.4"
@@ -1206,7 +1189,7 @@ version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
dependencies = [ dependencies = [
"bitflags 2.8.0", "bitflags",
"cfg-if", "cfg-if",
"cfg_aliases 0.1.1", "cfg_aliases 0.1.1",
"libc", "libc",
@@ -1219,7 +1202,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [ dependencies = [
"bitflags 2.8.0", "bitflags",
"cfg-if", "cfg-if",
"cfg_aliases 0.2.1", "cfg_aliases 0.2.1",
"libc", "libc",
@@ -1243,7 +1226,7 @@ checksum = "f237a10fe003123daa55a74b63a0b0a65de1671b2d128711ffe5886891a8f77f"
dependencies = [ dependencies = [
"clap", "clap",
"plist", "plist",
"thiserror 2.0.11", "thiserror",
] ]
[[package]] [[package]]
@@ -1273,7 +1256,7 @@ version = "0.10.69"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e"
dependencies = [ dependencies = [
"bitflags 2.8.0", "bitflags",
"cfg-if", "cfg-if",
"foreign-types", "foreign-types",
"libc", "libc",
@@ -1350,17 +1333,6 @@ dependencies = [
"windows-targets 0.52.6", "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]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.1" version = "2.3.1"
@@ -1416,25 +1388,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]] [[package]]
name = "proc-macro-error-attr2" name = "ppv-lite86"
version = "2.0.0" version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [ dependencies = [
"proc-macro2", "zerocopy",
"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",
] ]
[[package]] [[package]]
@@ -1464,13 +1423,43 @@ dependencies = [
"proc-macro2", "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]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.5.8" version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [ dependencies = [
"bitflags 2.8.0", "bitflags",
] ]
[[package]] [[package]]
@@ -1573,7 +1562,7 @@ version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [ dependencies = [
"bitflags 2.8.0", "bitflags",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
@@ -1654,7 +1643,7 @@ version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [ dependencies = [
"bitflags 2.8.0", "bitflags",
"core-foundation", "core-foundation",
"core-foundation-sys", "core-foundation-sys",
"libc", "libc",
@@ -1756,22 +1745,6 @@ version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 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]] [[package]]
name = "socket2" name = "socket2"
version = "0.5.8" version = "0.5.8"
@@ -1854,7 +1827,7 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
dependencies = [ dependencies = [
"bitflags 2.8.0", "bitflags",
"core-foundation", "core-foundation",
"system-configuration-sys", "system-configuration-sys",
] ]
@@ -1883,33 +1856,13 @@ dependencies = [
"windows-sys 0.59.0", "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]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.11" version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
dependencies = [ dependencies = [
"thiserror-impl 2.0.11", "thiserror-impl",
]
[[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",
] ]
[[package]] [[package]]
@@ -1966,9 +1919,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.43.0" version = "1.44.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
@@ -2095,11 +2048,11 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53141e64197ff7e758b8152615e50bb4a3b18c970738876e7906d31f242c7d6e" checksum = "53141e64197ff7e758b8152615e50bb4a3b18c970738876e7906d31f242c7d6e"
dependencies = [ dependencies = [
"bitflags 2.8.0", "bitflags",
"blocking", "blocking",
"byteorder", "byteorder",
"bytes", "bytes",
"c2rust-bitfields", "c2rust-bitfields 0.19.0",
"cfg-if", "cfg-if",
"encoding_rs", "encoding_rs",
"getifaddrs", "getifaddrs",
@@ -2110,13 +2063,41 @@ dependencies = [
"mac_address", "mac_address",
"nix 0.29.0", "nix 0.29.0",
"scopeguard", "scopeguard",
"thiserror 2.0.11", "thiserror",
"tokio", "tokio",
"windows-sys 0.59.0", "windows-sys 0.59.0",
"winreg", "winreg 0.52.0",
"wintun-bindings", "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]] [[package]]
name = "typenum" name = "typenum"
version = "1.17.0" version = "1.17.0"
@@ -2341,6 +2322,12 @@ dependencies = [
"rustls-pki-types", "rustls-pki-types",
] ]
[[package]]
name = "widestring"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"
@@ -2551,16 +2538,26 @@ dependencies = [
"windows-sys 0.48.0", "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]] [[package]]
name = "wintun-bindings" name = "wintun-bindings"
version = "0.7.30" version = "0.7.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67a02981bed4592bcd271f9bfe154228ddbd2fd69e37a7d358da5d3a1251d696" checksum = "67a02981bed4592bcd271f9bfe154228ddbd2fd69e37a7d358da5d3a1251d696"
dependencies = [ dependencies = [
"c2rust-bitfields", "c2rust-bitfields 0.19.0",
"libloading", "libloading",
"log", "log",
"thiserror 2.0.11", "thiserror",
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
@@ -2570,7 +2567,7 @@ version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
dependencies = [ dependencies = [
"bitflags 2.8.0", "bitflags",
] ]
[[package]] [[package]]
@@ -2609,6 +2606,26 @@ dependencies = [
"synstructure", "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]] [[package]]
name = "zerofrom" name = "zerofrom"
version = "0.1.5" version = "0.1.5"

View File

@@ -2,7 +2,7 @@
name = "idevice" name = "idevice"
description = "A Rust library to interact with services on iOS devices." description = "A Rust library to interact with services on iOS devices."
authors = ["Jackson Coxson"] authors = ["Jackson Coxson"]
version = "0.1.23" version = "0.1.24"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
documentation = "https://docs.rs/idevice" documentation = "https://docs.rs/idevice"
@@ -32,13 +32,15 @@ json = { version = "0.12", optional = true }
byteorder = { version = "1.5", optional = true } byteorder = { version = "1.5", optional = true }
reqwest = { version = "0.12", features = ["json"], optional = true } reqwest = { version = "0.12", features = ["json"], optional = true }
smoltcp = { version = "0.12", optional = true } rand = { version = "0.9", optional = true }
pcap-file = { version = "2.0", optional = true } futures = { version = "0.3", optional = true }
sha2 = { version = "0.10", optional = true } sha2 = { version = "0.10", optional = true }
[dev-dependencies] [dev-dependencies]
tokio = { version = "1.43", features = ["fs"] } tokio = { version = "1.43", features = ["fs"] }
tun-rs = { version = "2.0.8", features = ["async_tokio"] }
bytes = "1.10.1"
[features] [features]
core_device_proxy = ["dep:serde_json", "dep:json", "dep:byteorder"] core_device_proxy = ["dep:serde_json", "dep:json", "dep:byteorder"]
@@ -49,7 +51,7 @@ installation_proxy = []
misagent = [] misagent = []
mounter = ["dep:sha2"] mounter = ["dep:sha2"]
tcp = ["tokio/net"] 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"] tss = ["dep:uuid", "dep:reqwest"]
tunneld = ["dep:serde_json", "dep:json", "dep:reqwest"] tunneld = ["dep:serde_json", "dep:json", "dep:reqwest"]
usbmuxd = ["tokio/net"] usbmuxd = ["tokio/net"]

View File

@@ -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
}
}

View File

@@ -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(()))
}
}

View File

@@ -1,10 +1,5 @@
// Jackson Coxson // Jackson Coxson
#[cfg(feature = "tunnel_tcp_stack")]
mod device;
#[cfg(feature = "tunnel_tcp_stack")]
mod interface;
use crate::{lockdownd::LockdowndClient, Idevice, IdeviceError, IdeviceService}; use crate::{lockdownd::LockdowndClient, Idevice, IdeviceError, IdeviceService};
use byteorder::{BigEndian, WriteBytesExt}; use byteorder::{BigEndian, WriteBytesExt};
@@ -167,7 +162,17 @@ impl CoreDeviceProxy {
} }
#[cfg(feature = "tunnel_tcp_stack")] #[cfg(feature = "tunnel_tcp_stack")]
pub fn create_software_tunnel(self) -> Result<interface::TunnelInterface, IdeviceError> { pub fn create_software_tunnel(self) -> Result<crate::tcp::adapter::Adapter, IdeviceError> {
interface::TunnelInterface::new(self) 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,
))
} }
} }

View File

@@ -9,8 +9,8 @@ use crate::{IdeviceError, ReadWrite};
pub const SERVICE_NAME: &str = "com.apple.internal.dt.remote.debugproxy"; pub const SERVICE_NAME: &str = "com.apple.internal.dt.remote.debugproxy";
pub struct DebugProxyClient { pub struct DebugProxyClient<R: ReadWrite> {
pub socket: Box<dyn ReadWrite>, pub socket: R,
pub noack_mode: bool, pub noack_mode: bool,
} }
@@ -25,14 +25,18 @@ impl DebugserverCommand {
} }
} }
impl DebugProxyClient { impl<R: ReadWrite> DebugProxyClient<R> {
pub fn new(socket: Box<dyn ReadWrite>) -> Self { pub fn new(socket: R) -> Self {
Self { Self {
socket, socket,
noack_mode: false, noack_mode: false,
} }
} }
pub fn into_inner(self) -> R {
self.socket
}
pub async fn send_command( pub async fn send_command(
&mut self, &mut self,
command: DebugserverCommand, command: DebugserverCommand,

View File

@@ -3,18 +3,18 @@
use log::warn; use log::warn;
use plist::{Dictionary, Value}; use plist::{Dictionary, Value};
use crate::{dvt::message::AuxValue, IdeviceError}; use crate::{dvt::message::AuxValue, IdeviceError, ReadWrite};
use super::remote_server::{Channel, RemoteServerClient}; use super::remote_server::{Channel, RemoteServerClient};
const IDENTIFIER: &str = "com.apple.instruments.server.services.processcontrol"; const IDENTIFIER: &str = "com.apple.instruments.server.services.processcontrol";
pub struct ProcessControlClient<'a> { pub struct ProcessControlClient<'a, R: ReadWrite> {
channel: Channel<'a>, channel: Channel<'a, R>,
} }
impl<'a> ProcessControlClient<'a> { impl<'a, R: ReadWrite> ProcessControlClient<'a, R> {
pub async fn new(client: &'a mut RemoteServerClient) -> Result<Self, IdeviceError> { pub async fn new(client: &'a mut RemoteServerClient<R>) -> Result<Self, IdeviceError> {
let channel = client.make_channel(IDENTIFIER).await?; // Drop `&mut client` before continuing let channel = client.make_channel(IDENTIFIER).await?; // Drop `&mut client` before continuing
Ok(Self { channel }) Ok(Self { channel })

View File

@@ -14,20 +14,20 @@ use super::message::AuxValue;
pub const INSTRUMENTS_MESSAGE_TYPE: u32 = 2; pub const INSTRUMENTS_MESSAGE_TYPE: u32 = 2;
pub struct RemoteServerClient { pub struct RemoteServerClient<R: ReadWrite> {
idevice: Box<dyn ReadWrite>, idevice: R,
current_message: u32, current_message: u32,
new_channel: u32, new_channel: u32,
channels: HashMap<u32, VecDeque<Message>>, channels: HashMap<u32, VecDeque<Message>>,
} }
pub struct Channel<'a> { pub struct Channel<'a, R: ReadWrite> {
client: &'a mut RemoteServerClient, client: &'a mut RemoteServerClient<R>,
channel: u32, channel: u32,
} }
impl RemoteServerClient { impl<R: ReadWrite> RemoteServerClient<R> {
pub fn new(idevice: Box<dyn ReadWrite>) -> Result<Self, IdeviceError> { pub fn new(idevice: R) -> Result<Self, IdeviceError> {
let mut channels = HashMap::new(); let mut channels = HashMap::new();
channels.insert(0, VecDeque::new()); channels.insert(0, VecDeque::new());
Ok(Self { Ok(Self {
@@ -38,7 +38,7 @@ impl RemoteServerClient {
}) })
} }
pub fn root_channel(&mut self) -> Channel { pub fn root_channel(&mut self) -> Channel<R> {
Channel { Channel {
client: self, client: self,
channel: 0, channel: 0,
@@ -48,7 +48,7 @@ impl RemoteServerClient {
pub async fn make_channel( pub async fn make_channel(
&mut self, &mut self,
identifier: impl Into<String>, identifier: impl Into<String>,
) -> Result<Channel, IdeviceError> { ) -> Result<Channel<R>, IdeviceError> {
let code = self.new_channel; let code = self.new_channel;
self.new_channel += 1; self.new_channel += 1;
@@ -78,7 +78,7 @@ impl RemoteServerClient {
self.build_channel(code) 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 { Ok(Channel {
client: self, client: self,
channel: code, 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> { pub async fn read_message(&mut self) -> Result<Message, IdeviceError> {
self.client.read_message(self.channel).await self.client.read_message(self.channel).await
} }

View File

@@ -17,20 +17,22 @@ use h2::{
HTTP2_MAGIC, HTTP2_MAGIC,
}; };
use crate::ReadWrite;
pub type Channels = HashMap<u32, (Sender<Vec<u8>>, Receiver<Vec<u8>>)>; pub type Channels = HashMap<u32, (Sender<Vec<u8>>, Receiver<Vec<u8>>)>;
pub struct Connection { pub const INIT_STREAM: u32 = 0;
stream: crate::IdeviceSocket, pub const ROOT_CHANNEL: u32 = 1;
pub const REPLY_CHANNEL: u32 = 3;
pub struct Connection<R: ReadWrite> {
pub stream: R,
channels: Channels, channels: Channels,
window_size: u32, window_size: u32,
} }
impl Connection { impl<R: ReadWrite> Connection<R> {
pub const INIT_STREAM: u32 = 0; pub async fn new(mut stream: R) -> Result<Self, Http2Error> {
pub const ROOT_CHANNEL: u32 = 1;
pub const REPLY_CHANNEL: u32 = 3;
pub async fn new(mut stream: crate::IdeviceSocket) -> Result<Self, Http2Error> {
stream.write_all(HTTP2_MAGIC).await?; stream.write_all(HTTP2_MAGIC).await?;
Ok(Self { Ok(Self {
stream, stream,
@@ -181,12 +183,12 @@ mod tests {
// apart of spec we are allowed to send frames before reading any from the server. // apart of spec we are allowed to send frames before reading any from the server.
// 'INIT_STREAM'/0 applies to all stream_ids. // 'INIT_STREAM'/0 applies to all stream_ids.
client client
.send_frame(WindowUpdateFrame::new(Connection::INIT_STREAM, 983041)) .send_frame(WindowUpdateFrame::new(INIT_STREAM, 983041))
.await .await
.unwrap(); .unwrap();
// We create stream_id '1' by sending Header frame. // 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( frame.set_body(
[ [
0x41, 0x89, 0x2, 0xe0, 0x5c, 0xb, 0x82, 0xe0, 0x40, 0x10, 0x7f, 0x82, 0x84, 0x86, 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. // when server sends 'Settings' on a streamId that the client hasn't sent one on.
// then we must send them back one. // then we must send them back one.
client client
.send_frame(Frame::new(Connection::ROOT_CHANNEL, 1, FrameType::Settings)) .send_frame(Frame::new(ROOT_CHANNEL, 1, FrameType::Settings))
.await .await
.unwrap(); .unwrap();
client client
.write_streamid(Connection::ROOT_CHANNEL, b"nibba\x00".to_vec()) .write_streamid(ROOT_CHANNEL, b"nibba\x00".to_vec())
.await .await
.unwrap(); .unwrap();
// 'END_HEADERS' is sent before data. // 'END_HEADERS' is sent before data.

View File

@@ -19,6 +19,8 @@ pub mod misagent;
pub mod mounter; pub mod mounter;
pub mod pairing_file; pub mod pairing_file;
pub mod provider; pub mod provider;
#[cfg(feature = "tunnel_tcp_stack")]
pub mod tcp;
#[cfg(feature = "tss")] #[cfg(feature = "tss")]
pub mod tss; pub mod tss;
#[cfg(feature = "tunneld")] #[cfg(feature = "tunneld")]
@@ -313,22 +315,13 @@ pub enum IdeviceError {
#[error("unknown channel")] #[error("unknown channel")]
UnknownChannel(u32), UnknownChannel(u32),
#[error("cannot parse string as IpAddr")]
AddrParseError(#[from] std::net::AddrParseError),
#[cfg(feature = "dvt")] #[cfg(feature = "dvt")]
#[error("disable memory limit failed")] #[error("disable memory limit failed")]
DisableMemoryLimitFailed, 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}")] #[error("not enough bytes, expected {1}, got {0}")]
NotEnoughBytes(usize, usize), NotEnoughBytes(usize, usize),

399
idevice/src/tcp/adapter.rs Normal file
View 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
View 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
View 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:#?}");
}
}

View File

@@ -7,7 +7,7 @@ use crate::{
self, self,
h2::{SettingsFrame, WindowUpdateFrame}, h2::{SettingsFrame, WindowUpdateFrame},
}, },
IdeviceError, IdeviceError, ReadWrite,
}; };
use error::XPCError; use error::XPCError;
use format::{XPCFlag, XPCMessage, XPCObject}; use format::{XPCFlag, XPCMessage, XPCObject};
@@ -18,8 +18,8 @@ pub mod cdtunnel;
pub mod error; pub mod error;
pub mod format; pub mod format;
pub struct XPCDevice { pub struct XPCDevice<R: ReadWrite> {
pub connection: XPCConnection, pub connection: XPCConnection<R>,
pub services: HashMap<String, XPCService>, pub services: HashMap<String, XPCService>,
} }
@@ -32,19 +32,17 @@ pub struct XPCService {
pub service_version: Option<i64>, pub service_version: Option<i64>,
} }
pub struct XPCConnection { pub struct XPCConnection<R: ReadWrite> {
inner: http2::Connection, pub(crate) inner: http2::Connection<R>,
root_message_id: u64, root_message_id: u64,
reply_message_id: u64, reply_message_id: u64,
} }
impl XPCDevice { impl<R: ReadWrite> XPCDevice<R> {
pub async fn new(stream: crate::IdeviceSocket) -> Result<Self, IdeviceError> { pub async fn new(stream: R) -> Result<Self, IdeviceError> {
let mut connection = XPCConnection::new(stream).await?; let mut connection = XPCConnection::new(stream).await?;
let data = connection let data = connection.read_message(http2::ROOT_CHANNEL).await?;
.read_message(http2::Connection::ROOT_CHANNEL)
.await?;
let data = match data.message { let data = match data.message {
Some(d) => match d Some(d) => match d
@@ -132,14 +130,18 @@ impl XPCDevice {
services, services,
}) })
} }
pub fn into_inner(self) -> R {
self.connection.inner.stream
}
} }
impl XPCConnection { impl<R: ReadWrite> XPCConnection<R> {
pub const ROOT_CHANNEL: u32 = http2::Connection::ROOT_CHANNEL; pub const ROOT_CHANNEL: u32 = http2::ROOT_CHANNEL;
pub const REPLY_CHANNEL: u32 = http2::Connection::REPLY_CHANNEL; pub const REPLY_CHANNEL: u32 = http2::REPLY_CHANNEL;
const INIT_STREAM: u32 = http2::Connection::INIT_STREAM; 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?; let mut client = http2::Connection::new(stream).await?;
client client
.send_frame(SettingsFrame::new( .send_frame(SettingsFrame::new(

View File

@@ -3,7 +3,6 @@
use clap::{Arg, Command}; use clap::{Arg, Command};
use idevice::{ use idevice::{
core_device_proxy::{self}, core_device_proxy::{self},
xpc::XPCDevice,
IdeviceService, IdeviceService,
}; };
use tun_rs::AbstractDevice; use tun_rs::AbstractDevice;
@@ -63,57 +62,51 @@ async fn main() {
let mut tun_proxy = core_device_proxy::CoreDeviceProxy::connect(&*provider) let mut tun_proxy = core_device_proxy::CoreDeviceProxy::connect(&*provider)
.await .await
.expect("Unable to connect"); .expect("Unable to connect");
let rsd_port = tun_proxy.handshake.server_rsd_port;
let mut tunnel = tun_proxy.create_software_tunnel().unwrap(); let dev = tun_rs::create(&tun_rs::Configuration::default()).unwrap();
let channel = tunnel.connect_tcp(rsd_port).unwrap(); dev.add_address_v6(
let client = XPCDevice::new(Box::new(channel)).await.expect("no xpc??"); tun_proxy
todo!(); .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(); let async_dev = tun_rs::AsyncDevice::new(dev).unwrap();
// dev.add_address_v6( async_dev.enabled(true).unwrap();
// tun_proxy println!("-----------------------------");
// .handshake println!("tun device created: {:?}", async_dev.name());
// .client_parameters println!("server address: {}", tun_proxy.handshake.server_address);
// .address println!("rsd port: {}", tun_proxy.handshake.server_rsd_port);
// .parse() println!("-----------------------------");
// .unwrap(),
// 32, let mut buf = vec![0; 1500];
// ) loop {
// .unwrap(); tokio::select! {
// dev.set_mtu(tun_proxy.handshake.client_parameters.mtu) Ok(len) = async_dev.recv(&mut buf) => {
// .unwrap(); println!("tun pkt: {:?}", &buf[..len]);
// dev.set_network_address( tun_proxy.send(&buf[..len]).await.unwrap();
// tun_proxy.handshake.client_parameters.address.clone(), }
// tun_proxy Ok(res) = tun_proxy.recv() => {
// .handshake println!("dev pkt: {:?}", &res);
// .client_parameters async_dev.send(&res).await.unwrap();
// .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();
// }
// }
// }
} }

View File

@@ -1,14 +1,12 @@
// Jackson Coxson // Jackson Coxson
use std::{ use std::io::Write;
io::Write,
net::{IpAddr, SocketAddr},
str::FromStr,
};
use clap::{Arg, Command}; use clap::{Arg, Command};
use idevice::{debug_proxy::DebugProxyClient, tunneld::get_tunneld_devices, xpc::XPCDevice}; use idevice::{
use tokio::net::TcpStream; core_device_proxy::CoreDeviceProxy, debug_proxy::DebugProxyClient, xpc::XPCDevice,
IdeviceService,
};
mod common; mod common;
@@ -18,6 +16,18 @@ async fn main() {
let matches = Command::new("remotexpc") let matches = Command::new("remotexpc")
.about("Get services from 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(
Arg::new("udid") Arg::new("udid")
.value_name("UDID") .value_name("UDID")
@@ -39,46 +49,41 @@ async fn main() {
} }
let udid = matches.get_one::<String>("udid"); 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( let provider =
IpAddr::from_str("127.0.0.1").unwrap(), match common::get_provider(udid, host, pairing_file, "debug-proxy-jkcoxson").await {
idevice::tunneld::DEFAULT_PORT, Ok(p) => p,
); Err(e) => {
let mut devices = get_tunneld_devices(socket) eprintln!("{e}");
return;
}
};
let proxy = CoreDeviceProxy::connect(&*provider)
.await .await
.expect("Failed to get tunneld devices"); .expect("no core proxy");
let rsd_port = proxy.handshake.server_rsd_port;
let (_udid, device) = match udid { let mut adapter = proxy.create_software_tunnel().expect("no software tunnel");
Some(u) => ( adapter.connect(rsd_port).await.expect("no RSD connect");
u.to_owned(),
devices.remove(u).expect("Device not in tunneld"),
),
None => devices.into_iter().next().expect("No devices"),
};
// Make the connection to RemoteXPC // Make the connection to RemoteXPC
let client = XPCDevice::new(Box::new( let client = XPCDevice::new(Box::new(adapter)).await.unwrap();
TcpStream::connect((device.tunnel_address.as_str(), device.tunnel_port))
.await
.unwrap(),
))
.await
.unwrap();
// Get the debug proxy // Get the debug proxy
let service = client let service = client
.services .services
.get(idevice::debug_proxy::SERVICE_NAME) .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( let mut adapter = client.into_inner();
IpAddr::from_str(&device.tunnel_address).unwrap(), adapter.close().await.unwrap();
service.port, adapter.connect(service.port).await.unwrap();
))
.await
.expect("Failed to connect");
let mut dp = DebugProxyClient::new(Box::new(stream)); let mut dp = DebugProxyClient::new(Box::new(adapter));
println!("Shell connected!"); println!("Shell connected!");
loop { loop {

View File

@@ -1,12 +1,7 @@
// Jackson Coxson // Jackson Coxson
use std::path::PathBuf; use clap::{Arg, Command};
use idevice::{misagent::MisagentClient, pretty_print_plist, IdeviceService};
use clap::{arg, value_parser, Arg, Command};
use idevice::{
lockdownd::LockdowndClient, misagent::MisagentClient, mounter::ImageMounter,
pretty_print_plist, IdeviceService,
};
mod common; mod common;

View File

@@ -1,13 +1,7 @@
// Jackson Coxson // Jackson Coxson
use std::{
net::{IpAddr, SocketAddr},
str::FromStr,
};
use clap::{Arg, Command}; use clap::{Arg, Command};
use idevice::{tunneld::get_tunneld_devices, xpc::XPCDevice}; use idevice::{core_device_proxy::CoreDeviceProxy, xpc::XPCDevice, IdeviceService};
use tokio::net::TcpStream;
mod common; mod common;
@@ -17,6 +11,18 @@ async fn main() {
let matches = Command::new("process_control") let matches = Command::new("process_control")
.about("Query 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(
Arg::new("udid") Arg::new("udid")
.value_name("UDID") .value_name("UDID")
@@ -44,50 +50,44 @@ async fn main() {
} }
let udid = matches.get_one::<String>("udid"); 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 let bundle_id = matches
.get_one::<String>("bundle_id") .get_one::<String>("bundle_id")
.expect("No bundle ID specified"); .expect("No bundle ID specified");
let socket = SocketAddr::new( let proxy = CoreDeviceProxy::connect(&*provider)
IpAddr::from_str("127.0.0.1").unwrap(),
idevice::tunneld::DEFAULT_PORT,
);
let mut devices = get_tunneld_devices(socket)
.await .await
.expect("Failed to get tunneld devices"); .expect("no core proxy");
let rsd_port = proxy.handshake.server_rsd_port;
let (_udid, device) = match udid { let mut adapter = proxy.create_software_tunnel().expect("no software tunnel");
Some(u) => ( adapter.connect(rsd_port).await.expect("no RSD connect");
u.to_owned(),
devices.remove(u).expect("Device not in tunneld"),
),
None => devices.into_iter().next().expect("No devices"),
};
// Make the connection to RemoteXPC // Make the connection to RemoteXPC
let client = XPCDevice::new(Box::new( let client = XPCDevice::new(Box::new(adapter)).await.unwrap();
TcpStream::connect((device.tunnel_address.as_str(), device.tunnel_port))
.await
.unwrap(),
))
.await
.unwrap();
// Get the debug proxy // Get the debug proxy
let service = client let service = client
.services .services
.get(idevice::dvt::SERVICE_NAME) .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( let mut adapter = client.into_inner();
IpAddr::from_str(&device.tunnel_address).unwrap(), adapter.connect(service.port).await.unwrap();
service.port,
))
.await
.expect("Failed to connect");
let mut rs_client = 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??"); rs_client.read_message(0).await.expect("no read??");
let mut pc_client = idevice::dvt::process_control::ProcessControlClient::new(&mut rs_client) let mut pc_client = idevice::dvt::process_control::ProcessControlClient::new(&mut rs_client)
.await .await

View File

@@ -1,14 +1,8 @@
// Jackson Coxson // Jackson Coxson
// Print out all the RemoteXPC services // Print out all the RemoteXPC services
use std::{
net::{IpAddr, SocketAddr},
str::FromStr,
};
use clap::{Arg, Command}; use clap::{Arg, Command};
use idevice::{tunneld::get_tunneld_devices, xpc::XPCDevice}; use idevice::{core_device_proxy::CoreDeviceProxy, xpc::XPCDevice, IdeviceService};
use tokio::net::TcpStream;
mod common; mod common;
@@ -18,6 +12,18 @@ async fn main() {
let matches = Command::new("remotexpc") let matches = Command::new("remotexpc")
.about("Get services from 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(
Arg::new("udid") Arg::new("udid")
.value_name("UDID") .value_name("UDID")
@@ -39,31 +45,28 @@ async fn main() {
} }
let udid = matches.get_one::<String>("udid"); 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( let provider = match common::get_provider(udid, host, pairing_file, "remotexpc-jkcoxson").await
IpAddr::from_str("127.0.0.1").unwrap(), {
idevice::tunneld::DEFAULT_PORT, Ok(p) => p,
); Err(e) => {
let mut devices = get_tunneld_devices(socket) eprintln!("{e}");
.await return;
.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 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 // Make the connection to RemoteXPC
let client = XPCDevice::new(Box::new( let client = XPCDevice::new(Box::new(adapter)).await.unwrap();
TcpStream::connect((device.tunnel_address.as_str(), device.tunnel_port))
.await
.unwrap(),
))
.await
.unwrap();
println!("{:#?}", client.services); println!("{:#?}", client.services);
} }