Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96b67c6162 | ||
|
|
ce02b30b2b | ||
|
|
2fbdfe920e | ||
|
|
7901407d69 | ||
|
|
e6cfdec9c6 | ||
|
|
7e0de2c4e6 | ||
|
|
803af55246 | ||
|
|
8e1a8fa7f0 | ||
|
|
fc6bf407c4 | ||
|
|
dc4e472f20 | ||
|
|
afff49e61a | ||
|
|
0f9ac2ac87 | ||
|
|
4347ed68c3 | ||
|
|
64d68c8a84 | ||
|
|
210b7c959e | ||
|
|
e8322adbcc | ||
|
|
e523b02b58 | ||
|
|
13df028cd9 | ||
|
|
4550bea063 | ||
|
|
be8d4cbb8b | ||
|
|
ace0268a8c | ||
|
|
56fd0d7a4f | ||
|
|
91ee751b07 | ||
|
|
fa9903ace1 | ||
|
|
c2854352df | ||
|
|
b3472fb5b3 | ||
|
|
e373c9ee16 | ||
|
|
352f322f61 | ||
|
|
be966c4f83 | ||
|
|
b8cbf2cdac | ||
|
|
05cf0e1a6f | ||
|
|
f90ef96a5d | ||
|
|
81c6c8c1fc | ||
|
|
7ebdffcdd3 | ||
|
|
1abe33cb16 | ||
|
|
965cbfbb7e | ||
|
|
14ee9c4632 | ||
|
|
ddfa89167d | ||
|
|
1f9ed7b314 |
Generated
+76
-41
@@ -280,7 +280,7 @@ dependencies = [
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -307,7 +307,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "common"
|
||||
version = "1.2.11"
|
||||
version = "1.2.13"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -319,6 +319,7 @@ dependencies = [
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_yaml",
|
||||
"sys-locale",
|
||||
"uuid",
|
||||
"vnt",
|
||||
]
|
||||
@@ -857,9 +858,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
version = "0.2.155"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
@@ -868,7 +869,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -880,7 +881,7 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
|
||||
[[package]]
|
||||
name = "libsm"
|
||||
version = "0.5.1"
|
||||
source = "git+https://github.com/lbl8603/libsm#f9e1b8cf20d0829efb4934a940d0ba4f3ee0ac14"
|
||||
source = "git+https://github.com/vnt-dev/libsm#f9e1b8cf20d0829efb4934a940d0ba4f3ee0ac14"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"getrandom",
|
||||
@@ -954,7 +955,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "lwip-rs"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/lbl8603/lwip-rs#3133f0c3bde55333a27641182ae3550e7acc2a39"
|
||||
source = "git+https://github.com/vnt-dev/lwip-rs#3133f0c3bde55333a27641182ae3550e7acc2a39"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"cc",
|
||||
@@ -1004,6 +1005,18 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "network-interface"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "433419f898328beca4f2c6c73a1b52540658d92b0a99f0269330457e0fd998d5"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"thiserror",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
@@ -1120,7 +1133,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.93"
|
||||
source = "git+https://github.com/lbl8603/rust-openssl#e8b3d2c02d2d07a0e4a82ff4e4bef210f4bc71c8"
|
||||
source = "git+https://github.com/vnt-dev/rust-openssl#e8b3d2c02d2d07a0e4a82ff4e4bef210f4bc71c8"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -1746,9 +1759,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.6"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
|
||||
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
@@ -1834,6 +1847,15 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sys-locale"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e801cf239ecd6ccd71f03d270d67dd53d13e90aab208bf4b8fe4ad957ea949b0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.10.1"
|
||||
@@ -1958,6 +1980,7 @@ dependencies = [
|
||||
"libloading",
|
||||
"log",
|
||||
"rand",
|
||||
"sha2",
|
||||
"widestring",
|
||||
"winapi",
|
||||
]
|
||||
@@ -2095,7 +2118,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "vn-link"
|
||||
version = "1.2.11"
|
||||
version = "1.2.13"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"crossbeam-utils",
|
||||
@@ -2108,7 +2131,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vn-link-cli"
|
||||
version = "1.2.11"
|
||||
version = "1.2.13"
|
||||
dependencies = [
|
||||
"common",
|
||||
"log",
|
||||
@@ -2118,7 +2141,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vnt"
|
||||
version = "1.2.11"
|
||||
version = "1.2.13"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"aes-gcm",
|
||||
@@ -2142,6 +2165,7 @@ dependencies = [
|
||||
"log",
|
||||
"lz4_flex",
|
||||
"mio",
|
||||
"network-interface",
|
||||
"openssl-sys",
|
||||
"packet",
|
||||
"parking_lot",
|
||||
@@ -2160,16 +2184,18 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tun",
|
||||
"windows-sys 0.59.0",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vnt-cli"
|
||||
version = "1.2.11"
|
||||
version = "1.2.13"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"common",
|
||||
"console",
|
||||
"log",
|
||||
"rand",
|
||||
"signal-hook",
|
||||
@@ -2299,7 +2325,7 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2317,7 +2343,16 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2337,18 +2372,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.5",
|
||||
"windows_aarch64_msvc 0.52.5",
|
||||
"windows_i686_gnu 0.52.5",
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.5",
|
||||
"windows_x86_64_gnu 0.52.5",
|
||||
"windows_x86_64_gnullvm 0.52.5",
|
||||
"windows_x86_64_msvc 0.52.5",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2359,9 +2394,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
@@ -2371,9 +2406,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
@@ -2383,15 +2418,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
@@ -2401,9 +2436,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
@@ -2413,9 +2448,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
@@ -2425,9 +2460,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
@@ -2437,9 +2472,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "xml-rs"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Vnt
|
||||
# VNT
|
||||
|
||||
A virtual network tool (VPN)
|
||||
🚀An efficient VPN
|
||||
|
||||
将不同网络下的多个设备虚拟到一个局域网下
|
||||
🚀一个简单、高效、能快速组建虚拟局域网的工具
|
||||
|
||||
### vnt-cli参数详解 [参数说明](https://github.com/lbl8603/vnt/blob/main/vnt-cli/README.md)
|
||||
### vnt-cli参数详解 [参数说明](https://github.com/vnt-dev/vnt/blob/main/vnt-cli/README.md)
|
||||
|
||||
### 快速使用:
|
||||
### 快速开始:
|
||||
|
||||
1. 指定一个token,在多台设备上运行该程序,例如:
|
||||
```shell
|
||||
@@ -39,27 +39,26 @@ A virtual network tool (VPN)
|
||||
```
|
||||
4. 最后可以用虚拟ip实现设备间相互访问
|
||||
|
||||
<img width="506" alt="ssh" src="https://raw.githubusercontent.com/lbl8603/vnt/main/documents/img/ssh.jpg">
|
||||
<img width="506" alt="ssh" src="https://raw.githubusercontent.com/vnt-dev/vnt/main/documents/img/ssh.jpg">
|
||||
5. 帮助,使用-h命令查看
|
||||
|
||||
### 更多玩法
|
||||
|
||||
1. 和远程桌面(如mstsc)搭配,超低延迟的体验
|
||||
2. 安装samba服务,共享磁盘
|
||||
3. 搭配公网服务器nginx反向代理,在公网访问内网文件或服务
|
||||
4. 点对网,访问内网其他机器、IP代理(结合启动参数'-i'和'-o')
|
||||
|
||||
### 使用须知
|
||||
|
||||
- token的作用是标识一个虚拟局域网,当使用公共服务器时,建议使用一个唯一值当token(比如uuid),否则有可能连接到其他人创建的虚拟局域网中
|
||||
- 默认使用公共服务器做注册和中继,目前的配置是2核4G 4Mbps,有需要再扩展~
|
||||
- 需要root/管理员权限
|
||||
- vnt-cli需要使用命令行运行
|
||||
- Mac和Linux下需要加可执行权限(例如:chmod +x ./vnt-cli)
|
||||
- 可以自己搭注册和中继服务器([server](https://github.com/lbl8603/vnts))
|
||||
- vnt使用stun服务器探测网络NAT类型,默认使用谷歌和腾讯的stun服务器,也可自己搭建(-e参数指定)
|
||||
- 可以自己搭中继服务器([server](https://github.com/vnt-dev/vnts))
|
||||
|
||||
### 编译
|
||||
### 直接使用
|
||||
|
||||
[**下载release文件**](https://github.com/vnt-dev/vnt/releases)
|
||||
|
||||
[**帮助文档**](https://rustvnt.com)
|
||||
|
||||
### 自行编译
|
||||
|
||||
<details> <summary>点击展开</summary>
|
||||
|
||||
前提条件:安装rust编译环境([install rust](https://www.rust-lang.org/zh-CN/tools/install))
|
||||
|
||||
@@ -91,82 +90,9 @@ features说明
|
||||
| file_config | yaml配置文件 | 是 |
|
||||
| lz4 | lz4压缩 | 是 |
|
||||
| zstd | zstd压缩 | 否 |
|
||||
|
||||
### ip转发/代理
|
||||
|
||||
如果编译时去除了内置的ip代理(或使用--no-proxy关闭了代理),则可以使用网卡NAT转发来实现点对网,
|
||||
一般来说使用网卡NAT转发会比内置的ip代理性能更好
|
||||
<details> <summary>NAT配置可参考如下示例,点击展开</summary>
|
||||
|
||||
### 在出口一端做如下配置
|
||||
|
||||
注意原有的-i(入口)和-o(出口)的参数不能少
|
||||
|
||||
### windows
|
||||
|
||||
参考 https://learn.microsoft.com/zh-cn/virtualization/hyper-v-on-windows/user-guide/setup-nat-network
|
||||
|
||||
```shell
|
||||
#设置nat,名字可以自己取,网段是vnt的网段
|
||||
New-NetNat -Name vntnat -InternalIPInterfaceAddressPrefix 10.26.0.0/24
|
||||
#查看设置
|
||||
Get-NetNat
|
||||
```
|
||||
|
||||
### linux
|
||||
|
||||
```shell
|
||||
# 开启ip转发
|
||||
sudo sysctl -w net.ipv4.ip_forward=1
|
||||
# 开启nat转发 表示来源10.26.0.0/24的数据通过nat映射后再从vnt-tun以外的其他网卡发出去
|
||||
sudo iptables -t nat -A POSTROUTING ! -o vnt-tun -s 10.26.0.0/24 -j MASQUERADE
|
||||
# 或者这样 表示来源10.26.0.0/24的数据通过nat映射后再从eth0网卡发出去
|
||||
sudo iptables -t nat -A POSTROUTING -o eth0 -s 10.26.0.0/24 -j MASQUERADE
|
||||
# 查看设置
|
||||
iptables -vnL -t nat
|
||||
```
|
||||
|
||||
### Arch Linux
|
||||
|
||||
[](https://repology.org/project/vnt/versions)
|
||||
|
||||
- 通过 AUR 安装 [vnt-git](https://aur.archlinux.org/packages/vnt-git)
|
||||
|
||||
```bash
|
||||
yay -Syu vnt
|
||||
```
|
||||
|
||||
- 通过 `systemd` 设置开机自启及配置
|
||||
|
||||
```bash
|
||||
sudo systemctl enable --now vnt-cli@
|
||||
sudo systemctl status vnt-cli@
|
||||
```
|
||||
|
||||
- 启用内置 `IPv4` 转发规则
|
||||
|
||||
```bash
|
||||
sudo sysctl --system
|
||||
```
|
||||
|
||||
- 通过内置防火墙文件配置防火墙转发规则
|
||||
|
||||
```bash
|
||||
sudo cat /etc/vnt/iptables-vnt.rules >> /etc/iptables/iptables.rules
|
||||
sudo iptables-restore iptables.rules
|
||||
```
|
||||
|
||||
### macos
|
||||
|
||||
```shell
|
||||
# 开启ip转发
|
||||
sudo sysctl -w net.ipv4.ip_forward=1
|
||||
# 配置NAT转发规则
|
||||
# 在/etc/pf.conf文件中添加以下规则,en0是出口网卡,10.26.0.0/24是来源网段
|
||||
nat on en0 from 10.26.0.0/24 to any -> (en0)
|
||||
# 加载规则
|
||||
sudo pfctl -f /etc/pf.conf -e
|
||||
```
|
||||
| upnp | upnp协议 | 否 |
|
||||
| ws | ws协议 | 是 |
|
||||
| wss | wss协议 | 是 |
|
||||
|
||||
</details>
|
||||
|
||||
@@ -176,60 +102,38 @@ sudo pfctl -f /etc/pf.conf -e
|
||||
- Linux
|
||||
- Windows
|
||||
- 默认使用tun网卡 依赖wintun.dll([win-tun](https://www.wintun.net/))(将dll放到同目录下,建议使用版本0.14.1)
|
||||
- 使用tap网卡 依赖tap-windows([win-tap](https://build.openvpn.net/downloads/releases/))(建议使用版本9.24.7)
|
||||
- 可选择使用tap网卡 依赖tap-windows([win-tap](https://build.openvpn.net/downloads/releases/))(建议使用版本9.24.7)
|
||||
- Android
|
||||
|
||||
### GUI
|
||||
|
||||
支持安卓和Windows [下载](https://github.com/lbl8603/VntApp/releases/)
|
||||
支持安卓和Windows [下载](https://github.com/vnt-dev/VntApp/releases/)
|
||||
|
||||
### 特性
|
||||
|
||||
- IP层数据转发
|
||||
- tun虚拟网卡
|
||||
- tap虚拟网卡
|
||||
- NAT穿透
|
||||
- 点对点穿透
|
||||
- 服务端中继转发
|
||||
- 客户端中继转发
|
||||
- IP代理
|
||||
- IP代理(点对点、点对网)
|
||||
- p2p组播/广播
|
||||
- 客户端数据加密
|
||||
- 服务端数据加密
|
||||
- 客户端数据加密(`aes-gcm`、`chacha20-poly1305`等多种加密算法)
|
||||
- 服务端数据加密(`rsa` + `aes-gcm`)
|
||||
- 多通道UDP应对QOS
|
||||
- 支持TCP、UDP、WebSocket等多种协议
|
||||
- 支持数据压缩
|
||||
|
||||
### 结构
|
||||
### 更多玩法
|
||||
|
||||
<details> <summary>展开</summary>
|
||||
|
||||
<pre>
|
||||
|
||||
0 15 31
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|e |s |unused| 版本(4) | 协议(8) | 上层协议(8) |初始ttl(4)|生存时间(4) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 源ip地址(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 目的ip地址(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 数据体(n) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
| 指纹(96) |
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
注:
|
||||
1. e为是否加密标志,s为服务端通信包标志,unused占两位未使用;
|
||||
2. 开启加密时,数据体为加密后的密文(加密方式取决于密码长度和加密模式),
|
||||
且会存在指纹,指纹使用sha256生成,用于对数据包完整性和真实性的校验
|
||||
</pre>
|
||||
|
||||
|
||||
</details>
|
||||
1. 和远程桌面(如mstsc)搭配,超低延迟的体验
|
||||
2. 安装samba服务,共享磁盘
|
||||
3. 点对网,访问内网其他机器、IP代理(结合启动参数'-i'和'-o')
|
||||
|
||||
### Todo
|
||||
|
||||
- 桌面UI(测试中)
|
||||
- ~~桌面UI(已支持)~~
|
||||
- 使用FEC、ARQ等方式提升弱网环境的稳定性
|
||||
|
||||
### 常见问题
|
||||
|
||||
@@ -288,10 +192,12 @@ vnt默认使用10.26.0.0/24网段,和本地网络适配器的ip冲突
|
||||
对VNT有任何问题均可以加群联系作者
|
||||
|
||||
QQ: 1034868233
|
||||
|
||||
### 赞助
|
||||
|
||||
如果VNT对你有帮助,欢迎打赏作者
|
||||
|
||||
<img width="300" alt="" src="https://github.com/lbl8603/vnt/assets/49143209/0d3a7311-43fc-4ed7-9507-863b5d69b6b2">
|
||||
<img width="300" alt="" src="https://github.com/vnt-dev/vnt/assets/49143209/0d3a7311-43fc-4ed7-9507-863b5d69b6b2">
|
||||
|
||||
### 其他
|
||||
|
||||
@@ -302,6 +208,6 @@ QQ: 1034868233
|
||||
|
||||
### 参与贡献
|
||||
|
||||
<a href="https://github.com/lbl8603/vnt/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=lbl8603/vnt" />
|
||||
<a href="https://github.com/vnt-dev/vnt/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=vnt-dev/vnt" />
|
||||
</a>
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "common"
|
||||
version = "1.2.11"
|
||||
version = "1.2.13"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -16,6 +16,7 @@ serde_yaml = "0.9.32"
|
||||
getopts = "0.2.21"
|
||||
gethostname = "0.4.3"
|
||||
uuid = { version = "1.8.0", features = ["v4"] }
|
||||
sys-locale = "0.3.1"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -10,7 +10,7 @@ impl VntCallback for VntHandler {
|
||||
fn success(&self) {
|
||||
println!(" {} ", style("====== Connect Successfully ======").green())
|
||||
}
|
||||
#[cfg(feature = "vnt-model")]
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
fn create_tun(&self, info: vnt::DeviceInfo) {
|
||||
println!("create_tun {}", info)
|
||||
}
|
||||
@@ -37,7 +37,8 @@ impl VntCallback for VntHandler {
|
||||
| ErrorType::AddressExhausted
|
||||
| ErrorType::IpAlreadyExists
|
||||
| ErrorType::InvalidIp
|
||||
| ErrorType::LocalIpExists => {
|
||||
| ErrorType::LocalIpExists
|
||||
| ErrorType::FailedToCrateDevice => {
|
||||
self.stop();
|
||||
}
|
||||
_ => {}
|
||||
|
||||
+205
-54
@@ -5,10 +5,12 @@ use crate::{config, generated_serial_number};
|
||||
use anyhow::anyhow;
|
||||
use console::style;
|
||||
use getopts::Options;
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use sys_locale::get_locale;
|
||||
use vnt::channel::punch::PunchModel;
|
||||
use vnt::channel::UseChannelType;
|
||||
use vnt::cipher::CipherModel;
|
||||
@@ -74,7 +76,9 @@ pub fn parse_args_config() -> anyhow::Result<Option<(Config, Vec<String>, bool)>
|
||||
opts.optmulti("", "vnt-mapping", "vnt-mapping", "<mapping>");
|
||||
opts.optopt("f", "", "配置文件", "<conf>");
|
||||
opts.optopt("", "compressor", "压缩算法", "<lz4>");
|
||||
opts.optopt("", "local-ipv4", "指定本地ipv4网卡IP", "<IP>");
|
||||
opts.optflag("", "disable-stats", "关闭流量统计");
|
||||
opts.optflag("", "allow-wg", "允许接入WireGuard");
|
||||
//"后台运行时,查看其他设备列表"
|
||||
opts.optflag("", "add", "后台运行时,添加地址");
|
||||
opts.optflag("", "list", "后台运行时,查看其他设备列表");
|
||||
@@ -113,10 +117,13 @@ pub fn parse_args_config() -> anyhow::Result<Option<(Config, Vec<String>, bool)>
|
||||
} else if matches.opt_present("all") {
|
||||
command::command(command::CommandEnum::All);
|
||||
return Ok(None);
|
||||
} else if matches.opt_present("chart_a") {
|
||||
}
|
||||
#[cfg(feature = "command")]
|
||||
if matches.opt_present("chart_a") {
|
||||
command::command(command::CommandEnum::ChartA);
|
||||
return Ok(None);
|
||||
}
|
||||
#[cfg(feature = "command")]
|
||||
if let Some(v) = matches.opt_str("chart_b") {
|
||||
command::command(command::CommandEnum::ChartB(v));
|
||||
return Ok(None);
|
||||
@@ -277,7 +284,17 @@ pub fn parse_args_config() -> anyhow::Result<Option<(Config, Vec<String>, bool)>
|
||||
#[cfg(feature = "port_mapping")]
|
||||
let port_mapping_list = matches.opt_strs("mapping");
|
||||
let vnt_mapping_list = matches.opt_strs("vnt-mapping");
|
||||
let local_ipv4: Option<String> = matches.opt_get("local-ipv4").unwrap();
|
||||
let local_ipv4 = local_ipv4
|
||||
.map(|v| Ipv4Addr::from_str(&v).expect(&format!("'--local-ipv4 {}' error", v)));
|
||||
if let Some(local_ipv4) = local_ipv4 {
|
||||
if local_ipv4.is_unspecified() || local_ipv4.is_broadcast() || local_ipv4.is_multicast()
|
||||
{
|
||||
return Err(anyhow::anyhow!("'--local-ipv4 {}' invalid", local_ipv4));
|
||||
}
|
||||
}
|
||||
let disable_stats = matches.opt_present("disable-stats");
|
||||
let allow_wire_guard = matches.opt_present("allow-wg");
|
||||
let compressor = if let Some(compressor) = matches.opt_str("compressor").as_ref() {
|
||||
Compressor::from_str(compressor)
|
||||
.map_err(|e| anyhow!("{}", e))
|
||||
@@ -285,7 +302,7 @@ pub fn parse_args_config() -> anyhow::Result<Option<(Config, Vec<String>, bool)>
|
||||
} else {
|
||||
Compressor::None
|
||||
};
|
||||
let config = match Config::new(
|
||||
let config = Config::new(
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(target_os = "windows")]
|
||||
tap,
|
||||
@@ -318,13 +335,9 @@ pub fn parse_args_config() -> anyhow::Result<Option<(Config, Vec<String>, bool)>
|
||||
port_mapping_list,
|
||||
compressor,
|
||||
!disable_stats,
|
||||
) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
println!("config error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
allow_wire_guard,
|
||||
local_ipv4,
|
||||
)?;
|
||||
(config, vnt_mapping_list, cmd)
|
||||
};
|
||||
println!("version {}", vnt::VNT_VERSION);
|
||||
@@ -337,38 +350,124 @@ pub fn parse_args_config() -> anyhow::Result<Option<(Config, Vec<String>, bool)>
|
||||
Ok(Some((config, vnt_link_config, cmd)))
|
||||
}
|
||||
|
||||
fn get_description(key: &str, language: &str) -> String {
|
||||
// 设置一个全局的映射来存储中英文对照
|
||||
let descriptions: HashMap<&str, (&str, &str)> = [
|
||||
("-k <token>", ("使用相同的token,就能组建一个局域网络", "Use the same token to form a local network")),
|
||||
("-n <name>", ("给设备一个名字,便于区分不同设备,默认使用系统版本", "Give the device a name to distinguish it, defaults to system version")),
|
||||
("-d <id>", ("设备唯一标识符,不使用--ip参数时,服务端凭此参数分配虚拟ip,注意不能重复", "Device unique identifier, used by the server to allocate virtual IP when --ip parameter is not used, must be unique")),
|
||||
("-s <server>", ("注册和中继服务器地址,协议支持使用tcp://和ws://和wss://,默认为udp://", "Registration and relay server address, protocols support using tcp://, ws://, and wss://, default is udp://")),
|
||||
("-e <stun-server>", ("stun服务器,用于探测NAT类型,可使用多个地址,如-e stun.miwifi.com -e turn.cloudflare.com", "STUN server for detecting NAT type, can specify multiple addresses, e.g., -e stun.miwifi.com -e turn.cloudflare.com")),
|
||||
("-a", ("使用tap模式,默认使用tun模式,使用tap时需要配合'--nic'参数指定tap网卡", "Use tap mode, default is tun mode, specify '--nic' parameter with tap network card")),
|
||||
("-i <in-ip>", ("配置点对网(IP代理)时使用,-i 192.168.0.0/24,10.26.0.3表示允许接收网段192.168.0.0/24的数据并转发到10.26.0.3,可指定多个网段", "Used when configuring point-to-point network (IP proxy), -i 192.168.0.0/24,10.26.0.3 allows receiving data from subnet 192.168.0.0/24 and forwarding to 10.26.0.3, specify multiple subnets")),
|
||||
("-o <out-ip>", ("配置点对网时使用,-o 192.168.0.0/24表示允许将数据转发到192.168.0.0/24,可指定多个网段", "Used when configuring point-to-point network, -o 192.168.0.0/24 allows forwarding data to 192.168.0.0/24, specify multiple subnets")),
|
||||
("-w <password>", ("使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密,使用相同密码的客户端才能通信", "Encrypt client data with keys generated by this password, server cannot decrypt, clients must use the same password to communicate")),
|
||||
("-W", ("加密当前客户端和服务端通信的数据,请留意服务端指纹是否正确", "Encrypt the data currently being communicated between the client and server, please pay attention to whether the server fingerprint is correct")),
|
||||
("-u <mtu>", ("自定义mtu(默认为1420)", "Customize MTU (default is 1420)")),
|
||||
("-f <conf_file>", ("读取配置文件中的配置", "Read configuration from file")),
|
||||
("--ip <ip>", ("指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配", "Specify virtual IP, must be unique and valid within server subnet, by default allocated by server")),
|
||||
("--model <model>", ("加密模式(默认aes_gcm),可选值", "Encryption mode (default aes_gcm), options ")),
|
||||
("--finger", ("增加数据指纹校验,可增加安全性,如果服务端开启指纹校验,则客户端也必须开启", "Add data fingerprint verification for increased security, client must enable if server does")),
|
||||
("--punch <punch>", ("取值ipv4/ipv6/ipv4-tcp/ipv4-udp/ipv6-tcp/ipv6-udp/all,ipv4表示仅使用ipv4打洞", "Values ipv4/ipv6/ipv4-tcp/ipv4-udp/ipv6-tcp/ipv6-udp/all, ipv4 for IPv4 hole punching only")),
|
||||
("--ports <port,port>", ("取值0~65535,指定本地监听的一组端口,默认监听两个随机端口,使用过多端口会增加网络负担", "Values 0~65535, specify a group of local listening ports, defaults to two random ports, using many ports increases network load")),
|
||||
("--cmd", ("开启交互式命令,使用此参数开启控制台输入", "Enable interactive command mode, use this parameter to enable console input")),
|
||||
("--no-proxy", ("关闭内置代理,如需点对网则需要配置网卡NAT转发", "Disable built-in proxy, configure network card NAT forwarding for point-to-point networking")),
|
||||
("--first-latency", ("优先低延迟的通道,默认情况优先使用p2p通道", "Prioritize low-latency channels, defaults to prioritizing p2p channel")),
|
||||
("--use-channel <p2p>", ("使用通道 relay/p2p/all,默认两者都使用", "Use channel relay/p2p/all, defaults to using both")),
|
||||
("--nic <tun0>", ("指定虚拟网卡名称", "Specify virtual network card name")),
|
||||
("--packet-loss <0>", ("模拟丢包,取值0~1之间的小数,程序会按设定的概率主动丢包,可用于模拟弱网", "Simulate packet loss, value between 0 and 1, program actively drops packets based on set probability, useful for simulating weak networks")),
|
||||
("--packet-delay <0>", ("模拟延迟,正整数,单位毫秒,程序将根据设定值延迟发送数据包,可用于模拟弱网", "Simulate latency, integer, in milliseconds (ms). The program will delay sending packets according to the set value and can be used to simulate weak networks")),
|
||||
("--dns <host:port>", ("DNS服务器地址,可使用多个dns,不指定时使用系统解析", "DNS server address, can specify multiple DNS servers, defaults to system resolution if not specified")),
|
||||
("--mapping <mapping>", ("端口映射,例如 --mapping udp:0.0.0.0:80-domain:80 映射目标是本地路由能访问的设备", "Port mapping, e.g., --mapping udp:0.0.0.0:80-domain:80 maps to a device accessible by local routing")),
|
||||
("--compressor-all <lz4>", ("启用压缩,可选值lz4/zstd<,level>,level为压缩级别,例如 --compressor lz4 或--compressor zstd,10", "Enable compression, options lz4/zstd<,level>, level is compression level, e.g., --compressor lz4 or --compressor zstd,10")),
|
||||
("--compressor-lz4 <lz4>", ("启用压缩,可选值lz4,例如 --compressor lz4", "Enable compression, option lz4, e.g., --compressor lz4")),
|
||||
("--compressor-zstd <zstd>", ("启用压缩,可选值zstd<,level>,level为压缩级别,例如 --compressor zstd,10", "Enable compression, options zstd<,level>, level is compression level, e.g., --compressor zstd,10")),
|
||||
("--vnt-mapping <x>", ("vnt地址映射,例如 --vnt-mapping tcp:80-10.26.0.10:80 映射目标是vnt网络或其子网中的设备", "VNT address mapping, e.g., --vnt-mapping tcp:80-10.26.0.10:80 maps to a device in VNT network or its subnet")),
|
||||
("--local-ipv4", ("本地出口网卡的ipv4地址", "IPv4 address of local export network card")),
|
||||
("--disable-stats", ("关闭流量统计", "Disable traffic statistics")),
|
||||
("--allow-wg", ("允许接入WireGuard客户端", "Allow access to WireGuard client")),
|
||||
("--list", ("后台运行时,查看其他设备列表", "View list of other devices when running in background")),
|
||||
("--all", ("后台运行时,查看其他设备完整信息", "View complete information of other devices when running in background")),
|
||||
("--info", ("后台运行时,查看当前设备信息", "View information of current device when running in background")),
|
||||
("--route", ("后台运行时,查看数据转发路径", "View data forwarding path when running in background")),
|
||||
("--chart_a", ("后台运行时,查看所有IP的流量统计", "View traffic statistics of all IPs when running in background")),
|
||||
("--chart_b <IP>", ("后台运行时,查看单个IP的历史流量", "View historical traffic of a single IP when running in background")),
|
||||
("--stop", ("停止后台运行", "Stop running in background"))
|
||||
// ... 其他选项
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
if let Some(&(zh, en)) = descriptions.get(key) {
|
||||
if language.starts_with("zh") {
|
||||
return zh.to_string(); // 返回 String 类型
|
||||
}
|
||||
// 默认返回英文
|
||||
return en.to_string(); // 返回 String 类型
|
||||
}
|
||||
// 如果没有找到对应的键,则返回空字符串
|
||||
String::new()
|
||||
}
|
||||
|
||||
fn print_usage(program: &str, _opts: Options) {
|
||||
// 获取系统语言 Locale::user_default().unwrap_or_else(|_| Locale::default());
|
||||
let language = get_locale().unwrap_or_else(|| String::from("en-US"));
|
||||
println!("Usage: {} [options]", program);
|
||||
println!("version:{}", vnt::VNT_VERSION);
|
||||
println!("Serial:{}", generated_serial_number::SERIAL_NUMBER);
|
||||
println!("Options:");
|
||||
println!(
|
||||
" -k <token> {}",
|
||||
green("使用相同的token,就能组建一个局域网络".to_string())
|
||||
green(get_description("-k <token>", &language).to_string())
|
||||
);
|
||||
println!(" -n <name> 给设备一个名字,便于区分不同设备,默认使用系统版本");
|
||||
println!(" -d <id> 设备唯一标识符,不使用--ip参数时,服务端凭此参数分配虚拟ip,注意不能重复");
|
||||
println!(
|
||||
" -s <server> 注册和中继服务器地址,协议支持使用tcp://和ws://和wss://,默认为udp://"
|
||||
" -n <name> {}",
|
||||
get_description("-n <name>", &language)
|
||||
);
|
||||
println!(
|
||||
" -d <id> {}",
|
||||
get_description("-d <id>", &language)
|
||||
);
|
||||
println!(
|
||||
" -s <server> {}",
|
||||
get_description("-s <server>", &language)
|
||||
);
|
||||
println!(
|
||||
" -e <stun-server> {}",
|
||||
get_description("-e <stun-server>", &language)
|
||||
);
|
||||
println!(" -e <stun-server> stun服务器,用于探测NAT类型,可使用多个地址,如-e stun.miwifi.com -e turn.cloudflare.com");
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
println!(" -a {}", get_description("-a", &language));
|
||||
println!(
|
||||
" -a 使用tap模式,默认使用tun模式,使用tap时需要配合'--nic'参数指定tap网卡"
|
||||
" -i <in-ip> {}",
|
||||
get_description("-i <in-ip>", &language)
|
||||
);
|
||||
println!(
|
||||
" -o <out-ip> {}",
|
||||
get_description("-o <out-ip>", &language)
|
||||
);
|
||||
println!(
|
||||
" -w <password> {}",
|
||||
get_description("-w <password>", &language)
|
||||
);
|
||||
println!(" -i <in-ip> 配置点对网(IP代理)时使用,-i 192.168.0.0/24,10.26.0.3表示允许接收网段192.168.0.0/24的数据");
|
||||
println!(" 并转发到10.26.0.3,可指定多个网段");
|
||||
println!(" -o <out-ip> 配置点对网时使用,-o 192.168.0.0/24表示允许将数据转发到192.168.0.0/24,可指定多个网段");
|
||||
|
||||
println!(" -w <password> 使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密,使用相同密码的客户端才能通信");
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
println!(" -W 加密当前客户端和服务端通信的数据,请留意服务端指纹是否正确");
|
||||
println!(" -u <mtu> 自定义mtu(不加密默认为1450,加密默认为1410)");
|
||||
println!(" -W {}", get_description("-W", &language));
|
||||
println!(
|
||||
" -u <mtu> {}",
|
||||
get_description("-u <mtu>", &language)
|
||||
);
|
||||
#[cfg(feature = "file_config")]
|
||||
println!(" -f <conf_file> 读取配置文件中的配置");
|
||||
println!(
|
||||
" -f <conf_file> {}",
|
||||
get_description("-f <conf_file>", &language)
|
||||
);
|
||||
|
||||
println!(" --ip <ip> 指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配");
|
||||
println!(
|
||||
" --ip <ip> {}",
|
||||
get_description("--ip <ip>", &language)
|
||||
);
|
||||
let mut enums = String::new();
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
enums.push_str("/aes_gcm");
|
||||
@@ -382,7 +481,8 @@ fn print_usage(program: &str, _opts: Options) {
|
||||
enums.push_str("/sm4_cbc");
|
||||
enums.push_str("/xor");
|
||||
println!(
|
||||
" --model <model> 加密模式(默认aes_gcm),可选值{}",
|
||||
" --model <model> {}{}",
|
||||
get_description("--model <model>", &language),
|
||||
&enums[1..]
|
||||
);
|
||||
#[cfg(any(
|
||||
@@ -393,45 +493,96 @@ fn print_usage(program: &str, _opts: Options) {
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
))]
|
||||
println!(" --finger 增加数据指纹校验,可增加安全性,如果服务端开启指纹校验,则客户端也必须开启");
|
||||
println!(" --punch <punch> 取值ipv4/ipv6/all,ipv4表示仅使用ipv4打洞");
|
||||
println!(" --ports <port,port> 取值0~65535,指定本地监听的一组端口,默认监听两个随机端口,使用过多端口会增加网络负担");
|
||||
println!(
|
||||
" --finger {}",
|
||||
get_description("--finger", &language)
|
||||
);
|
||||
println!(
|
||||
" --punch <punch> {}",
|
||||
get_description("--punch <punch>", &language)
|
||||
);
|
||||
println!(
|
||||
" --ports <port,port> {}",
|
||||
get_description("--ports <port,port>", &language)
|
||||
);
|
||||
#[cfg(feature = "command")]
|
||||
println!(" --cmd 开启交互式命令,使用此参数开启控制台输入");
|
||||
println!(
|
||||
" --cmd {}",
|
||||
get_description("--cmd", &language)
|
||||
);
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
println!(" --no-proxy 关闭内置代理,如需点对网则需要配置网卡NAT转发");
|
||||
println!(" --first-latency 优先低延迟的通道,默认情况优先使用p2p通道");
|
||||
println!(" --use-channel <p2p> 使用通道 relay/p2p/all,默认两者都使用");
|
||||
#[cfg(not(feature = "vn-link-model"))]
|
||||
println!(" --nic <tun0> 指定虚拟网卡名称");
|
||||
println!(" --packet-loss <0> 模拟丢包,取值0~1之间的小数,程序会按设定的概率主动丢包,可用于模拟弱网");
|
||||
println!(
|
||||
" --packet-delay <0> 模拟延迟,整数,单位毫秒(ms),程序会按设定的值延迟发包,可用于模拟弱网"
|
||||
" --no-proxy {}",
|
||||
get_description("--no-proxy", &language)
|
||||
);
|
||||
println!(
|
||||
" --first-latency {}",
|
||||
get_description("--first-latency", &language)
|
||||
);
|
||||
println!(
|
||||
" --use-channel <p2p> {}",
|
||||
get_description("--use-channel <p2p>", &language)
|
||||
);
|
||||
#[cfg(not(feature = "vn-link-model"))]
|
||||
println!(
|
||||
" --nic <tun0> {}",
|
||||
get_description("--nic <tun0>", &language)
|
||||
);
|
||||
println!(
|
||||
" --packet-loss <0> {}",
|
||||
get_description("--packet-loss <0>", &language)
|
||||
);
|
||||
println!(
|
||||
" --packet-delay <0> {}",
|
||||
get_description("--packet-delay <0>", &language)
|
||||
);
|
||||
println!(
|
||||
" --dns <host:port> {}",
|
||||
get_description("--dns <host:port>", &language)
|
||||
);
|
||||
println!(" --dns <host:port> DNS服务器地址,可使用多个dns,不指定时使用系统解析");
|
||||
|
||||
#[cfg(feature = "port_mapping")]
|
||||
println!(" --mapping <mapping> 端口映射,例如 --mapping udp:0.0.0.0:80-domain:80 映射目标是本地路由能访问的设备");
|
||||
println!(
|
||||
" --mapping <mapping> {}",
|
||||
get_description("--mapping <mapping>", &language)
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "lz4", feature = "zstd"))]
|
||||
println!(" --compressor <lz4> 启用压缩,可选值lz4/zstd<,level>,level为压缩级别,例如 --compressor lz4 或--compressor zstd,10");
|
||||
println!(
|
||||
" --compressor <lz4> {}",
|
||||
get_description("--compressor-all <lz4>", &language)
|
||||
);
|
||||
#[cfg(feature = "lz4")]
|
||||
#[cfg(not(feature = "zstd"))]
|
||||
println!(" --compressor <lz4> 启用压缩,可选值lz4,例如 --compressor lz4");
|
||||
println!(
|
||||
" --compressor <lz4> {}",
|
||||
get_description("--compressor-lz4 <lz4>", &language)
|
||||
);
|
||||
#[cfg(feature = "zstd")]
|
||||
#[cfg(not(feature = "lz4"))]
|
||||
println!(" --compressor <zstd> 启用压缩,可选值zstd<,level>,level为压缩级别,例如 --compressor zstd,10");
|
||||
println!(
|
||||
" --compressor <zstd> {}",
|
||||
get_description("--compressor-zstd <zstd>", &language)
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "integrated_tun"))]
|
||||
println!(
|
||||
" --vnt-mapping <x> {}",
|
||||
green(
|
||||
"vnt地址映射,例如 --vnt-mapping tcp:80-10.26.0.10:80 映射目标是vnt网络或其子网中的设备"
|
||||
.to_string()
|
||||
)
|
||||
green(get_description("--vnt-mapping <x>", &language).to_string())
|
||||
);
|
||||
println!(
|
||||
" --local-ipv4 <IP> {}",
|
||||
get_description("--local-ipv4", &language)
|
||||
);
|
||||
println!(
|
||||
" --disable-stats {}",
|
||||
get_description("--disable-stats", &language)
|
||||
);
|
||||
println!(
|
||||
" --allow-wg {}",
|
||||
get_description("--allow-wg", &language)
|
||||
);
|
||||
println!(" --disable-stats 关闭流量统计");
|
||||
println!();
|
||||
#[cfg(feature = "command")]
|
||||
{
|
||||
@@ -442,34 +593,34 @@ fn print_usage(program: &str, _opts: Options) {
|
||||
// );
|
||||
println!(
|
||||
" --list {}",
|
||||
yellow("后台运行时,查看其他设备列表".to_string())
|
||||
yellow(get_description("--list", &language).to_string())
|
||||
);
|
||||
println!(
|
||||
" --all {}",
|
||||
yellow("后台运行时,查看其他设备完整信息".to_string())
|
||||
yellow(get_description("--all", &language).to_string())
|
||||
);
|
||||
println!(
|
||||
" --info {}",
|
||||
yellow("后台运行时,查看当前设备信息".to_string())
|
||||
yellow(get_description("--info", &language).to_string())
|
||||
);
|
||||
println!(
|
||||
" --route {}",
|
||||
yellow("后台运行时,查看数据转发路径".to_string())
|
||||
yellow(get_description("--route", &language).to_string())
|
||||
);
|
||||
println!(
|
||||
" --chart_a {}",
|
||||
yellow("后台运行时,查看所有IP的流量统计".to_string())
|
||||
yellow(get_description("--chart_a", &language).to_string())
|
||||
);
|
||||
println!(
|
||||
" --chart_b <IP> {}",
|
||||
yellow("后台运行时,查看单个IP的历史流量".to_string())
|
||||
yellow(get_description("--chart_b <IP>", &language).to_string())
|
||||
);
|
||||
println!(
|
||||
" --stop {}",
|
||||
yellow("停止后台运行".to_string())
|
||||
yellow(get_description("--stop", &language).to_string())
|
||||
);
|
||||
}
|
||||
println!(" -h, --help 帮助");
|
||||
println!(" -h, --help display help information(显示帮助信息)");
|
||||
}
|
||||
|
||||
fn green(str: String) -> impl std::fmt::Display {
|
||||
|
||||
@@ -45,6 +45,7 @@ pub struct DeviceItem {
|
||||
pub client_secret_hash: Vec<u8>,
|
||||
pub current_client_secret: bool,
|
||||
pub current_client_secret_hash: Vec<u8>,
|
||||
pub wire_guard: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||
|
||||
@@ -219,6 +219,7 @@ pub fn command_list(vnt: &Vnt) -> Vec<DeviceItem> {
|
||||
client_secret_hash: peer.client_secret_hash,
|
||||
current_client_secret,
|
||||
current_client_secret_hash: client_encrypt_hash.to_vec(),
|
||||
wire_guard: peer.wireguard,
|
||||
};
|
||||
list.push(item);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,9 @@ pub struct FileConfig {
|
||||
pub compressor: Option<String>,
|
||||
pub vnt_mapping: Vec<String>,
|
||||
pub disable_stats: bool,
|
||||
// 允许传递wg流量
|
||||
pub allow_wire_guard: bool,
|
||||
pub local_ipv4: Option<Ipv4Addr>,
|
||||
}
|
||||
|
||||
impl Default for FileConfig {
|
||||
@@ -90,6 +93,8 @@ impl Default for FileConfig {
|
||||
compressor: None,
|
||||
vnt_mapping: vec![],
|
||||
disable_stats: false,
|
||||
allow_wire_guard: false,
|
||||
local_ipv4: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,8 +104,8 @@ pub fn read_config(file_path: &str) -> anyhow::Result<(Config, Vec<String>, bool
|
||||
let file_conf = match serde_yaml::from_str::<FileConfig>(&conf) {
|
||||
Ok(val) => val,
|
||||
Err(e) => {
|
||||
log::error!("{:?}", e);
|
||||
return Err(anyhow!("{}", e));
|
||||
log::error!("serde_yaml::from_str {:?}", e);
|
||||
return Err(anyhow!("serde_yaml::from_str {:?}", e));
|
||||
}
|
||||
};
|
||||
if file_conf.token.is_empty() {
|
||||
@@ -123,12 +128,12 @@ pub fn read_config(file_path: &str) -> anyhow::Result<(Config, Vec<String>, bool
|
||||
None => None,
|
||||
Some(r) => Some(r.map_err(|e| anyhow!("ip {:?} error:{}", &file_conf.ip, e))?),
|
||||
};
|
||||
let cipher_model = {
|
||||
let cipher_model = if let Some(v) = file_conf.cipher_model {
|
||||
CipherModel::from_str(&v).map_err(|e| anyhow!("{}", e))?
|
||||
} else {
|
||||
#[cfg(not(any(feature = "aes_gcm", feature = "server_encrypt")))]
|
||||
if file_conf.password.is_some() && file_conf.cipher_model.is_none() {
|
||||
if file_conf.password.is_some() {
|
||||
Err(anyhow!("cipher_model undefined"))?
|
||||
} else if let Some(v) = file_conf.cipher_model {
|
||||
CipherModel::from_str(&v).map_err(|e| anyhow!("{}", e))?
|
||||
} else {
|
||||
CipherModel::None
|
||||
}
|
||||
@@ -177,6 +182,8 @@ pub fn read_config(file_path: &str) -> anyhow::Result<(Config, Vec<String>, bool
|
||||
file_conf.mapping,
|
||||
compressor,
|
||||
!file_conf.disable_stats,
|
||||
file_conf.allow_wire_guard,
|
||||
file_conf.local_ipv4,
|
||||
)?;
|
||||
|
||||
Ok((config, file_conf.vnt_mapping, file_conf.cmd))
|
||||
|
||||
@@ -132,15 +132,21 @@ pub fn console_device_list(mut list: Vec<DeviceItem>) {
|
||||
("Rt".to_string(), Style::new()),
|
||||
]);
|
||||
for item in list {
|
||||
let name = if item.wire_guard {
|
||||
format!("{}(wg)", item.name)
|
||||
} else {
|
||||
item.name
|
||||
};
|
||||
if &item.status == "Online" {
|
||||
if item.client_secret != item.current_client_secret
|
||||
|| (!item.current_client_secret_hash.is_empty()
|
||||
&& !item.client_secret_hash.is_empty()
|
||||
&& item.current_client_secret_hash != item.client_secret_hash)
|
||||
if !item.wire_guard
|
||||
&& (item.client_secret != item.current_client_secret
|
||||
|| (!item.current_client_secret_hash.is_empty()
|
||||
&& !item.client_secret_hash.is_empty()
|
||||
&& item.current_client_secret_hash != item.client_secret_hash))
|
||||
{
|
||||
//加密状态不一致,无法通信的
|
||||
out_list.push(vec![
|
||||
(item.name, Style::new().red()),
|
||||
(name, Style::new().red()),
|
||||
(item.virtual_ip, Style::new().red()),
|
||||
(item.status, Style::new().red()),
|
||||
("Mismatch".to_string(), Style::new().red()),
|
||||
@@ -149,7 +155,7 @@ pub fn console_device_list(mut list: Vec<DeviceItem>) {
|
||||
} else {
|
||||
if item.nat_traversal_type.contains("p2p") {
|
||||
out_list.push(vec![
|
||||
(item.name, Style::new().green()),
|
||||
(name, Style::new().green()),
|
||||
(item.virtual_ip, Style::new().green()),
|
||||
(item.status, Style::new().green()),
|
||||
(item.nat_traversal_type, Style::new().green()),
|
||||
@@ -157,7 +163,7 @@ pub fn console_device_list(mut list: Vec<DeviceItem>) {
|
||||
]);
|
||||
} else {
|
||||
out_list.push(vec![
|
||||
(item.name, Style::new().yellow()),
|
||||
(name, Style::new().yellow()),
|
||||
(item.virtual_ip, Style::new().yellow()),
|
||||
(item.status, Style::new().yellow()),
|
||||
(item.nat_traversal_type, Style::new().yellow()),
|
||||
@@ -167,7 +173,7 @@ pub fn console_device_list(mut list: Vec<DeviceItem>) {
|
||||
}
|
||||
} else {
|
||||
out_list.push(vec![
|
||||
(item.name, Style::new().color256(102)),
|
||||
(name, Style::new().color256(102)),
|
||||
(item.virtual_ip, Style::new().color256(102)),
|
||||
(item.status, Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "vn-link-cli"
|
||||
version = "1.2.11"
|
||||
version = "1.2.13"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@@ -11,7 +11,7 @@ log = "0.4.17"
|
||||
|
||||
[features]
|
||||
default = ["default-feature"]
|
||||
default-feature = ["server_encrypt", "aes_gcm", "aes_cbc", "aes_ecb", "sm4_cbc", "chacha20_poly1305", "port_mapping", "log", "command", "file_config", "lz4"]
|
||||
default-feature = ["server_encrypt", "aes_gcm", "aes_cbc", "aes_ecb", "sm4_cbc", "chacha20_poly1305", "port_mapping", "log", "command", "file_config", "lz4", "ws"]
|
||||
|
||||
openssl = ["vn-link/openssl", "common/openssl"]
|
||||
openssl-vendored = ["vn-link/openssl-vendored", "common/openssl-vendored"]
|
||||
|
||||
@@ -12,7 +12,12 @@ fn main() {
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{}", e);
|
||||
log::error!(
|
||||
"parse error={:?} cmd={:?}",
|
||||
e,
|
||||
std::env::args().collect::<Vec<String>>()
|
||||
);
|
||||
println!("Error {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
[package]
|
||||
name = "vn-link"
|
||||
version = "1.2.11"
|
||||
version = "1.2.13"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
lwip-rs = { git = "https://github.com/lbl8603/lwip-rs" }
|
||||
lwip-rs = { git = "https://github.com/vnt-dev/lwip-rs" }
|
||||
vnt = { path = "../vnt", package = "vnt", default-features = false }
|
||||
log = "0.4.17"
|
||||
anyhow = "1.0.82"
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "vnt-cli"
|
||||
version = "1.2.11"
|
||||
version = "1.2.13"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -10,7 +10,7 @@ vnt = { path = "../vnt", package = "vnt", default-features = false, features = [
|
||||
common = { path = "../common", default-features = false, features = ["integrated_tun"] }
|
||||
log = "0.4.17"
|
||||
anyhow = "1.0.82"
|
||||
|
||||
console = "0.15.2"
|
||||
|
||||
[target.'cfg(any(target_os = "linux",target_os = "macos"))'.dependencies]
|
||||
sudo = "0.6.0"
|
||||
|
||||
+15
-3
@@ -74,9 +74,11 @@
|
||||
|
||||
设置虚拟网卡的mtu值,大多数情况下使用默认值效率会更高,也可根据实际情况微调这个值,不加密默认为1450,加密默认为1410
|
||||
|
||||
### --tcp
|
||||
### ~~--tcp~~
|
||||
|
||||
和服务端使用tcp通信。有些网络提供商对UDP限制比较大,这个时候可以选择使用TCP模式,提高稳定性。一般来说udp延迟和消耗更低
|
||||
~~和服务端使用tcp通信。有些网络提供商对UDP限制比较大,这个时候可以选择使用TCP模式,提高稳定性。一般来说udp延迟和消耗更低~~
|
||||
|
||||
新版本使用 `-s tcp://`的形式使用tcp
|
||||
|
||||
### --ip `<IP>`
|
||||
|
||||
@@ -135,7 +137,7 @@ aes_gcm/aes_cbc/aes_ecb/sm4_cbc/chacha20_poly1305/chacha20/xor,默认使用aes
|
||||
### --no-proxy
|
||||
|
||||
关闭内置的ip代理,内置的代理较为简单,而且一般来说直接使用网卡NAT转发性能会更高,
|
||||
有需要可以自行配置NAT转发,[可参考‘编译’小节中的NAT配置](https://github.com/lbl8603/vnt#%E7%BC%96%E8%AF%91)
|
||||
有需要可以自行配置NAT转发,[可参考‘编译’小节中的NAT配置](https://github.com/vnt-dev/vnt#%E7%BC%96%E8%AF%91)
|
||||
|
||||
### --dns `<223.5.5.5>`
|
||||
|
||||
@@ -201,6 +203,8 @@ mapping:
|
||||
- udp:0.0.0.0:80-10.26.0.10:80 # 映射udp数据
|
||||
- tcp:0.0.0.0:80-10.26.0.10:81 # 映射tcp数据
|
||||
- tcp:0.0.0.0:82-localhost:83 # 映射tcp数据
|
||||
disable_stats: false # 为true表示关闭统计
|
||||
allow_wire_guard: false # 为true则表示允许接入wg
|
||||
```
|
||||
|
||||
或者需要哪个配置就加哪个,当然token是必须的
|
||||
@@ -223,6 +227,14 @@ token: xxx #组网token
|
||||
|
||||
模拟延迟,整数,单位毫秒(ms),程序会按设定的值延迟发包,可用于模拟弱网
|
||||
|
||||
### --disable-stats
|
||||
|
||||
关闭流量统计
|
||||
|
||||
### --allow-wg
|
||||
|
||||
允许接入WireGuard客户端,和wg混用时必须开启此参数
|
||||
|
||||
### --list
|
||||
|
||||
在后台运行时,查看其他设备列表
|
||||
|
||||
+8
-1
@@ -1,4 +1,5 @@
|
||||
use common::callback;
|
||||
use console::style;
|
||||
use vnt::core::{Config, Vnt};
|
||||
mod root_check;
|
||||
fn main() {
|
||||
@@ -11,7 +12,12 @@ fn main() {
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{}", e);
|
||||
log::error!(
|
||||
"parse error={:?} cmd={:?}",
|
||||
e,
|
||||
std::env::args().collect::<Vec<String>>()
|
||||
);
|
||||
println!("{}", style(format!("Error {:?}", e)).red());
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -35,6 +41,7 @@ fn main0(config: Config, _show_cmd: bool) {
|
||||
let vnt_util = match Vnt::new(config, callback::VntHandler {}) {
|
||||
Ok(vnt) => vnt,
|
||||
Err(e) => {
|
||||
log::error!("vnt create error {:?}", e);
|
||||
println!("error: {:?}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
+14
-5
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "vnt"
|
||||
version = "1.2.11"
|
||||
version = "1.2.13"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -18,7 +18,7 @@ rand = "0.8.5"
|
||||
sha2 = { version = "0.10.6", features = ["oid"] }
|
||||
thiserror = "1.0.37"
|
||||
protobuf = "=3.2.0"
|
||||
socket2 = { version = "0.5.2", features = ["all"] }
|
||||
socket2 = { version = "0.5.7", features = ["all"] }
|
||||
aes-gcm = { version = "0.10.2", optional = true }
|
||||
ring = { version = "0.17.0", optional = true }
|
||||
cbc = { version = "0.1.2", optional = true }
|
||||
@@ -29,8 +29,8 @@ aes = "0.8.3"
|
||||
stun-format = { version = "1.0.1", features = ["fmt", "rfc3489"] }
|
||||
rsa = { version = "0.9.2", features = [], optional = true }
|
||||
spki = { version = "0.7.2", features = ["fingerprint", "alloc", "base64"], optional = true }
|
||||
openssl-sys = { git = "https://github.com/lbl8603/rust-openssl", optional = true }
|
||||
libsm = { git = "https://github.com/lbl8603/libsm", optional = true }
|
||||
openssl-sys = { git = "https://github.com/vnt-dev/rust-openssl", optional = true }
|
||||
libsm = { git = "https://github.com/vnt-dev/libsm", optional = true }
|
||||
|
||||
mio = { version = "=0.8.11", features = ["os-poll", "net", "os-ext"] }
|
||||
crossbeam-queue = "0.3.11"
|
||||
@@ -46,10 +46,19 @@ fnv = "1.0.7"
|
||||
igd = { version = "0.12.1", optional = true }
|
||||
tokio-tungstenite = { version = "0.23.1", optional = true }
|
||||
rustls = { version = "0.23.0", features = ["ring"], default-features = false, optional = true }
|
||||
|
||||
network-interface = "2.0.0"
|
||||
|
||||
futures-util = "0.3.30"
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
libloading = "0.8.0"
|
||||
|
||||
windows-sys = {version = "0.59.0",features = [ "Win32_Foundation",
|
||||
"Win32_NetworkManagement",
|
||||
"Win32_NetworkManagement_IpHelper",
|
||||
"Win32_Networking_WinSock",
|
||||
"Win32_System_IO",
|
||||
"Win32_System_Threading",
|
||||
"Win32_System_WindowsProgramming",]}
|
||||
|
||||
[build-dependencies]
|
||||
protobuf-codegen = "=3.2.0"
|
||||
|
||||
@@ -43,6 +43,7 @@ message DeviceInfo {
|
||||
uint32 device_status = 3;
|
||||
bool client_secret = 4;
|
||||
bytes client_secret_hash = 5;
|
||||
bool wireguard = 6;
|
||||
}
|
||||
|
||||
message DeviceList {
|
||||
@@ -63,11 +64,23 @@ message PunchInfo {
|
||||
uint32 tcp_port = 11;
|
||||
repeated uint32 udp_ports = 12;
|
||||
repeated uint32 public_ports = 13;
|
||||
uint32 public_tcp_port = 14;
|
||||
PunchNatModel punch_model = 15;
|
||||
}
|
||||
enum PunchNatType {
|
||||
Symmetric = 0;
|
||||
Cone = 1;
|
||||
}
|
||||
enum PunchNatModel {
|
||||
All = 0;
|
||||
IPv4 = 1;
|
||||
IPv6 = 2;
|
||||
IPv4Tcp = 3;
|
||||
IPv4Udp = 4;
|
||||
IPv6Tcp = 5;
|
||||
IPv6Udp = 6;
|
||||
}
|
||||
|
||||
/// 向服务器上报客户端状态信息
|
||||
message ClientStatusInfo {
|
||||
fixed32 source = 1;
|
||||
|
||||
+81
-89
@@ -1,7 +1,7 @@
|
||||
use fnv::FnvHashMap;
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV6, UdpSocket};
|
||||
use std::net::{Ipv4Addr, SocketAddr, UdpSocket};
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{io, thread};
|
||||
@@ -12,6 +12,7 @@ use rand::Rng;
|
||||
|
||||
use crate::channel::punch::NatType;
|
||||
use crate::channel::sender::{AcceptSocketSender, PacketSender};
|
||||
use crate::channel::socket::LocalInterface;
|
||||
use crate::channel::{ConnectProtocol, Route, RouteKey, UseChannelType, DEFAULT_RT};
|
||||
use crate::protocol::NetPacket;
|
||||
use crate::util::limit::TrafficMeterMultiAddress;
|
||||
@@ -25,16 +26,17 @@ pub struct ChannelContext {
|
||||
impl ChannelContext {
|
||||
pub fn new(
|
||||
main_udp_socket: Vec<UdpSocket>,
|
||||
v4_len: usize,
|
||||
use_channel_type: UseChannelType,
|
||||
first_latency: bool,
|
||||
protocol: ConnectProtocol,
|
||||
packet_loss_rate: Option<f64>,
|
||||
packet_delay: u32,
|
||||
use_ipv6: bool,
|
||||
up_traffic_meter: Option<TrafficMeterMultiAddress>,
|
||||
down_traffic_meter: Option<TrafficMeterMultiAddress>,
|
||||
default_interface: LocalInterface,
|
||||
) -> Self {
|
||||
let channel_num = main_udp_socket.len();
|
||||
let channel_num = v4_len;
|
||||
assert_ne!(channel_num, 0, "not channel");
|
||||
let packet_loss_rate = packet_loss_rate
|
||||
.map(|v| {
|
||||
@@ -48,16 +50,17 @@ impl ChannelContext {
|
||||
.unwrap_or(0);
|
||||
let inner = ContextInner {
|
||||
main_udp_socket,
|
||||
v4_len,
|
||||
sub_udp_socket: RwLock::new(Vec::new()),
|
||||
packet_map: RwLock::new(FnvHashMap::default()),
|
||||
route_table: RouteTable::new(use_channel_type, first_latency, channel_num),
|
||||
protocol,
|
||||
packet_loss_rate,
|
||||
packet_delay,
|
||||
main_index: AtomicUsize::new(0),
|
||||
use_ipv6,
|
||||
up_traffic_meter,
|
||||
down_traffic_meter,
|
||||
default_interface,
|
||||
default_route_key: AtomicCell::default(),
|
||||
};
|
||||
Self {
|
||||
inner: Arc::new(inner),
|
||||
@@ -80,10 +83,11 @@ const PACKET_LOSS_RATE_DENOMINATOR: u32 = 100_0000;
|
||||
pub struct ContextInner {
|
||||
// 核心udp socket
|
||||
pub(crate) main_udp_socket: Vec<UdpSocket>,
|
||||
v4_len: usize,
|
||||
// 对称网络增加的udp socket
|
||||
sub_udp_socket: RwLock<Vec<UdpSocket>>,
|
||||
// tcp数据发送器
|
||||
pub(crate) packet_map: RwLock<FnvHashMap<SocketAddr, PacketSender>>,
|
||||
pub(crate) packet_map: RwLock<FnvHashMap<RouteKey, PacketSender>>,
|
||||
// 路由信息
|
||||
pub route_table: RouteTable,
|
||||
// 使用什么协议连接服务器
|
||||
@@ -92,16 +96,22 @@ pub struct ContextInner {
|
||||
packet_loss_rate: u32,
|
||||
//控制延迟
|
||||
packet_delay: u32,
|
||||
main_index: AtomicUsize,
|
||||
use_ipv6: bool,
|
||||
pub(crate) up_traffic_meter: Option<TrafficMeterMultiAddress>,
|
||||
pub(crate) down_traffic_meter: Option<TrafficMeterMultiAddress>,
|
||||
default_interface: LocalInterface,
|
||||
default_route_key: AtomicCell<Option<RouteKey>>,
|
||||
}
|
||||
|
||||
impl ContextInner {
|
||||
pub fn use_channel_type(&self) -> UseChannelType {
|
||||
self.route_table.use_channel_type
|
||||
}
|
||||
pub fn default_interface(&self) -> &LocalInterface {
|
||||
&self.default_interface
|
||||
}
|
||||
pub fn set_default_route_key(&self, route_key: RouteKey) {
|
||||
self.default_route_key.store(Some(route_key));
|
||||
}
|
||||
/// 通过sub_udp_socket是否为空来判断是否为锥形网络
|
||||
pub fn is_cone(&self) -> bool {
|
||||
self.sub_udp_socket.read().is_empty()
|
||||
@@ -120,7 +130,7 @@ impl ContextInner {
|
||||
&self,
|
||||
nat_type: NatType,
|
||||
udp_socket_sender: &AcceptSocketSender<Option<Vec<mio::net::UdpSocket>>>,
|
||||
) -> io::Result<()> {
|
||||
) -> anyhow::Result<()> {
|
||||
let mut write_guard = self.sub_udp_socket.write();
|
||||
match nat_type {
|
||||
NatType::Symmetric => {
|
||||
@@ -129,9 +139,11 @@ impl ContextInner {
|
||||
}
|
||||
let mut vec = Vec::with_capacity(SYMMETRIC_CHANNEL_NUM);
|
||||
for _ in 0..SYMMETRIC_CHANNEL_NUM {
|
||||
let udp = UdpSocket::bind("0.0.0.0:0")?;
|
||||
//副通道使用异步io
|
||||
udp.set_nonblocking(true)?;
|
||||
let udp = crate::channel::socket::bind_udp(
|
||||
"0.0.0.0:0".parse().unwrap(),
|
||||
&self.default_interface,
|
||||
)?;
|
||||
let udp: UdpSocket = udp.into();
|
||||
vec.push(udp);
|
||||
}
|
||||
let mut mio_vec = Vec::with_capacity(SYMMETRIC_CHANNEL_NUM);
|
||||
@@ -152,39 +164,39 @@ impl ContextInner {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn channel_num(&self) -> usize {
|
||||
self.v4_len
|
||||
}
|
||||
#[inline]
|
||||
pub fn main_len(&self) -> usize {
|
||||
self.main_udp_socket.len()
|
||||
}
|
||||
/// 获取核心udp监听的端口,用于其他客户端连接
|
||||
pub fn main_local_udp_port(&self) -> io::Result<Vec<u16>> {
|
||||
let mut ports = Vec::new();
|
||||
for udp in self.main_udp_socket.iter() {
|
||||
for udp in self.main_udp_socket[..self.v4_len].iter() {
|
||||
ports.push(udp.local_addr()?.port())
|
||||
}
|
||||
Ok(ports)
|
||||
}
|
||||
pub fn send_tcp(&self, buf: &[u8], addr: SocketAddr) -> io::Result<()> {
|
||||
if let Some(tcp) = self.packet_map.read().get(&addr) {
|
||||
pub fn send_tcp(&self, buf: &[u8], route_key: &RouteKey) -> io::Result<()> {
|
||||
if let Some(tcp) = self.packet_map.read().get(route_key) {
|
||||
tcp.try_send(buf)
|
||||
} else {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
format!("dest={:?}", route_key),
|
||||
))
|
||||
}
|
||||
}
|
||||
pub fn send_main_udp(&self, index: usize, buf: &[u8], mut addr: SocketAddr) -> io::Result<()> {
|
||||
if self.use_ipv6 {
|
||||
//如果是v4地址则需要转换成v6
|
||||
if let SocketAddr::V4(ipv4) = addr {
|
||||
addr = SocketAddr::V6(SocketAddrV6::new(
|
||||
ipv4.ip().to_ipv6_mapped(),
|
||||
ipv4.port(),
|
||||
0,
|
||||
0,
|
||||
));
|
||||
}
|
||||
pub fn send_main_udp(&self, index: usize, buf: &[u8], addr: SocketAddr) -> io::Result<()> {
|
||||
if let Some(udp) = self.main_udp_socket.get(index) {
|
||||
udp.send_to(buf, addr)?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "overflow"))
|
||||
}
|
||||
self.main_udp_socket[index].send_to(buf, addr)?;
|
||||
Ok(())
|
||||
}
|
||||
/// 将数据发送到默认通道,一般发往服务器才用此方法
|
||||
pub fn send_default<B: AsRef<[u8]>>(
|
||||
@@ -193,9 +205,20 @@ impl ContextInner {
|
||||
addr: SocketAddr,
|
||||
) -> io::Result<()> {
|
||||
if self.protocol.is_udp() {
|
||||
self.send_main_udp(self.main_index.load(Ordering::Relaxed), buf.buffer(), addr)?
|
||||
if addr.is_ipv4() {
|
||||
self.send_main_udp(0, buf.buffer(), addr)?
|
||||
} else {
|
||||
self.send_main_udp(self.v4_len, buf.buffer(), addr)?
|
||||
}
|
||||
} else {
|
||||
self.send_tcp(buf.buffer(), addr)?
|
||||
if let Some(key) = self.default_route_key.load() {
|
||||
self.send_tcp(buf.buffer(), &key)?
|
||||
} else {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
format!("dest={:?}", addr),
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(up_traffic_meter) = &self.up_traffic_meter {
|
||||
up_traffic_meter.add_traffic(buf.destination(), buf.data_len());
|
||||
@@ -203,10 +226,6 @@ impl ContextInner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn change_main_index(&self) {
|
||||
let index = (self.main_index.load(Ordering::Relaxed) + 1) % self.main_udp_socket.len();
|
||||
self.main_index.store(index, Ordering::Relaxed);
|
||||
}
|
||||
/// 此方法仅用于对称网络打洞
|
||||
pub fn try_send_all(&self, buf: &[u8], addr: SocketAddr) {
|
||||
self.try_send_all_main(buf, addr);
|
||||
@@ -287,7 +306,7 @@ impl ContextInner {
|
||||
if let Some(udp) = self
|
||||
.sub_udp_socket
|
||||
.read()
|
||||
.get(route_key.index - self.main_udp_socket.len())
|
||||
.get(route_key.index - self.main_len())
|
||||
{
|
||||
udp.send_to(buf.buffer(), route_key.addr)?;
|
||||
} else {
|
||||
@@ -296,7 +315,7 @@ impl ContextInner {
|
||||
}
|
||||
}
|
||||
ConnectProtocol::TCP | ConnectProtocol::WS | ConnectProtocol::WSS => {
|
||||
self.send_tcp(buf.buffer(), route_key.addr)?
|
||||
self.send_tcp(buf.buffer(), &route_key)?
|
||||
}
|
||||
}
|
||||
if let Some(up_traffic_meter) = &self.up_traffic_meter {
|
||||
@@ -372,19 +391,11 @@ impl RouteTable {
|
||||
let key = route.route_key();
|
||||
if only_if_absent {
|
||||
if let Some((_, list)) = self.route_table.read().get(&id) {
|
||||
let mut p2p_num = 0;
|
||||
for (x, _) in list {
|
||||
if x.is_p2p() {
|
||||
p2p_num += 1;
|
||||
}
|
||||
if x.route_key() == key {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if !self.first_latency && p2p_num >= self.channel_num {
|
||||
// 非优先延迟的情况下,通道满了则不用再添加
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut route_table = self.route_table.write();
|
||||
@@ -409,61 +420,42 @@ impl RouteTable {
|
||||
}
|
||||
}
|
||||
if exist {
|
||||
// 这个排序还有待优化,因为后加入的大概率排最后,被直接淘汰的概率也大,可能导致更好的通道被移除了
|
||||
list.sort_by_key(|(k, _)| k.rt);
|
||||
//如果延迟都稳定了,则去除多余通道
|
||||
for (route, _) in list.iter() {
|
||||
if route.rt == DEFAULT_RT {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//延迟优先模式需要更多的通道探测延迟最低的路线
|
||||
let limit_len = if self.first_latency {
|
||||
self.channel_num + 2
|
||||
} else {
|
||||
self.channel_num
|
||||
};
|
||||
self.truncate_(list, limit_len);
|
||||
} else {
|
||||
if !self.first_latency {
|
||||
if route.is_p2p() {
|
||||
//非优先延迟的情况下 添加了直连的则排除非直连的
|
||||
list.retain(|(k, _)| k.is_p2p());
|
||||
}
|
||||
if self.channel_num <= list.len() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
//增加路由表容量,避免波动
|
||||
let limit_len = self.channel_num * 2;
|
||||
list.sort_by_key(|(k, _)| k.rt);
|
||||
self.truncate_(list, limit_len);
|
||||
list.push((route, AtomicCell::new(Instant::now())));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
fn truncate_(&self, list: &mut Vec<(Route, AtomicCell<Instant>)>, len: usize) {
|
||||
if list.len() <= len {
|
||||
return;
|
||||
}
|
||||
if self.first_latency {
|
||||
//找到第一个p2p通道
|
||||
if let Some(index) =
|
||||
list.iter()
|
||||
.enumerate()
|
||||
.find_map(|(index, (route, _))| if route.is_p2p() { Some(index) } else { None })
|
||||
{
|
||||
if index >= len {
|
||||
//保留第一个p2p通道
|
||||
let route = list.remove(index);
|
||||
list.truncate(len - 1);
|
||||
list.push(route);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
list.truncate(len);
|
||||
}
|
||||
// 直接移除会导致通道不稳定,所以废弃这个方法,后面改用多余通道不发心跳包,从而让通道自动过期
|
||||
// fn truncate_(&self, list: &mut Vec<(Route, AtomicCell<Instant>)>, len: usize) {
|
||||
// if list.len() <= len {
|
||||
// return;
|
||||
// }
|
||||
// if self.first_latency {
|
||||
// //找到第一个p2p通道
|
||||
// if let Some(index) =
|
||||
// list.iter()
|
||||
// .enumerate()
|
||||
// .find_map(|(index, (route, _))| if route.is_p2p() { Some(index) } else { None })
|
||||
// {
|
||||
// if index >= len {
|
||||
// //保留第一个p2p通道
|
||||
// let route = list.remove(index);
|
||||
// list.truncate(len - 1);
|
||||
// list.push(route);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// list.truncate(len);
|
||||
// }
|
||||
pub fn route(&self, id: &Ipv4Addr) -> Option<Vec<Route>> {
|
||||
if let Some((_, v)) = self.route_table.read().get(id) {
|
||||
Some(v.iter().map(|(i, _)| *i).collect())
|
||||
|
||||
+70
-27
@@ -6,6 +6,7 @@ use tokio::sync::mpsc::channel;
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::handler::RecvChannelHandler;
|
||||
use crate::channel::sender::{AcceptSocketSender, ConnectUtil};
|
||||
use crate::channel::socket::{bind_udp, LocalInterface};
|
||||
use crate::channel::tcp_channel::tcp_listen;
|
||||
use crate::channel::udp_channel::udp_listen;
|
||||
#[cfg(feature = "ws")]
|
||||
@@ -19,6 +20,7 @@ pub mod idle;
|
||||
pub mod notify;
|
||||
pub mod punch;
|
||||
pub mod sender;
|
||||
pub mod socket;
|
||||
pub mod tcp_channel;
|
||||
pub mod udp_channel;
|
||||
#[cfg(feature = "ws")]
|
||||
@@ -177,7 +179,7 @@ pub struct RouteKey {
|
||||
}
|
||||
|
||||
impl RouteKey {
|
||||
pub(crate) fn new(protocol: ConnectProtocol, index: usize, addr: SocketAddr) -> Self {
|
||||
pub(crate) const fn new(protocol: ConnectProtocol, index: usize, addr: SocketAddr) -> Self {
|
||||
Self {
|
||||
protocol,
|
||||
index,
|
||||
@@ -201,11 +203,13 @@ pub(crate) fn init_context(
|
||||
protocol: ConnectProtocol,
|
||||
packet_loss_rate: Option<f64>,
|
||||
packet_delay: u32,
|
||||
default_interface: LocalInterface,
|
||||
up_traffic_meter: Option<TrafficMeterMultiAddress>,
|
||||
down_traffic_meter: Option<TrafficMeterMultiAddress>,
|
||||
) -> anyhow::Result<(ChannelContext, std::net::TcpListener)> {
|
||||
assert!(!ports.is_empty(), "not channel");
|
||||
let mut udps = Vec::with_capacity(ports.len());
|
||||
let mut main_udp_socket_v4 = Vec::with_capacity(ports.len());
|
||||
let mut main_udp_socket_v6 = Vec::with_capacity(ports.len());
|
||||
//检查系统是否支持ipv6
|
||||
let use_ipv6 = match socket2::Socket::new(socket2::Domain::IPV6, socket2::Type::DGRAM, None) {
|
||||
Ok(_) => true,
|
||||
@@ -215,40 +219,33 @@ pub(crate) fn init_context(
|
||||
}
|
||||
};
|
||||
for port in &ports {
|
||||
//监听v6+v4双栈
|
||||
let (socket, address) = if use_ipv6 {
|
||||
let address: SocketAddr = format!("[::]:{}", port).parse().unwrap();
|
||||
let socket = socket2::Socket::new(socket2::Domain::IPV6, socket2::Type::DGRAM, None)?;
|
||||
socket
|
||||
.set_only_v6(false)
|
||||
.with_context(|| format!("set_only_v6 failed: {}", &address))?;
|
||||
(socket, address)
|
||||
let addr_v4: SocketAddr = format!("0.0.0.0:{}", port).parse().unwrap();
|
||||
if use_ipv6 {
|
||||
let (main_channel_v4, main_channel_v6) = bind_udp_v4_and_v6(*port, &default_interface)?;
|
||||
main_udp_socket_v4.push(main_channel_v4);
|
||||
main_udp_socket_v6.push(main_channel_v6);
|
||||
} else {
|
||||
let address: SocketAddr = format!("0.0.0.0:{}", port).parse().unwrap();
|
||||
(
|
||||
socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::DGRAM, None)?,
|
||||
address,
|
||||
)
|
||||
};
|
||||
if let Err(e) = socket.set_recv_buffer_size(2 * 1024 * 1024) {
|
||||
log::warn!("set_recv_buffer_size {:?}", e);
|
||||
let socket = bind_udp(addr_v4, &default_interface)?;
|
||||
let main_channel_v4: UdpSocket = socket.into();
|
||||
main_udp_socket_v4.push(main_channel_v4);
|
||||
}
|
||||
socket
|
||||
.bind(&address.into())
|
||||
.with_context(|| format!("bind failed: {}", &address))?;
|
||||
let main_channel: UdpSocket = socket.into();
|
||||
udps.push(main_channel);
|
||||
}
|
||||
let mut main_udp_socket =
|
||||
Vec::with_capacity(main_udp_socket_v4.len() + main_udp_socket_v6.len());
|
||||
let v4_len = main_udp_socket_v4.len();
|
||||
main_udp_socket.append(&mut main_udp_socket_v4);
|
||||
main_udp_socket.append(&mut main_udp_socket_v6);
|
||||
let context = ChannelContext::new(
|
||||
udps,
|
||||
main_udp_socket,
|
||||
v4_len,
|
||||
use_channel_type,
|
||||
first_latency,
|
||||
protocol,
|
||||
packet_loss_rate,
|
||||
packet_delay,
|
||||
use_ipv6,
|
||||
up_traffic_meter,
|
||||
down_traffic_meter,
|
||||
default_interface,
|
||||
);
|
||||
|
||||
let port = context.main_local_udp_port()?[0];
|
||||
@@ -265,7 +262,13 @@ pub(crate) fn init_context(
|
||||
let socket = socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::STREAM, None)?;
|
||||
(socket, address)
|
||||
};
|
||||
|
||||
socket
|
||||
.set_reuse_address(true)
|
||||
.context("set_reuse_address")?;
|
||||
#[cfg(unix)]
|
||||
if let Err(e) = socket.set_reuse_port(true) {
|
||||
log::warn!("set_reuse_port {:?}", e)
|
||||
}
|
||||
if let Err(e) = socket.bind(&address.into()) {
|
||||
if ports[0] == 0 {
|
||||
//端口可能冲突,则使用任意端口
|
||||
@@ -285,9 +288,49 @@ pub(crate) fn init_context(
|
||||
}
|
||||
socket.listen(128)?;
|
||||
socket.set_nonblocking(true)?;
|
||||
socket.set_nodelay(false)?;
|
||||
socket.set_nodelay(true)?;
|
||||
Ok((context, socket.into()))
|
||||
}
|
||||
fn bind_udp_v4_and_v6(
|
||||
port: u16,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<(UdpSocket, UdpSocket)> {
|
||||
let mut count = 0;
|
||||
loop {
|
||||
let addr_v4: SocketAddr = format!("0.0.0.0:{}", port).parse().unwrap();
|
||||
let socket = bind_udp(addr_v4, default_interface)?;
|
||||
if let Err(e) = socket.set_recv_buffer_size(2 * 1024 * 1024) {
|
||||
log::warn!("set_recv_buffer_size {:?}", e);
|
||||
}
|
||||
let main_channel_v4: UdpSocket = socket.into();
|
||||
let addr = main_channel_v4.local_addr()?;
|
||||
let addr_v6: SocketAddr = format!("[::]:{}", addr.port()).parse().unwrap();
|
||||
let socket = if port == 0 {
|
||||
match bind_udp(addr_v6, default_interface) {
|
||||
Ok(socket) => socket,
|
||||
Err(e) => {
|
||||
if count > 10 {
|
||||
return Err(e);
|
||||
}
|
||||
if let Some(e) = e.downcast_ref::<std::io::Error>() {
|
||||
if e.kind() == std::io::ErrorKind::AddrInUse {
|
||||
count += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Err(e)?
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bind_udp(addr_v6, default_interface)?
|
||||
};
|
||||
if let Err(e) = socket.set_recv_buffer_size(2 * 1024 * 1024) {
|
||||
log::warn!("set_recv_buffer_size {:?}", e);
|
||||
}
|
||||
let main_channel_v6: UdpSocket = socket.into();
|
||||
return Ok((main_channel_v4, main_channel_v6));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn init_channel<H>(
|
||||
tcp_listener: std::net::TcpListener,
|
||||
|
||||
+161
-47
@@ -1,4 +1,3 @@
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use std::collections::HashMap;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||
use std::ops::{Div, Mul};
|
||||
@@ -7,20 +6,46 @@ use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{io, thread};
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use rand::prelude::SliceRandom;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::sender::ConnectUtil;
|
||||
use crate::external_route::ExternalRoute;
|
||||
use crate::handle::CurrentDeviceInfo;
|
||||
use crate::nat::NatTest;
|
||||
use crate::nat::{is_ipv4_global, NatTest};
|
||||
use crate::proto::message::{PunchNatModel, PunchNatType};
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum PunchModel {
|
||||
All,
|
||||
IPv4,
|
||||
IPv6,
|
||||
All,
|
||||
IPv4Tcp,
|
||||
IPv4Udp,
|
||||
IPv6Tcp,
|
||||
IPv6Udp,
|
||||
}
|
||||
|
||||
impl PunchModel {
|
||||
pub fn use_tcp(&self) -> bool {
|
||||
self != &PunchModel::IPv4Udp && self != &PunchModel::IPv6Udp
|
||||
}
|
||||
pub fn use_udp(&self) -> bool {
|
||||
self != &PunchModel::IPv4Tcp && self != &PunchModel::IPv6Tcp
|
||||
}
|
||||
pub fn use_ipv6(&self) -> bool {
|
||||
self == &PunchModel::All
|
||||
|| self == &PunchModel::IPv6
|
||||
|| self == &PunchModel::IPv6Tcp
|
||||
|| self == &PunchModel::IPv6Udp
|
||||
}
|
||||
pub fn use_ipv4(&self) -> bool {
|
||||
self == &PunchModel::All
|
||||
|| self == &PunchModel::IPv4
|
||||
|| self == &PunchModel::IPv4Tcp
|
||||
|| self == &PunchModel::IPv4Udp
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PunchModel {
|
||||
@@ -30,8 +55,15 @@ impl FromStr for PunchModel {
|
||||
match s.to_lowercase().trim() {
|
||||
"ipv4" => Ok(PunchModel::IPv4),
|
||||
"ipv6" => Ok(PunchModel::IPv6),
|
||||
"ipv4-tcp" => Ok(PunchModel::IPv4Tcp),
|
||||
"ipv4-udp" => Ok(PunchModel::IPv4Udp),
|
||||
"ipv6-tcp" => Ok(PunchModel::IPv6Tcp),
|
||||
"ipv6-udp" => Ok(PunchModel::IPv6Udp),
|
||||
"all" => Ok(PunchModel::All),
|
||||
_ => Err(format!("not match '{}', enum: ipv4/ipv6/all", s)),
|
||||
_ => Err(format!(
|
||||
"not match '{}', enum: ipv4/ipv4-tcp/ipv4-udp/ipv6/ipv6-tcp/ipv6-udp/all",
|
||||
s
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,6 +73,33 @@ impl Default for PunchModel {
|
||||
PunchModel::All
|
||||
}
|
||||
}
|
||||
impl From<PunchModel> for PunchNatModel {
|
||||
fn from(value: PunchModel) -> Self {
|
||||
match value {
|
||||
PunchModel::All => PunchNatModel::All,
|
||||
PunchModel::IPv4 => PunchNatModel::IPv4,
|
||||
PunchModel::IPv6 => PunchNatModel::IPv6,
|
||||
PunchModel::IPv4Tcp => PunchNatModel::IPv4Tcp,
|
||||
PunchModel::IPv4Udp => PunchNatModel::IPv4Udp,
|
||||
PunchModel::IPv6Tcp => PunchNatModel::IPv6Tcp,
|
||||
PunchModel::IPv6Udp => PunchNatModel::IPv6Udp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<PunchModel> for PunchNatModel {
|
||||
fn into(self) -> PunchModel {
|
||||
match self {
|
||||
PunchNatModel::All => PunchModel::All,
|
||||
PunchNatModel::IPv4 => PunchModel::IPv4,
|
||||
PunchNatModel::IPv6 => PunchModel::IPv6,
|
||||
PunchNatModel::IPv4Tcp => PunchModel::IPv4Tcp,
|
||||
PunchNatModel::IPv4Udp => PunchModel::IPv4Udp,
|
||||
PunchNatModel::IPv6Tcp => PunchModel::IPv6Tcp,
|
||||
PunchNatModel::IPv6Udp => PunchModel::IPv6Udp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NatInfo {
|
||||
@@ -52,6 +111,8 @@ pub struct NatInfo {
|
||||
pub(crate) ipv6: Option<Ipv6Addr>,
|
||||
pub udp_ports: Vec<u16>,
|
||||
pub tcp_port: u16,
|
||||
pub public_tcp_port: u16,
|
||||
pub punch_model: PunchModel,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
@@ -60,6 +121,29 @@ pub enum NatType {
|
||||
Cone,
|
||||
}
|
||||
|
||||
impl NatType {
|
||||
pub fn is_cone(&self) -> bool {
|
||||
self == &NatType::Cone
|
||||
}
|
||||
}
|
||||
impl From<NatType> for PunchNatType {
|
||||
fn from(value: NatType) -> Self {
|
||||
match value {
|
||||
NatType::Symmetric => PunchNatType::Symmetric,
|
||||
NatType::Cone => PunchNatType::Cone,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<NatType> for PunchNatType {
|
||||
fn into(self) -> NatType {
|
||||
match self {
|
||||
PunchNatType::Symmetric => NatType::Symmetric,
|
||||
PunchNatType::Cone => NatType::Cone,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NatInfo {
|
||||
pub fn new(
|
||||
mut public_ips: Vec<Ipv4Addr>,
|
||||
@@ -69,7 +153,9 @@ impl NatInfo {
|
||||
mut ipv6: Option<Ipv6Addr>,
|
||||
udp_ports: Vec<u16>,
|
||||
tcp_port: u16,
|
||||
public_tcp_port: u16,
|
||||
mut nat_type: NatType,
|
||||
punch_model: PunchModel,
|
||||
) -> Self {
|
||||
public_ips.retain(|ip| {
|
||||
!ip.is_multicast()
|
||||
@@ -99,7 +185,9 @@ impl NatInfo {
|
||||
ipv6,
|
||||
udp_ports,
|
||||
tcp_port,
|
||||
public_tcp_port,
|
||||
nat_type,
|
||||
punch_model,
|
||||
}
|
||||
}
|
||||
pub fn update_addr(&mut self, index: usize, ip: Ipv4Addr, port: u16) -> bool {
|
||||
@@ -113,7 +201,7 @@ impl NatInfo {
|
||||
*public_port = port;
|
||||
}
|
||||
}
|
||||
if crate::nat::is_ipv4_global(&ip) {
|
||||
if is_ipv4_global(&ip) {
|
||||
if !self.public_ips.contains(&ip) {
|
||||
self.public_ips.push(ip);
|
||||
updated = true;
|
||||
@@ -122,6 +210,9 @@ impl NatInfo {
|
||||
}
|
||||
updated
|
||||
}
|
||||
pub fn update_tcp_port(&mut self, port: u16) {
|
||||
self.public_tcp_port = port;
|
||||
}
|
||||
pub fn local_ipv4(&self) -> Option<Ipv4Addr> {
|
||||
self.local_ipv4
|
||||
}
|
||||
@@ -187,9 +278,7 @@ pub struct Punch {
|
||||
port_vec: Vec<u16>,
|
||||
port_index: HashMap<Ipv4Addr, usize>,
|
||||
punch_model: PunchModel,
|
||||
is_tcp: bool,
|
||||
connect_util: ConnectUtil,
|
||||
external_route: ExternalRoute,
|
||||
nat_test: NatTest,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
}
|
||||
@@ -198,9 +287,7 @@ impl Punch {
|
||||
pub fn new(
|
||||
context: ChannelContext,
|
||||
punch_model: PunchModel,
|
||||
is_tcp: bool,
|
||||
connect_util: ConnectUtil,
|
||||
external_route: ExternalRoute,
|
||||
nat_test: NatTest,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
) -> Self {
|
||||
@@ -213,9 +300,7 @@ impl Punch {
|
||||
port_vec,
|
||||
port_index: HashMap::new(),
|
||||
punch_model,
|
||||
is_tcp,
|
||||
connect_util,
|
||||
external_route,
|
||||
nat_test,
|
||||
current_device,
|
||||
}
|
||||
@@ -227,7 +312,10 @@ impl Punch {
|
||||
if self.nat_test.is_local_address(true, addr) {
|
||||
return;
|
||||
}
|
||||
self.connect_util.try_connect_tcp(buf.to_vec(), addr);
|
||||
if addr.ip().is_unspecified() || addr.port() == 0 {
|
||||
return;
|
||||
}
|
||||
self.connect_util.try_connect_tcp_punch(buf.to_vec(), addr);
|
||||
}
|
||||
pub fn punch(
|
||||
&mut self,
|
||||
@@ -242,35 +330,58 @@ impl Punch {
|
||||
return Ok(());
|
||||
}
|
||||
let device_info = self.current_device.load();
|
||||
nat_info.public_ips.retain(|ip| {
|
||||
self.external_route.route(ip).is_none() && device_info.not_in_network(*ip)
|
||||
});
|
||||
nat_info.local_ipv4.filter(|ip| {
|
||||
self.external_route.route(ip).is_none() && device_info.not_in_network(*ip)
|
||||
});
|
||||
nat_info.ipv6.filter(|ip| {
|
||||
if let Some(ip) = ip.to_ipv4() {
|
||||
self.external_route.route(&ip).is_none()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
if punch_tcp && self.is_tcp && nat_info.tcp_port != 0 {
|
||||
|
||||
nat_info
|
||||
.public_ips
|
||||
.retain(|ip| is_ipv4_global(ip) && device_info.not_in_network(*ip));
|
||||
nat_info.public_ports.retain(|port| *port != 0);
|
||||
nat_info.udp_ports.retain(|port| *port != 0);
|
||||
|
||||
nat_info.local_ipv4 = nat_info
|
||||
.local_ipv4
|
||||
.filter(|ip| device_info.not_in_network(*ip));
|
||||
if punch_tcp && self.punch_model.use_tcp() && nat_info.punch_model.use_tcp() {
|
||||
//向tcp发起连接
|
||||
if let Some(ipv6_addr) = nat_info.local_tcp_ipv6addr() {
|
||||
self.connect_tcp(buf, ipv6_addr)
|
||||
if self.punch_model.use_ipv6() && nat_info.punch_model.use_ipv6() {
|
||||
if let Some(ipv6_addr) = nat_info.local_tcp_ipv6addr() {
|
||||
self.connect_tcp(buf, ipv6_addr)
|
||||
}
|
||||
}
|
||||
//向tcp发起连接
|
||||
if let Some(ipv4_addr) = nat_info.local_tcp_ipv4addr() {
|
||||
self.connect_tcp(buf, ipv4_addr)
|
||||
}
|
||||
if nat_info.nat_type == NatType::Cone && nat_info.public_ips.len() == 1 {
|
||||
let addr =
|
||||
SocketAddr::V4(SocketAddrV4::new(nat_info.public_ips[0], nat_info.tcp_port));
|
||||
self.connect_tcp(buf, addr)
|
||||
if self.punch_model.use_ipv4() && nat_info.punch_model.use_ipv4() {
|
||||
if let Some(ipv4_addr) = nat_info.local_tcp_ipv4addr() {
|
||||
self.connect_tcp(buf, ipv4_addr)
|
||||
}
|
||||
for ip in &nat_info.public_ips {
|
||||
let addr = SocketAddr::V4(SocketAddrV4::new(*ip, nat_info.tcp_port));
|
||||
self.connect_tcp(buf, addr);
|
||||
}
|
||||
if nat_info.nat_type.is_cone() && nat_info.public_tcp_port != 0 {
|
||||
for ip in &nat_info.public_ips {
|
||||
let addr = SocketAddr::V4(SocketAddrV4::new(*ip, nat_info.public_tcp_port));
|
||||
self.connect_tcp(buf, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !self.punch_model.use_udp() || !nat_info.punch_model.use_udp() {
|
||||
return Ok(());
|
||||
}
|
||||
let channel_num = self.context.channel_num();
|
||||
let main_len = self.context.main_len();
|
||||
|
||||
if self.punch_model.use_ipv6() && nat_info.punch_model.use_ipv6() {
|
||||
for index in channel_num..main_len {
|
||||
if let Some(ipv6_addr) = nat_info.local_udp_ipv6addr(index) {
|
||||
if !self.nat_test.is_local_address(false, ipv6_addr) {
|
||||
let rs = self.context.send_main_udp(index, buf, ipv6_addr);
|
||||
log::info!("发送到ipv6地址:{:?},rs={:?} {}", ipv6_addr, rs, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !self.punch_model.use_ipv4() || !nat_info.punch_model.use_ipv4() {
|
||||
return Ok(());
|
||||
}
|
||||
for index in 0..channel_num {
|
||||
if let Some(ipv4_addr) = nat_info.local_udp_ipv4addr(index) {
|
||||
if !self.nat_test.is_local_address(false, ipv4_addr) {
|
||||
@@ -278,20 +389,23 @@ impl Punch {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.punch_model != PunchModel::IPv4 {
|
||||
for index in 0..channel_num {
|
||||
if let Some(ipv6_addr) = nat_info.local_udp_ipv6addr(index) {
|
||||
if !self.nat_test.is_local_address(false, ipv6_addr) {
|
||||
let rs = self.context.send_main_udp(index, buf, ipv6_addr);
|
||||
log::info!("发送到ipv6地址:{:?},rs={:?}", ipv6_addr, rs);
|
||||
if rs.is_ok() && self.punch_model == PunchModel::IPv6 {
|
||||
return Ok(());
|
||||
}
|
||||
// 可能是开放了端口的,需要打洞
|
||||
for index in 0..channel_num {
|
||||
for port in &nat_info.udp_ports {
|
||||
if *port == 0 {
|
||||
continue;
|
||||
}
|
||||
for ip in &nat_info.public_ips {
|
||||
if ip.is_unspecified() {
|
||||
continue;
|
||||
}
|
||||
let addr = SocketAddrV4::new(*ip, *port);
|
||||
let _ = self.context.send_main_udp(index, buf, addr.into());
|
||||
thread::sleep(Duration::from_millis(3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match nat_info.nat_type {
|
||||
NatType::Symmetric => {
|
||||
// 假设对方绑定n个端口,通过NAT对外映射出n个 公网ip:公网端口,自己随机尝试k次的情况下
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
use std::sync::mpsc::{SyncSender, TrySendError};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use tokio::sync::mpsc::Sender;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
@@ -11,7 +13,7 @@ use crate::channel::notify::AcceptNotify;
|
||||
use crate::cipher::Cipher;
|
||||
use crate::compression::Compressor;
|
||||
use crate::external_route::ExternalRoute;
|
||||
use crate::handle::CurrentDeviceInfo;
|
||||
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
|
||||
use crate::protocol;
|
||||
use crate::protocol::{ip_turn_packet, NetPacket};
|
||||
|
||||
@@ -21,7 +23,10 @@ pub struct IpPacketSender {
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
compressor: Compressor,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
ip_route: ExternalRoute,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
allow_wire_guard: bool,
|
||||
}
|
||||
|
||||
impl IpPacketSender {
|
||||
@@ -30,14 +35,20 @@ impl IpPacketSender {
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
compressor: Compressor,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
ip_route: ExternalRoute,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
allow_wire_guard: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
context,
|
||||
current_device,
|
||||
compressor,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
ip_route,
|
||||
device_map,
|
||||
allow_wire_guard,
|
||||
}
|
||||
}
|
||||
pub fn self_virtual_ip(&self) -> Ipv4Addr {
|
||||
@@ -58,19 +69,54 @@ impl IpPacketSender {
|
||||
if let Some(v) = self.ip_route.route(&dest_ip) {
|
||||
dest_ip = v;
|
||||
}
|
||||
if dest_ip.is_multicast() || dest_ip.is_broadcast() || dest_ip == device_info.broadcast_ip {
|
||||
if dest_ip.is_multicast() {
|
||||
//广播
|
||||
dest_ip = Ipv4Addr::BROADCAST;
|
||||
}
|
||||
|
||||
let mut net_packet = NetPacket::new0(data_len, buf)?;
|
||||
let mut auxiliary = NetPacket::new(auxiliary_buf)?;
|
||||
net_packet.set_default_version();
|
||||
net_packet.set_protocol(protocol::Protocol::IpTurn);
|
||||
net_packet.set_transport_protocol(ip_turn_packet::Protocol::Ipv4.into());
|
||||
net_packet.first_set_ttl(6);
|
||||
net_packet.set_source(src_ip);
|
||||
net_packet.set_destination(dest_ip);
|
||||
if self.allow_wire_guard {
|
||||
if dest_ip.is_broadcast() || dest_ip == device_info.broadcast_ip {
|
||||
let exists_wg = self
|
||||
.device_map
|
||||
.lock()
|
||||
.1
|
||||
.values()
|
||||
.any(|v| v.status.is_online() && v.wireguard);
|
||||
if exists_wg {
|
||||
send_to_wg_broadcast(
|
||||
&self.context,
|
||||
&net_packet,
|
||||
&self.server_cipher,
|
||||
&device_info,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
let guard = self.device_map.lock();
|
||||
if let Some(peer_info) = guard.1.get(&dest_ip) {
|
||||
if peer_info.wireguard {
|
||||
if peer_info.status.is_offline() {
|
||||
return Ok(());
|
||||
}
|
||||
drop(guard);
|
||||
send_to_wg(
|
||||
&self.context,
|
||||
&mut net_packet,
|
||||
&self.server_cipher,
|
||||
&device_info,
|
||||
)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut auxiliary = NetPacket::new(auxiliary_buf)?;
|
||||
|
||||
let mut net_packet = if self.compressor.compress(&net_packet, &mut auxiliary)? {
|
||||
auxiliary.set_default_version();
|
||||
@@ -84,7 +130,7 @@ impl IpPacketSender {
|
||||
net_packet
|
||||
};
|
||||
self.client_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
if dest_ip.is_broadcast() {
|
||||
if dest_ip.is_broadcast() || dest_ip == device_info.broadcast_ip {
|
||||
//走服务端广播
|
||||
self.context
|
||||
.send_default(&net_packet, device_info.connect_server)?;
|
||||
@@ -105,6 +151,40 @@ impl IpPacketSender {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_to_wg_broadcast(
|
||||
sender: &ChannelContext,
|
||||
net_packet: &NetPacket<&mut [u8]>,
|
||||
server_cipher: &Cipher,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut copy_packet = NetPacket::new0(net_packet.data_len(), [0; 65536])?;
|
||||
copy_packet.set_default_version();
|
||||
copy_packet.set_protocol(protocol::Protocol::IpTurn);
|
||||
copy_packet.set_transport_protocol(ip_turn_packet::Protocol::WGIpv4.into());
|
||||
copy_packet.first_set_ttl(6);
|
||||
copy_packet.set_source(net_packet.source());
|
||||
copy_packet.set_destination(net_packet.destination());
|
||||
copy_packet.set_gateway_flag(true);
|
||||
copy_packet.set_payload(net_packet.payload())?;
|
||||
server_cipher.encrypt_ipv4(&mut copy_packet)?;
|
||||
sender.send_default(©_packet, current_device.connect_server)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub fn send_to_wg(
|
||||
sender: &ChannelContext,
|
||||
net_packet: &mut NetPacket<&mut [u8]>,
|
||||
server_cipher: &Cipher,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
) -> anyhow::Result<()> {
|
||||
net_packet.set_transport_protocol(ip_turn_packet::Protocol::WGIpv4.into());
|
||||
net_packet.set_gateway_flag(true);
|
||||
server_cipher.encrypt_ipv4(net_packet)?;
|
||||
sender.send_default(&net_packet, current_device.connect_server)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct AcceptSocketSender<T> {
|
||||
sender: SyncSender<T>,
|
||||
notify: AcceptNotify,
|
||||
@@ -159,13 +239,13 @@ impl PacketSender {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConnectUtil {
|
||||
connect_tcp: Sender<(Vec<u8>, SocketAddr)>,
|
||||
connect_tcp: Sender<(Vec<u8>, Option<u16>, SocketAddr)>,
|
||||
connect_ws: Sender<(Vec<u8>, String)>,
|
||||
}
|
||||
|
||||
impl ConnectUtil {
|
||||
pub fn new(
|
||||
connect_tcp: Sender<(Vec<u8>, SocketAddr)>,
|
||||
connect_tcp: Sender<(Vec<u8>, Option<u16>, SocketAddr)>,
|
||||
connect_ws: Sender<(Vec<u8>, String)>,
|
||||
) -> Self {
|
||||
Self {
|
||||
@@ -174,7 +254,13 @@ impl ConnectUtil {
|
||||
}
|
||||
}
|
||||
pub fn try_connect_tcp(&self, buf: Vec<u8>, addr: SocketAddr) {
|
||||
if self.connect_tcp.try_send((buf, addr)).is_err() {
|
||||
if self.connect_tcp.try_send((buf, None, addr)).is_err() {
|
||||
log::warn!("try_connect_tcp failed {}", addr);
|
||||
}
|
||||
}
|
||||
pub fn try_connect_tcp_punch(&self, buf: Vec<u8>, addr: SocketAddr) {
|
||||
// 打洞的连接可以绑定随机端口
|
||||
if self.connect_tcp.try_send((buf, Some(0), addr)).is_err() {
|
||||
log::warn!("try_connect_tcp failed {}", addr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use network_interface::{NetworkInterface, NetworkInterfaceConfig};
|
||||
use socket2::Protocol;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
#[cfg(unix)]
|
||||
pub use unix::*;
|
||||
#[cfg(windows)]
|
||||
pub use windows::*;
|
||||
|
||||
#[cfg(unix)]
|
||||
mod unix;
|
||||
#[cfg(windows)]
|
||||
mod windows;
|
||||
|
||||
pub trait VntSocketTrait {
|
||||
fn set_ip_unicast_if(&self, _interface: &LocalInterface) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct LocalInterface {
|
||||
index: u32,
|
||||
#[cfg(unix)]
|
||||
name: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn connect_tcp(
|
||||
addr: SocketAddr,
|
||||
bind_port: u16,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<tokio::net::TcpStream> {
|
||||
let socket = create_tcp0(addr.is_ipv4(), bind_port, default_interface)?;
|
||||
Ok(socket.connect(addr).await?)
|
||||
}
|
||||
pub fn create_tcp(
|
||||
v4: bool,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<tokio::net::TcpSocket> {
|
||||
create_tcp0(v4, 0, default_interface)
|
||||
}
|
||||
pub fn create_tcp0(
|
||||
v4: bool,
|
||||
bind_port: u16,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<tokio::net::TcpSocket> {
|
||||
let socket = if v4 {
|
||||
socket2::Socket::new(
|
||||
socket2::Domain::IPV4,
|
||||
socket2::Type::STREAM,
|
||||
Some(Protocol::TCP),
|
||||
)?
|
||||
} else {
|
||||
socket2::Socket::new(
|
||||
socket2::Domain::IPV6,
|
||||
socket2::Type::STREAM,
|
||||
Some(Protocol::TCP),
|
||||
)?
|
||||
};
|
||||
if v4 {
|
||||
if let Err(e) = socket.set_ip_unicast_if(default_interface) {
|
||||
log::warn!("set_ip_unicast_if {:?}", e)
|
||||
}
|
||||
}
|
||||
if bind_port != 0 {
|
||||
socket
|
||||
.set_reuse_address(true)
|
||||
.context("set_reuse_address")?;
|
||||
#[cfg(unix)]
|
||||
if let Err(e) = socket.set_reuse_port(true) {
|
||||
log::warn!("set_reuse_port {:?}", e)
|
||||
}
|
||||
if v4 {
|
||||
let addr: SocketAddr = format!("0.0.0.0:{}", bind_port).parse().unwrap();
|
||||
socket.bind(&addr.into())?;
|
||||
} else {
|
||||
socket.set_only_v6(true)?;
|
||||
let addr: SocketAddr = format!("[::]:{}", bind_port).parse().unwrap();
|
||||
socket.bind(&addr.into())?;
|
||||
}
|
||||
}
|
||||
socket.set_nonblocking(true)?;
|
||||
socket.set_nodelay(true)?;
|
||||
Ok(tokio::net::TcpSocket::from_std_stream(socket.into()))
|
||||
}
|
||||
pub fn bind_udp_ops(
|
||||
addr: SocketAddr,
|
||||
only_v6: bool,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<socket2::Socket> {
|
||||
let socket = if addr.is_ipv4() {
|
||||
let socket = socket2::Socket::new(
|
||||
socket2::Domain::IPV4,
|
||||
socket2::Type::DGRAM,
|
||||
Some(Protocol::UDP),
|
||||
)?;
|
||||
if let Err(e) = socket.set_ip_unicast_if(default_interface) {
|
||||
log::warn!("set_ip_unicast_if {:?}", e)
|
||||
}
|
||||
socket
|
||||
} else {
|
||||
let socket = socket2::Socket::new(
|
||||
socket2::Domain::IPV6,
|
||||
socket2::Type::DGRAM,
|
||||
Some(Protocol::UDP),
|
||||
)?;
|
||||
socket
|
||||
.set_only_v6(only_v6)
|
||||
.with_context(|| format!("set_only_v6 failed: {}", &addr))?;
|
||||
socket
|
||||
};
|
||||
socket.set_nonblocking(true)?;
|
||||
socket.bind(&addr.into())?;
|
||||
Ok(socket)
|
||||
}
|
||||
pub fn bind_udp(
|
||||
addr: SocketAddr,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<socket2::Socket> {
|
||||
bind_udp_ops(addr, true, default_interface).with_context(|| format!("{}", addr))
|
||||
}
|
||||
|
||||
pub fn get_interface(dest_ip: Ipv4Addr) -> anyhow::Result<LocalInterface> {
|
||||
let network_interfaces = NetworkInterface::show()?;
|
||||
for iface in network_interfaces {
|
||||
for addr in iface.addr {
|
||||
if let IpAddr::V4(ip) = addr.ip() {
|
||||
if ip == dest_ip {
|
||||
return Ok(LocalInterface {
|
||||
index: iface.index,
|
||||
#[cfg(unix)]
|
||||
name: Some(iface.name),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(anyhow!("No network card with IP {} found", dest_ip))
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
use crate::channel::socket::get_interface;
|
||||
use crate::channel::socket::{LocalInterface, VntSocketTrait};
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
use anyhow::Context;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
impl VntSocketTrait for socket2::Socket {
|
||||
fn set_ip_unicast_if(&self, interface: &LocalInterface) -> anyhow::Result<()> {
|
||||
if let Some(name) = &interface.name {
|
||||
self.bind_device(Some(name.as_bytes()))
|
||||
.context("bind_device")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
impl VntSocketTrait for socket2::Socket {
|
||||
fn set_ip_unicast_if(&self, interface: &LocalInterface) -> anyhow::Result<()> {
|
||||
if interface.index != 0 {
|
||||
self.bind_device_by_index_v4(std::num::NonZeroU32::new(interface.index))
|
||||
.with_context(|| format!("bind_device_by_index_v4 {:?}", interface))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "android")]
|
||||
impl VntSocketTrait for socket2::Socket {
|
||||
fn set_ip_unicast_if(&self, _interface: &LocalInterface) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
pub fn get_best_interface(dest_ip: Ipv4Addr) -> anyhow::Result<LocalInterface> {
|
||||
match get_interface(dest_ip) {
|
||||
Ok(iface) => return Ok(iface),
|
||||
Err(e) => {
|
||||
log::warn!("not find interface e={:?},ip={}", e, dest_ip);
|
||||
}
|
||||
}
|
||||
// 应该再查路由表找到默认路由的
|
||||
Ok(LocalInterface::default())
|
||||
}
|
||||
#[cfg(target_os = "android")]
|
||||
pub fn get_best_interface(_dest_ip: Ipv4Addr) -> anyhow::Result<LocalInterface> {
|
||||
Ok(LocalInterface::default())
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
use std::mem;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::windows::io::AsRawSocket;
|
||||
|
||||
use windows_sys::core::PCSTR;
|
||||
use windows_sys::Win32::NetworkManagement::IpHelper::GetBestInterfaceEx;
|
||||
use windows_sys::Win32::Networking::WinSock::{
|
||||
htonl, setsockopt, AF_INET, IPPROTO_IP, IP_UNICAST_IF, SOCKADDR, SOCKADDR_IN, SOCKET_ERROR,
|
||||
};
|
||||
|
||||
use crate::channel::socket::{LocalInterface, VntSocketTrait};
|
||||
|
||||
impl VntSocketTrait for socket2::Socket {
|
||||
fn set_ip_unicast_if(&self, interface: &LocalInterface) -> anyhow::Result<()> {
|
||||
let index = interface.index;
|
||||
if index == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let raw_socket = self.as_raw_socket();
|
||||
let result = unsafe {
|
||||
let best_interface = htonl(index);
|
||||
setsockopt(
|
||||
raw_socket as usize,
|
||||
IPPROTO_IP,
|
||||
IP_UNICAST_IF,
|
||||
&best_interface as *const _ as PCSTR,
|
||||
mem::size_of_val(&best_interface) as i32,
|
||||
)
|
||||
};
|
||||
if result == SOCKET_ERROR {
|
||||
Err(anyhow::anyhow!(
|
||||
"Failed to set IP_UNICAST_IF: {:?} {}",
|
||||
std::io::Error::last_os_error(),
|
||||
index
|
||||
))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_best_interface(dest_ip: Ipv4Addr) -> anyhow::Result<LocalInterface> {
|
||||
// 获取最佳接口
|
||||
let index = unsafe {
|
||||
let mut dest: SOCKADDR_IN = mem::zeroed();
|
||||
dest.sin_family = AF_INET as u16;
|
||||
dest.sin_addr.S_un.S_addr = u32::from_ne_bytes(dest_ip.octets());
|
||||
|
||||
let mut index: u32 = 0;
|
||||
if GetBestInterfaceEx(&dest as *const _ as *mut SOCKADDR, &mut index) != 0 {
|
||||
Err(anyhow::anyhow!(
|
||||
"Failed to GetBestInterfaceEx: {:?}",
|
||||
std::io::Error::last_os_error()
|
||||
))?;
|
||||
}
|
||||
index
|
||||
};
|
||||
Ok(LocalInterface { index })
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use std::net::SocketAddr;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::io::AsRawFd;
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::io::AsRawSocket;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||
@@ -10,13 +14,14 @@ use tokio::sync::mpsc::{channel, Receiver};
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::handler::RecvChannelHandler;
|
||||
use crate::channel::sender::PacketSender;
|
||||
use crate::channel::socket::create_tcp0;
|
||||
use crate::channel::{ConnectProtocol, RouteKey, BUFFER_SIZE, TCP_MAX_PACKET_SIZE};
|
||||
use crate::util::StopManager;
|
||||
|
||||
/// 监听tcp端口,等待客户端连接
|
||||
pub fn tcp_listen<H>(
|
||||
tcp_server: std::net::TcpListener,
|
||||
receiver: Receiver<(Vec<u8>, SocketAddr)>,
|
||||
receiver: Receiver<(Vec<u8>, Option<u16>, SocketAddr)>,
|
||||
recv_handler: H,
|
||||
context: ChannelContext,
|
||||
stop_manager: StopManager,
|
||||
@@ -28,6 +33,7 @@ where
|
||||
let worker = stop_manager.add_listener("tcpChannel".into(), move || {
|
||||
let _ = stop_sender.send(());
|
||||
})?;
|
||||
let bind_port = tcp_server.local_addr()?.port();
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(2)
|
||||
.enable_all()
|
||||
@@ -46,9 +52,9 @@ where
|
||||
}
|
||||
});
|
||||
}
|
||||
tokio::spawn(
|
||||
async move { connect_tcp_handle(receiver, recv_handler, context).await },
|
||||
);
|
||||
tokio::spawn(async move {
|
||||
connect_tcp_handle(receiver, recv_handler, context, bind_port).await
|
||||
});
|
||||
});
|
||||
runtime.block_on(async {
|
||||
let _ = stop_receiver.await;
|
||||
@@ -61,18 +67,24 @@ where
|
||||
}
|
||||
|
||||
async fn connect_tcp_handle<H>(
|
||||
mut receiver: Receiver<(Vec<u8>, SocketAddr)>,
|
||||
mut receiver: Receiver<(Vec<u8>, Option<u16>, SocketAddr)>,
|
||||
recv_handler: H,
|
||||
context: ChannelContext,
|
||||
listener_bind_port: u16,
|
||||
) where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
while let Some((data, addr)) = receiver.recv().await {
|
||||
while let Some((data, bind_port, addr)) = receiver.recv().await {
|
||||
let recv_handler = recv_handler.clone();
|
||||
let context = context.clone();
|
||||
let bind_port = if let Some(bind_port) = bind_port {
|
||||
bind_port
|
||||
} else {
|
||||
listener_bind_port
|
||||
};
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = connect_tcp0(data, addr, recv_handler, context).await {
|
||||
log::warn!("发送失败,链接终止:{:?},{:?}", addr, e);
|
||||
if let Err(e) = connect_tcp0(data, addr, recv_handler, context, bind_port).await {
|
||||
log::warn!("连接失败,链接终止:{:?},{:?}", addr, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -83,12 +95,23 @@ async fn connect_tcp0<H>(
|
||||
addr: SocketAddr,
|
||||
recv_handler: H,
|
||||
context: ChannelContext,
|
||||
bind_port: u16,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let mut stream =
|
||||
tokio::time::timeout(Duration::from_secs(3), TcpStream::connect(addr)).await??;
|
||||
let socket = if bind_port != 0 {
|
||||
match create_tcp0(addr.is_ipv4(), bind_port, context.default_interface()) {
|
||||
Ok(socket) => socket,
|
||||
Err(e) => {
|
||||
log::warn!("{:?}", e);
|
||||
create_tcp0(addr.is_ipv4(), 0, context.default_interface())?
|
||||
}
|
||||
}
|
||||
} else {
|
||||
create_tcp0(addr.is_ipv4(), 0, context.default_interface())?
|
||||
};
|
||||
let mut stream = tokio::time::timeout(Duration::from_secs(3), socket.connect(addr)).await??;
|
||||
tcp_write(&mut stream, &data).await?;
|
||||
|
||||
tcp_stream_handle(stream, addr, recv_handler, context).await;
|
||||
@@ -107,6 +130,7 @@ where
|
||||
|
||||
loop {
|
||||
let (stream, addr) = tcp_server.accept().await?;
|
||||
|
||||
tcp_stream_handle(stream, addr, recv_handler.clone(), context.clone()).await;
|
||||
}
|
||||
}
|
||||
@@ -120,12 +144,18 @@ pub async fn tcp_stream_handle<H>(
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let _ = stream.set_nodelay(true);
|
||||
let local = stream.local_addr();
|
||||
#[cfg(windows)]
|
||||
let index = stream.as_raw_socket() as usize;
|
||||
#[cfg(unix)]
|
||||
let index = stream.as_raw_fd() as usize;
|
||||
let route_key = RouteKey::new(ConnectProtocol::TCP, index, addr);
|
||||
let (r, mut w) = stream.into_split();
|
||||
let (sender, mut receiver) = channel::<Vec<u8>>(100);
|
||||
context
|
||||
.packet_map
|
||||
.write()
|
||||
.insert(addr, PacketSender::new(sender));
|
||||
.insert(route_key, PacketSender::new(sender));
|
||||
tokio::spawn(async move {
|
||||
while let Some(data) = receiver.recv().await {
|
||||
if let Err(e) = tcp_write(&mut w, &data).await {
|
||||
@@ -136,10 +166,10 @@ pub async fn tcp_stream_handle<H>(
|
||||
let _ = w.shutdown().await;
|
||||
});
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = tcp_read(r, addr, &context, recv_handler).await {
|
||||
log::warn!("tcp_read {:?}", e)
|
||||
if let Err(e) = tcp_read(r, addr, &context, recv_handler, route_key).await {
|
||||
log::warn!("tcp_read {:?} {local:?}-{addr}", e)
|
||||
}
|
||||
context.packet_map.write().remove(&addr);
|
||||
context.packet_map.write().remove(&route_key);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -159,6 +189,7 @@ async fn tcp_read<H>(
|
||||
addr: SocketAddr,
|
||||
context: &ChannelContext,
|
||||
recv_handler: H,
|
||||
route_key: RouteKey,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
@@ -176,11 +207,6 @@ where
|
||||
return Err(anyhow!("tcp数据长度无效 {}", addr));
|
||||
}
|
||||
read.read_exact(&mut buf[..len]).await?;
|
||||
recv_handler.handle(
|
||||
&mut buf[..len],
|
||||
&mut extend,
|
||||
RouteKey::new(ConnectProtocol::TCP, 0, addr),
|
||||
context,
|
||||
);
|
||||
recv_handler.handle(&mut buf[..len], &mut extend, route_key, context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::mpsc::{sync_channel, Receiver};
|
||||
use std::{io, thread};
|
||||
|
||||
@@ -71,7 +70,8 @@ where
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let mut buf = [0; BUFFER_SIZE];
|
||||
let mut extend = [0; BUFFER_SIZE];
|
||||
let mut read_map: HashMap<Token, UdpSocket> = HashMap::with_capacity(32);
|
||||
let mut list: Vec<UdpSocket> = Vec::with_capacity(100);
|
||||
let main_len = context.main_len();
|
||||
loop {
|
||||
if let Err(e) = poll.poll(&mut events, None) {
|
||||
crate::ignore_io_interrupted(e)?;
|
||||
@@ -88,39 +88,43 @@ where
|
||||
match option {
|
||||
None => {
|
||||
log::info!("切换成锥形模式");
|
||||
for (_, mut udp_socket) in read_map.drain() {
|
||||
for mut udp_socket in list.drain(..) {
|
||||
if let Err(e) = udp_socket.deregister(poll.registry()) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(socket_list) => {
|
||||
for mut udp_socket in list.drain(..) {
|
||||
if let Err(e) = udp_socket.deregister(poll.registry()) {
|
||||
log::error!("deregister {:?}", e);
|
||||
}
|
||||
}
|
||||
log::info!("切换成对称模式 监听端口数:{}", socket_list.len());
|
||||
for (index, mut udp_socket) in
|
||||
socket_list.into_iter().enumerate()
|
||||
{
|
||||
let token = Token(index + context.channel_num());
|
||||
poll.registry().register(
|
||||
&mut udp_socket,
|
||||
token,
|
||||
Token(index),
|
||||
Interest::READABLE,
|
||||
)?;
|
||||
read_map.insert(token, udp_socket);
|
||||
list.push(udp_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
token => {
|
||||
if let Some(udp_socket) = read_map.get(&token) {
|
||||
Token(index) => {
|
||||
if let Some(udp_socket) = list.get(index) {
|
||||
loop {
|
||||
match udp_socket.recv_from(&mut buf) {
|
||||
Ok((len, addr)) => {
|
||||
recv_handler.handle(
|
||||
&mut buf[..len],
|
||||
&mut extend,
|
||||
RouteKey::new(ConnectProtocol::UDP, token.0, addr),
|
||||
RouteKey::new(ConnectProtocol::UDP, index + main_len, addr),
|
||||
&context,
|
||||
);
|
||||
}
|
||||
@@ -266,6 +270,7 @@ where
|
||||
for x in events.iter() {
|
||||
let index = match x.token() {
|
||||
NOTIFY => return Ok(()),
|
||||
// 0的位置留给NOTIFY了,这里要再减回去,因为路由是通过index来找到对应udp的
|
||||
Token(index) => index - 1,
|
||||
};
|
||||
let udp = if let Some(udp) = udps.get(index) {
|
||||
|
||||
@@ -58,14 +58,16 @@ async fn connect_ws_handle<H>(
|
||||
) where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let mut index = 0;
|
||||
while let Some((data, url)) = receiver.recv().await {
|
||||
let recv_handler = recv_handler.clone();
|
||||
let context = context.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = connect_ws(data, url, recv_handler, context).await {
|
||||
if let Err(e) = connect_ws(data, url, recv_handler, context, index).await {
|
||||
log::warn!("发送失败,ws链接终止:{:?}", e);
|
||||
}
|
||||
});
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
const WS_ADDR: SocketAddr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0));
|
||||
@@ -75,6 +77,7 @@ async fn connect_ws<H>(
|
||||
mut url: String,
|
||||
recv_handler: H,
|
||||
context: ChannelContext,
|
||||
index: usize,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
@@ -114,10 +117,12 @@ where
|
||||
ws.send(Message::Binary(data)).await?;
|
||||
let (mut ws_write, ws_read) = ws.split();
|
||||
let (sender, mut receiver) = channel::<Vec<u8>>(100);
|
||||
let route_key = RouteKey::new(ConnectProtocol::WS, index, WS_ADDR);
|
||||
|
||||
context
|
||||
.packet_map
|
||||
.write()
|
||||
.insert(WS_ADDR, PacketSender::new(sender));
|
||||
.insert(route_key, PacketSender::new(sender));
|
||||
tokio::spawn(async move {
|
||||
while let Some(data) = receiver.recv().await {
|
||||
if let Err(e) = ws_write.send(Message::Binary(data)).await {
|
||||
@@ -127,22 +132,22 @@ where
|
||||
}
|
||||
let _ = ws_write.close().await;
|
||||
});
|
||||
if let Err(e) = ws_read_handle(ws_read, recv_handler, &context).await {
|
||||
if let Err(e) = ws_read_handle(ws_read, recv_handler, &context, route_key).await {
|
||||
log::warn!("{:?}", e);
|
||||
}
|
||||
context.packet_map.write().remove(&WS_ADDR);
|
||||
context.packet_map.write().remove(&route_key);
|
||||
Ok(())
|
||||
}
|
||||
async fn ws_read_handle<H>(
|
||||
mut ws_read: SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>,
|
||||
recv_handler: H,
|
||||
context: &ChannelContext,
|
||||
route_key: RouteKey,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let mut extend = [0; BUFFER_SIZE];
|
||||
let route_key = RouteKey::new(ConnectProtocol::WS, 0, WS_ADDR);
|
||||
while let Some(msg) = ws_read.next().await {
|
||||
let msg = msg.context("Error during WebSocket ")?;
|
||||
match msg {
|
||||
|
||||
+33
-18
@@ -66,12 +66,13 @@ pub struct VntInner {
|
||||
config: Config,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
nat_test: NatTest,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
context: Arc<Mutex<Option<ChannelContext>>>,
|
||||
peer_nat_info_map: Arc<RwLock<HashMap<Ipv4Addr, NatInfo>>>,
|
||||
client_secret_hash: Option<[u8; 16]>,
|
||||
compressor: Compressor,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
external_route: ExternalRoute,
|
||||
up_traffic_meter: Option<TrafficMeterMultiAddress>,
|
||||
down_traffic_meter: Option<TrafficMeterMultiAddress>,
|
||||
@@ -104,6 +105,7 @@ impl VntInner {
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
//服务端非对称加密
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
let rsa_cipher: Arc<Mutex<Option<RsaCipher>>> = Arc::new(Mutex::new(None));
|
||||
@@ -128,8 +130,15 @@ impl VntInner {
|
||||
config.server_address,
|
||||
)));
|
||||
//设备列表
|
||||
let device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>> =
|
||||
Arc::new(Mutex::new((0, Vec::with_capacity(16))));
|
||||
let device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>> =
|
||||
Arc::new(Mutex::new((0, HashMap::with_capacity(16))));
|
||||
let local_ipv4 = if let Some(local_ipv4) = config.local_ipv4 {
|
||||
Some(local_ipv4)
|
||||
} else {
|
||||
nat::local_ipv4()
|
||||
};
|
||||
let default_interface = config.local_interface.clone();
|
||||
|
||||
//基础信息
|
||||
let config_info = BaseConfigInfo::new(
|
||||
config.name.clone(),
|
||||
@@ -147,6 +156,8 @@ impl VntInner {
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
config.device_name.clone(),
|
||||
config.allow_wire_guard,
|
||||
default_interface.clone(),
|
||||
);
|
||||
// 服务停止管理器
|
||||
let stop_manager = {
|
||||
@@ -177,10 +188,10 @@ impl VntInner {
|
||||
config.protocol,
|
||||
config.packet_loss_rate,
|
||||
config.packet_delay,
|
||||
default_interface,
|
||||
up_traffic_meter.clone(),
|
||||
down_traffic_meter.clone(),
|
||||
)?;
|
||||
let local_ipv4 = nat::local_ipv4();
|
||||
let local_ipv6 = nat::local_ipv6();
|
||||
let udp_ports = context.main_local_udp_port()?;
|
||||
let tcp_port = tcp_listener.local_addr()?.port();
|
||||
@@ -192,6 +203,8 @@ impl VntInner {
|
||||
local_ipv6,
|
||||
udp_ports,
|
||||
tcp_port,
|
||||
config.local_ipv4.is_none(),
|
||||
config.punch_model,
|
||||
);
|
||||
// 定时器
|
||||
let scheduler = Scheduler::new(stop_manager.clone())?;
|
||||
@@ -228,7 +241,7 @@ impl VntInner {
|
||||
proxy_map.clone(),
|
||||
client_cipher.clone(),
|
||||
server_cipher.clone(),
|
||||
device_list.clone(),
|
||||
device_map.clone(),
|
||||
config.compressor,
|
||||
device.clone().into_device_adapter(),
|
||||
)
|
||||
@@ -241,7 +254,7 @@ impl VntInner {
|
||||
client_cipher.clone(),
|
||||
current_device.clone(),
|
||||
device,
|
||||
device_list.clone(),
|
||||
device_map.clone(),
|
||||
config_info.clone(),
|
||||
nat_test.clone(),
|
||||
callback.clone(),
|
||||
@@ -264,9 +277,7 @@ impl VntInner {
|
||||
let punch = Punch::new(
|
||||
context.clone(),
|
||||
config.punch_model,
|
||||
config.protocol.is_base_tcp(),
|
||||
connect_util.clone(),
|
||||
external_route.clone(),
|
||||
nat_test.clone(),
|
||||
current_device.clone(),
|
||||
);
|
||||
@@ -287,7 +298,7 @@ impl VntInner {
|
||||
{
|
||||
let context = context.clone();
|
||||
let nat_test = nat_test.clone();
|
||||
let device_list = device_list.clone();
|
||||
let device_map = device_map.clone();
|
||||
let config_info = config_info.clone();
|
||||
let current_device = current_device.clone();
|
||||
if !config.use_channel_type.is_only_relay() {
|
||||
@@ -300,13 +311,14 @@ impl VntInner {
|
||||
);
|
||||
}
|
||||
let client_cipher = client_cipher.clone();
|
||||
let server_cipher = server_cipher.clone();
|
||||
//延迟启动
|
||||
scheduler.timeout(Duration::from_secs(3), move |scheduler| {
|
||||
start(
|
||||
scheduler,
|
||||
context,
|
||||
nat_test,
|
||||
device_list,
|
||||
device_map,
|
||||
current_device,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
@@ -323,12 +335,13 @@ impl VntInner {
|
||||
config,
|
||||
current_device,
|
||||
nat_test,
|
||||
device_list,
|
||||
device_map,
|
||||
context: Arc::new(Mutex::new(Some(context))),
|
||||
peer_nat_info_map,
|
||||
client_secret_hash: config_info.client_secret_hash,
|
||||
compressor,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
external_route,
|
||||
up_traffic_meter,
|
||||
down_traffic_meter,
|
||||
@@ -340,7 +353,7 @@ pub fn start<Call: VntCallback>(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
nat_test: NatTest,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
@@ -354,7 +367,7 @@ pub fn start<Call: VntCallback>(
|
||||
&scheduler,
|
||||
context.clone(),
|
||||
current_device.clone(),
|
||||
device_list.clone(),
|
||||
device_map.clone(),
|
||||
client_cipher.clone(),
|
||||
server_cipher.clone(),
|
||||
);
|
||||
@@ -374,7 +387,7 @@ pub fn start<Call: VntCallback>(
|
||||
&scheduler,
|
||||
context.clone(),
|
||||
current_device.clone(),
|
||||
device_list.clone(),
|
||||
device_map.clone(),
|
||||
client_cipher.clone(),
|
||||
);
|
||||
}
|
||||
@@ -385,7 +398,6 @@ pub fn start<Call: VntCallback>(
|
||||
&scheduler,
|
||||
context.clone(),
|
||||
current_device.clone(),
|
||||
server_cipher.clone(),
|
||||
nat_test.clone(),
|
||||
config_info.clone(),
|
||||
);
|
||||
@@ -394,7 +406,7 @@ pub fn start<Call: VntCallback>(
|
||||
&scheduler,
|
||||
context.clone(),
|
||||
nat_test.clone(),
|
||||
device_list.clone(),
|
||||
device_map.clone(),
|
||||
current_device.clone(),
|
||||
client_cipher.clone(),
|
||||
punch_receiver,
|
||||
@@ -433,10 +445,10 @@ impl VntInner {
|
||||
self.nat_test.nat_info()
|
||||
}
|
||||
pub fn device_list(&self) -> Vec<PeerDeviceInfo> {
|
||||
let device_list_lock = self.device_list.lock();
|
||||
let device_list_lock = self.device_map.lock();
|
||||
let (_epoch, device_list) = device_list_lock.clone();
|
||||
drop(device_list_lock);
|
||||
device_list
|
||||
device_list.into_values().collect()
|
||||
}
|
||||
pub fn route(&self, ip: &Ipv4Addr) -> Option<Route> {
|
||||
self.context.lock().as_ref()?.route_table.route_one(ip)
|
||||
@@ -508,7 +520,10 @@ impl VntInner {
|
||||
self.current_device.clone(),
|
||||
self.compressor.clone(),
|
||||
self.client_cipher.clone(),
|
||||
self.server_cipher.clone(),
|
||||
self.external_route.clone(),
|
||||
self.device_map.clone(),
|
||||
self.config.allow_wire_guard,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
|
||||
+31
-2
@@ -5,6 +5,7 @@ use std::str::FromStr;
|
||||
pub use conn::Vnt;
|
||||
|
||||
use crate::channel::punch::PunchModel;
|
||||
use crate::channel::socket::LocalInterface;
|
||||
use crate::channel::{ConnectProtocol, UseChannelType};
|
||||
use crate::cipher::CipherModel;
|
||||
use crate::compression::Compressor;
|
||||
@@ -51,6 +52,9 @@ pub struct Config {
|
||||
pub port_mapping_list: Vec<(bool, SocketAddr, String)>,
|
||||
pub compressor: Compressor,
|
||||
pub enable_traffic: bool,
|
||||
pub allow_wire_guard: bool,
|
||||
pub local_ipv4: Option<Ipv4Addr>,
|
||||
pub local_interface: LocalInterface,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -88,7 +92,19 @@ impl Config {
|
||||
#[cfg(feature = "port_mapping")] port_mapping_list: Vec<String>,
|
||||
compressor: Compressor,
|
||||
enable_traffic: bool,
|
||||
// 允许传递wg流量
|
||||
allow_wire_guard: bool,
|
||||
local_ipv4: Option<Ipv4Addr>,
|
||||
) -> anyhow::Result<Self> {
|
||||
#[cfg(windows)]
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
if !tap {
|
||||
if let Err(e) = tun::Device::check_tun_dll() {
|
||||
log::warn!("校验平台dll {:?}", e);
|
||||
Err(e)?;
|
||||
}
|
||||
}
|
||||
|
||||
for x in stun_server.iter_mut() {
|
||||
if !x.contains(":") {
|
||||
x.push_str(":3478");
|
||||
@@ -134,8 +150,11 @@ impl Config {
|
||||
server_address_str = s.to_string();
|
||||
protocol = ConnectProtocol::TCP;
|
||||
}
|
||||
server_address =
|
||||
address_choose(dns_query_all(&server_address_str, name_servers.clone())?)?;
|
||||
server_address = address_choose(dns_query_all(
|
||||
&server_address_str,
|
||||
name_servers.clone(),
|
||||
&LocalInterface::default(),
|
||||
)?)?;
|
||||
}
|
||||
#[cfg(feature = "port_mapping")]
|
||||
let port_mapping_list = crate::port_mapping::convert(port_mapping_list)?;
|
||||
@@ -144,6 +163,13 @@ impl Config {
|
||||
*dest = *mask & *dest;
|
||||
}
|
||||
in_ips.sort_by(|(dest1, _, _), (dest2, _, _)| dest2.cmp(dest1));
|
||||
let local_interface = if let Some(local_ip) = local_ipv4 {
|
||||
let default_interface = crate::channel::socket::get_interface(local_ip)?;
|
||||
log::info!("default_interface = {:?}", default_interface);
|
||||
default_interface
|
||||
} else {
|
||||
LocalInterface::default()
|
||||
};
|
||||
Ok(Self {
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(target_os = "windows")]
|
||||
@@ -180,6 +206,9 @@ impl Config {
|
||||
port_mapping_list,
|
||||
compressor,
|
||||
enable_traffic,
|
||||
allow_wire_guard,
|
||||
local_ipv4,
|
||||
local_interface,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,6 +172,8 @@ pub enum ErrorType {
|
||||
IpAlreadyExists,
|
||||
InvalidIp,
|
||||
LocalIpExists,
|
||||
FailedToCrateDevice,
|
||||
Warn,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
@@ -184,6 +186,8 @@ impl Into<u8> for ErrorType {
|
||||
ErrorType::IpAlreadyExists => 4,
|
||||
ErrorType::InvalidIp => 5,
|
||||
ErrorType::LocalIpExists => 6,
|
||||
ErrorType::FailedToCrateDevice => 101,
|
||||
ErrorType::Warn => 102,
|
||||
ErrorType::Unknown => 255,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,48 +5,30 @@ use crossbeam_utils::atomic::AtomicCell;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::punch::NatType;
|
||||
use crate::cipher::Cipher;
|
||||
use crate::handle::{BaseConfigInfo, CurrentDeviceInfo};
|
||||
use crate::nat::NatTest;
|
||||
use crate::protocol::body::ENCRYPTION_RESERVED;
|
||||
use crate::protocol::{control_packet, NetPacket, Protocol, MAX_TTL};
|
||||
use crate::util::Scheduler;
|
||||
|
||||
pub fn addr_request(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
current_device_info: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
server_cipher: Cipher,
|
||||
nat_test: NatTest,
|
||||
_config: BaseConfigInfo,
|
||||
) {
|
||||
pub_address_request(
|
||||
scheduler,
|
||||
context,
|
||||
current_device_info.clone(),
|
||||
server_cipher,
|
||||
nat_test,
|
||||
0,
|
||||
);
|
||||
pub_address_request(scheduler, context, current_device_info.clone(), nat_test, 0);
|
||||
}
|
||||
|
||||
fn pub_address_request(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
current_device_info: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
server_cipher: Cipher,
|
||||
nat_test: NatTest,
|
||||
count: usize,
|
||||
) {
|
||||
let channel_num = context.channel_num();
|
||||
let index = count % channel_num;
|
||||
if let Err(e) = addr_request0(
|
||||
&context,
|
||||
¤t_device_info,
|
||||
&server_cipher,
|
||||
&nat_test,
|
||||
index,
|
||||
) {
|
||||
if let Err(e) = addr_request0(&context, ¤t_device_info, &nat_test, index) {
|
||||
log::warn!("{:?}", e);
|
||||
}
|
||||
let nat_info = nat_test.nat_info();
|
||||
@@ -58,7 +40,7 @@ fn pub_address_request(
|
||||
if index == channel_num - 1 {
|
||||
19
|
||||
} else {
|
||||
7
|
||||
9
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -66,14 +48,7 @@ fn pub_address_request(
|
||||
};
|
||||
|
||||
let rs = scheduler.timeout(Duration::from_secs(time), move |s| {
|
||||
pub_address_request(
|
||||
s,
|
||||
context,
|
||||
current_device_info,
|
||||
server_cipher,
|
||||
nat_test,
|
||||
index + 1,
|
||||
)
|
||||
pub_address_request(s, context, current_device_info, nat_test, index + 1)
|
||||
});
|
||||
if !rs {
|
||||
log::info!("定时任务停止");
|
||||
@@ -83,7 +58,6 @@ fn pub_address_request(
|
||||
fn addr_request0(
|
||||
context: &ChannelContext,
|
||||
current_device: &AtomicCell<CurrentDeviceInfo>,
|
||||
server_cipher: &Cipher,
|
||||
nat_test: &NatTest,
|
||||
index: usize,
|
||||
) -> anyhow::Result<()> {
|
||||
@@ -91,24 +65,7 @@ fn addr_request0(
|
||||
if current_dev.status.offline() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if current_dev.connect_server.is_ipv4() && !context.main_protocol().is_base_tcp() {
|
||||
// 如果连接的是ipv4服务,则探测公网端口
|
||||
let gateway_ip = current_dev.virtual_gateway;
|
||||
let src_ip = current_dev.virtual_ip;
|
||||
let mut packet = NetPacket::new_encrypt([0; 12 + ENCRYPTION_RESERVED]).unwrap();
|
||||
packet.set_default_version();
|
||||
packet.set_gateway_flag(true);
|
||||
packet.set_protocol(Protocol::Control);
|
||||
packet.set_transport_protocol(control_packet::Protocol::AddrRequest.into());
|
||||
packet.first_set_ttl(MAX_TTL);
|
||||
packet.set_source(src_ip);
|
||||
packet.set_destination(gateway_ip);
|
||||
server_cipher.encrypt_ipv4(&mut packet)?;
|
||||
context.send_main_udp(index, packet.buffer(), current_dev.connect_server)?;
|
||||
} else {
|
||||
let (data, addr) = nat_test.send_data()?;
|
||||
context.send_main_udp(index, &data, addr)?;
|
||||
}
|
||||
let (data, addr) = nat_test.send_data()?;
|
||||
context.send_main_udp(index, &data, addr)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@@ -19,14 +20,14 @@ pub fn heartbeat(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
current_device_info: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
) {
|
||||
heartbeat0(
|
||||
&context,
|
||||
¤t_device_info.load(),
|
||||
&device_list,
|
||||
&device_map,
|
||||
&client_cipher,
|
||||
&server_cipher,
|
||||
);
|
||||
@@ -36,7 +37,7 @@ pub fn heartbeat(
|
||||
s,
|
||||
context,
|
||||
current_device_info,
|
||||
device_list,
|
||||
device_map,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
)
|
||||
@@ -49,15 +50,16 @@ pub fn heartbeat(
|
||||
fn heartbeat0(
|
||||
context: &ChannelContext,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
device_list: &Mutex<(u16, Vec<PeerDeviceInfo>)>,
|
||||
device_map: &Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>,
|
||||
client_cipher: &Cipher,
|
||||
server_cipher: &Cipher,
|
||||
) {
|
||||
let gateway_ip = current_device.virtual_gateway;
|
||||
let src_ip = current_device.virtual_ip;
|
||||
let channel_num = context.channel_num();
|
||||
// 可能服务器ip发生变化,导致发送失败
|
||||
let mut is_send_gateway = false;
|
||||
match heartbeat_packet_server(device_list, server_cipher, src_ip, gateway_ip) {
|
||||
match heartbeat_packet_server(device_map, server_cipher, src_ip, gateway_ip) {
|
||||
Ok(net_packet) => {
|
||||
if let Err(e) = context.send_default(&net_packet, current_device.connect_server) {
|
||||
log::warn!("heartbeat err={:?}", e)
|
||||
@@ -75,7 +77,7 @@ fn heartbeat0(
|
||||
if is_send_gateway {
|
||||
continue;
|
||||
}
|
||||
heartbeat_packet_server(device_list, server_cipher, src_ip, gateway_ip)
|
||||
heartbeat_packet_server(device_map, server_cipher, src_ip, gateway_ip)
|
||||
} else {
|
||||
heartbeat_packet_client(client_cipher, src_ip, dest_ip)
|
||||
};
|
||||
@@ -86,15 +88,24 @@ fn heartbeat0(
|
||||
continue;
|
||||
}
|
||||
};
|
||||
for route in routes {
|
||||
for (index, route) in routes.iter().enumerate() {
|
||||
let limit = if context.first_latency() {
|
||||
channel_num + 1
|
||||
} else {
|
||||
channel_num
|
||||
};
|
||||
if index >= limit {
|
||||
// 多余的通道不再发送心跳包,让它自动过期
|
||||
break;
|
||||
}
|
||||
if let Err(e) = context.send_by_key(&net_packet, route.route_key()) {
|
||||
log::warn!("heartbeat err={:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
let peer_list = { device_list.lock().1.clone() };
|
||||
for peer in &peer_list {
|
||||
if !peer.status.is_online() {
|
||||
let peer_list = { device_map.lock().1.clone() };
|
||||
for peer in peer_list.values() {
|
||||
if !peer.status.is_online() || peer.wireguard {
|
||||
continue;
|
||||
}
|
||||
if current_device.is_gateway(&peer.virtual_ip) {
|
||||
@@ -124,11 +135,11 @@ pub fn client_relay(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
client_cipher: Cipher,
|
||||
) {
|
||||
let rs = scheduler.timeout(Duration::from_secs(30), move |s| {
|
||||
client_relay_(s, context, current_device, device_list, client_cipher)
|
||||
client_relay_(s, context, current_device, device_map, client_cipher)
|
||||
});
|
||||
if !rs {
|
||||
log::info!("定时任务停止");
|
||||
@@ -140,19 +151,19 @@ fn client_relay_(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
client_cipher: Cipher,
|
||||
) {
|
||||
if let Err(e) = client_relay0(
|
||||
&context,
|
||||
¤t_device.load(),
|
||||
&device_list,
|
||||
&device_map,
|
||||
&client_cipher,
|
||||
) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
let rs = scheduler.timeout(Duration::from_secs(30), move |s| {
|
||||
client_relay_(s, context, current_device, device_list, client_cipher)
|
||||
client_relay_(s, context, current_device, device_map, client_cipher)
|
||||
});
|
||||
if !rs {
|
||||
log::info!("定时任务停止");
|
||||
@@ -162,17 +173,20 @@ fn client_relay_(
|
||||
fn client_relay0(
|
||||
context: &ChannelContext,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
device_list: &Mutex<(u16, Vec<PeerDeviceInfo>)>,
|
||||
device_map: &Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>,
|
||||
client_cipher: &Cipher,
|
||||
) -> anyhow::Result<()> {
|
||||
// 离线了不再探测
|
||||
if current_device.status.offline() {
|
||||
return Ok(());
|
||||
}
|
||||
let peer_list = { device_list.lock().1.clone() };
|
||||
let peer_list = { device_map.lock().1.clone() };
|
||||
let mut routes = context.route_table.route_table_p2p();
|
||||
for peer in &peer_list {
|
||||
if !peer.status.is_online() || peer.virtual_ip == current_device.virtual_ip {
|
||||
for peer in peer_list.values() {
|
||||
if peer.wireguard
|
||||
|| !peer.status.is_online()
|
||||
|| peer.virtual_ip == current_device.virtual_ip
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if context
|
||||
@@ -232,14 +246,14 @@ fn heartbeat_packet_client(
|
||||
}
|
||||
|
||||
fn heartbeat_packet_server(
|
||||
device_list: &Mutex<(u16, Vec<PeerDeviceInfo>)>,
|
||||
device_map: &Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>,
|
||||
server_cipher: &Cipher,
|
||||
src: Ipv4Addr,
|
||||
dest: Ipv4Addr,
|
||||
) -> anyhow::Result<NetPacket<[u8; 12 + 4 + ENCRYPTION_RESERVED]>> {
|
||||
let mut net_packet = heartbeat_packet(src, dest)?;
|
||||
let mut ping = PingPacket::new(net_packet.payload_mut())?;
|
||||
ping.set_epoch(device_list.lock().0);
|
||||
ping.set_epoch(device_map.lock().0);
|
||||
net_packet.set_gateway_flag(true);
|
||||
server_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
Ok(net_packet)
|
||||
|
||||
@@ -7,6 +7,7 @@ use crossbeam_utils::atomic::AtomicCell;
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::idle::{Idle, IdleType};
|
||||
use crate::channel::sender::ConnectUtil;
|
||||
use crate::channel::socket::LocalInterface;
|
||||
use crate::channel::ConnectProtocol;
|
||||
use crate::handle::callback::{ConnectInfo, ErrorType};
|
||||
use crate::handle::handshaker::Handshake;
|
||||
@@ -130,7 +131,8 @@ fn check_gateway_channel<Call: VntCallback>(
|
||||
let connect_protocol = context.main_protocol();
|
||||
if connect_protocol.is_transport() {
|
||||
// 传输层的协议需要探测服务器地址
|
||||
current_device = domain_request0(current_device_info, config);
|
||||
current_device =
|
||||
domain_request0(current_device_info, config, context.default_interface());
|
||||
}
|
||||
//需要重连
|
||||
call.connect(ConnectInfo::new(*count, current_device.connect_server));
|
||||
@@ -160,11 +162,16 @@ fn check_gateway_channel<Call: VntCallback>(
|
||||
pub fn domain_request0(
|
||||
current_device: &AtomicCell<CurrentDeviceInfo>,
|
||||
config: &BaseConfigInfo,
|
||||
default_interface: &LocalInterface,
|
||||
) -> CurrentDeviceInfo {
|
||||
let mut current_dev = current_device.load();
|
||||
|
||||
// 探测服务端地址变化
|
||||
match dns_query_all(&config.server_addr, config.name_servers.clone()) {
|
||||
match dns_query_all(
|
||||
&config.server_addr,
|
||||
config.name_servers.clone(),
|
||||
default_interface,
|
||||
) {
|
||||
Ok(addrs) => {
|
||||
log::info!(
|
||||
"domain {} dns {:?} addr {:?}",
|
||||
|
||||
@@ -90,7 +90,7 @@ pub fn punch(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
nat_test: NatTest,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
client_cipher: Cipher,
|
||||
receiver: PunchReceiver,
|
||||
@@ -102,7 +102,7 @@ pub fn punch(
|
||||
scheduler,
|
||||
context,
|
||||
nat_test,
|
||||
device_list,
|
||||
device_map,
|
||||
current_device.clone(),
|
||||
client_cipher.clone(),
|
||||
0,
|
||||
@@ -149,7 +149,7 @@ fn punch_start(
|
||||
*v += 1;
|
||||
*v
|
||||
} else {
|
||||
guard.insert(peer_ip, 1);
|
||||
guard.insert(peer_ip, 0);
|
||||
0
|
||||
}
|
||||
};
|
||||
@@ -170,19 +170,19 @@ fn punch_request(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
nat_test: NatTest,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
client_cipher: Cipher,
|
||||
count: usize,
|
||||
punch_record: Arc<Mutex<HashMap<Ipv4Addr, usize>>>,
|
||||
mut last_punch_record: HashMap<Ipv4Addr, usize>,
|
||||
mut last_punch_record: HashMap<Ipv4Addr, PunchRecordItem>,
|
||||
) {
|
||||
let curr = current_device.load();
|
||||
let secs = if curr.status.online() {
|
||||
if let Err(e) = punch0(
|
||||
&context,
|
||||
&nat_test,
|
||||
&device_list,
|
||||
&device_map,
|
||||
curr,
|
||||
&client_cipher,
|
||||
&punch_record,
|
||||
@@ -201,7 +201,7 @@ fn punch_request(
|
||||
s,
|
||||
context,
|
||||
nat_test,
|
||||
device_list,
|
||||
device_map,
|
||||
current_device,
|
||||
client_cipher,
|
||||
count + 1,
|
||||
@@ -213,16 +213,21 @@ fn punch_request(
|
||||
log::info!("定时任务停止");
|
||||
}
|
||||
}
|
||||
#[derive(Copy, Clone, Default)]
|
||||
struct PunchRecordItem {
|
||||
pub punch_record: usize,
|
||||
pub last_p2p_num: usize,
|
||||
}
|
||||
|
||||
/// 随机对需要打洞的客户端发起打洞请求
|
||||
fn punch0(
|
||||
context: &ChannelContext,
|
||||
nat_test: &NatTest,
|
||||
device_list: &Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
device_map: &Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
current_device: CurrentDeviceInfo,
|
||||
client_cipher: &Cipher,
|
||||
punch_record: &Mutex<HashMap<Ipv4Addr, usize>>,
|
||||
last_punch_record: &mut HashMap<Ipv4Addr, usize>,
|
||||
last_punch_record: &mut HashMap<Ipv4Addr, PunchRecordItem>,
|
||||
total_count: usize,
|
||||
) -> anyhow::Result<()> {
|
||||
let nat_info = nat_test.nat_info();
|
||||
@@ -233,20 +238,25 @@ fn punch0(
|
||||
|| nat_info.public_ports.iter().filter(|&&v| v == 0).count()
|
||||
> nat_info.public_ports.len() / 2)
|
||||
{
|
||||
log::info!("公网地址为空,暂时放弃打洞,第{}轮", total_count);
|
||||
log::info!("未获取到公网地址,暂时放弃打洞,第{}轮", total_count);
|
||||
return Ok(());
|
||||
}
|
||||
let current_ip = current_device.virtual_ip;
|
||||
let mut list: Vec<PeerDeviceInfo> = device_list
|
||||
let mut list: Vec<PeerDeviceInfo> = device_map
|
||||
.lock()
|
||||
.1
|
||||
.iter()
|
||||
.filter(|info| info.status.is_online() && info.virtual_ip > current_ip)
|
||||
.values()
|
||||
.filter(|info| !info.wireguard && info.virtual_ip > current_ip)
|
||||
.cloned()
|
||||
.collect();
|
||||
list.shuffle(&mut rand::thread_rng());
|
||||
for info in list {
|
||||
let punch_count = punch_record
|
||||
if info.status.is_offline() {
|
||||
// 客户端掉线了要重置打洞记录
|
||||
punch_record.lock().remove(&info.virtual_ip);
|
||||
continue;
|
||||
}
|
||||
let mut punch_count = punch_record
|
||||
.lock()
|
||||
.get(&info.virtual_ip)
|
||||
.cloned()
|
||||
@@ -256,12 +266,11 @@ fn punch0(
|
||||
let p2p_num = context.route_table.p2p_num(&info.virtual_ip);
|
||||
let mut max_punch_interval = 50;
|
||||
if p2p_num > 0 {
|
||||
if punch_count == 0 {
|
||||
continue;
|
||||
}
|
||||
if p2p_num >= context.channel_num() {
|
||||
//通道数满足要求,不再打洞
|
||||
punch_record.lock().remove(&info.virtual_ip);
|
||||
if punch_count != 0 {
|
||||
punch_record.lock().remove(&info.virtual_ip);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
//有p2p通道,但是通道数量不够,则继续打洞
|
||||
@@ -269,13 +278,18 @@ fn punch0(
|
||||
max_punch_interval = 300;
|
||||
}
|
||||
// 能发起打洞的前提是自己空闲,这里会间隔5秒以上发起一次打洞,所以假定上一轮打洞已结束
|
||||
let last_punch = last_punch_record
|
||||
.get(&info.virtual_ip)
|
||||
.cloned()
|
||||
.unwrap_or(0);
|
||||
let last_punch = last_punch_record.entry(info.virtual_ip).or_default();
|
||||
if last_punch.last_p2p_num > p2p_num {
|
||||
// 打的洞掉线了,需要重置重新打
|
||||
punch_record.lock().remove(&info.virtual_ip);
|
||||
punch_count = 0;
|
||||
}
|
||||
|
||||
// 梯度增加打洞时间间隔
|
||||
if total_count > last_punch + punch_count.min(max_punch_interval) {
|
||||
last_punch_record.insert(info.virtual_ip, total_count);
|
||||
if total_count > last_punch.punch_record + punch_count.min(max_punch_interval) {
|
||||
// 记录打洞周期,抑制下一次打洞,从而递减打洞频率
|
||||
last_punch.punch_record = total_count;
|
||||
last_punch.last_p2p_num = p2p_num;
|
||||
let packet = punch_packet(
|
||||
client_cipher,
|
||||
current_device.virtual_ip(),
|
||||
@@ -312,6 +326,7 @@ fn punch_packet(
|
||||
punch_reply.public_port = nat_info.public_ports.get(0).map_or(0, |v| *v as u32);
|
||||
punch_reply.public_ports = nat_info.public_ports.iter().map(|e| *e as u32).collect();
|
||||
punch_reply.public_port_range = nat_info.public_port_range as u32;
|
||||
punch_reply.public_tcp_port = nat_info.public_tcp_port as u32;
|
||||
punch_reply.local_ip = u32::from(nat_info.local_ipv4().unwrap_or(Ipv4Addr::UNSPECIFIED));
|
||||
punch_reply.local_port = nat_info.udp_ports[0] as u32;
|
||||
punch_reply.tcp_port = nat_info.tcp_port as u32;
|
||||
@@ -321,6 +336,7 @@ fn punch_packet(
|
||||
punch_reply.ipv6 = ipv6.octets().to_vec();
|
||||
}
|
||||
punch_reply.nat_type = protobuf::EnumOrUnknown::new(PunchNatType::from(nat_info.nat_type));
|
||||
punch_reply.punch_model = protobuf::EnumOrUnknown::new(nat_info.punch_model.into());
|
||||
log::info!("请求打洞={:?}", punch_reply);
|
||||
let bytes = punch_reply
|
||||
.write_to_bytes()
|
||||
|
||||
@@ -29,9 +29,13 @@ fn retrieve_nat_type0(
|
||||
.name("natTest".into())
|
||||
.spawn(move || {
|
||||
if nat_test.can_update() {
|
||||
let local_ipv4 = nat::local_ipv4();
|
||||
let local_ipv4 = if nat_test.update_local_ipv4 {
|
||||
nat::local_ipv4()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let local_ipv6 = nat::local_ipv6();
|
||||
match nat_test.re_test(local_ipv4, local_ipv6) {
|
||||
match nat_test.re_test(local_ipv4, local_ipv6, context.default_interface()) {
|
||||
Ok(nat_info) => {
|
||||
log::info!("当前nat信息:{:?}", nat_info);
|
||||
if let Err(e) = context.switch(nat_info.nat_type, &udp_socket_sender) {
|
||||
@@ -44,7 +48,7 @@ fn retrieve_nat_type0(
|
||||
};
|
||||
#[cfg(feature = "upnp")]
|
||||
nat_test.reset_upnp();
|
||||
log::info!("刷新nat成功")
|
||||
log::info!("刷新nat结束")
|
||||
}
|
||||
})
|
||||
.expect("natTest");
|
||||
|
||||
+16
-3
@@ -1,3 +1,4 @@
|
||||
use crate::channel::socket::LocalInterface;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
|
||||
@@ -29,6 +30,7 @@ pub struct PeerDeviceInfo {
|
||||
pub status: PeerDeviceStatus,
|
||||
pub client_secret: bool,
|
||||
pub client_secret_hash: Vec<u8>,
|
||||
pub wireguard: bool,
|
||||
}
|
||||
|
||||
impl PeerDeviceInfo {
|
||||
@@ -38,6 +40,7 @@ impl PeerDeviceInfo {
|
||||
status: u8,
|
||||
client_secret: bool,
|
||||
client_secret_hash: Vec<u8>,
|
||||
wireguard: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
virtual_ip,
|
||||
@@ -45,6 +48,7 @@ impl PeerDeviceInfo {
|
||||
status: PeerDeviceStatus::from(status),
|
||||
client_secret,
|
||||
client_secret_hash,
|
||||
wireguard,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,6 +70,8 @@ pub struct BaseConfigInfo {
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
pub device_name: Option<String>,
|
||||
pub allow_wire_guard: bool,
|
||||
pub default_interface: LocalInterface,
|
||||
}
|
||||
|
||||
impl BaseConfigInfo {
|
||||
@@ -85,6 +91,8 @@ impl BaseConfigInfo {
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
device_name: Option<String>,
|
||||
allow_wire_guard: bool,
|
||||
default_interface: LocalInterface,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
@@ -102,6 +110,8 @@ impl BaseConfigInfo {
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
device_name,
|
||||
allow_wire_guard,
|
||||
default_interface,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,6 +126,9 @@ impl PeerDeviceStatus {
|
||||
pub fn is_online(&self) -> bool {
|
||||
self == &PeerDeviceStatus::Online
|
||||
}
|
||||
pub fn is_offline(&self) -> bool {
|
||||
self == &PeerDeviceStatus::Offline
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for PeerDeviceStatus {
|
||||
@@ -210,10 +223,10 @@ impl CurrentDeviceInfo {
|
||||
virtual_gateway: Ipv4Addr,
|
||||
) {
|
||||
let broadcast_ip = (!u32::from_be_bytes(virtual_netmask.octets()))
|
||||
| u32::from_be_bytes(virtual_gateway.octets());
|
||||
| u32::from_be_bytes(virtual_ip.octets());
|
||||
let broadcast_ip = Ipv4Addr::from(broadcast_ip);
|
||||
let virtual_network = u32::from_be_bytes(virtual_netmask.octets())
|
||||
& u32::from_be_bytes(virtual_gateway.octets());
|
||||
let virtual_network =
|
||||
u32::from_be_bytes(virtual_netmask.octets()) & u32::from_be_bytes(virtual_ip.octets());
|
||||
let virtual_network = Ipv4Addr::from(virtual_network);
|
||||
self.virtual_ip = virtual_ip;
|
||||
self.virtual_netmask = virtual_netmask;
|
||||
|
||||
@@ -194,6 +194,9 @@ impl<Device: DeviceWrite> ClientPacketHandler<Device> {
|
||||
}
|
||||
self.device.write(net_packet.payload())?;
|
||||
}
|
||||
ip_turn_packet::Protocol::WGIpv4 => {
|
||||
// WG客户端的数据不会直接发过来,不用处理
|
||||
}
|
||||
ip_turn_packet::Protocol::Ipv4Broadcast => {
|
||||
//客户端不帮忙转发广播包,所以不会出现这种类型的数据
|
||||
}
|
||||
@@ -213,17 +216,13 @@ impl<Device: DeviceWrite> ClientPacketHandler<Device> {
|
||||
match ControlPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
|
||||
ControlPacket::PingPacket(_) => {
|
||||
let route = Route::from_default_rt(route_key, metric);
|
||||
if context.route_table.add_route_if_absent(source, route)
|
||||
|| net_packet.source() < current_device.virtual_ip
|
||||
{
|
||||
//在路由表中,或者来源比自己小,就需要回复,注意不能调换顺序
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::Pong.into());
|
||||
net_packet.set_source(current_device.virtual_ip);
|
||||
net_packet.set_destination(source);
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
self.client_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
context.send_by_key(&net_packet, route_key)?;
|
||||
}
|
||||
context.route_table.add_route_if_absent(source, route);
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::Pong.into());
|
||||
net_packet.set_source(current_device.virtual_ip);
|
||||
net_packet.set_destination(source);
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
self.client_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
context.send_by_key(&net_packet, route_key)?;
|
||||
}
|
||||
ControlPacket::PongPacket(pong_packet) => {
|
||||
let current_time = crate::handle::now_time() as u16;
|
||||
@@ -269,7 +268,7 @@ impl<Device: DeviceWrite> ClientPacketHandler<Device> {
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
let route = Route::from_default_rt(route_key, 1);
|
||||
let route = Route::from_default_rt(route_key, metric);
|
||||
context.route_table.add_route_if_absent(source, route);
|
||||
}
|
||||
ControlPacket::AddrRequest => match route_key.addr.ip() {
|
||||
@@ -315,6 +314,7 @@ impl<Device: DeviceWrite> ClientPacketHandler<Device> {
|
||||
.collect();
|
||||
let local_ipv4 = Some(Ipv4Addr::from(punch_info.local_ip.to_be_bytes()));
|
||||
let tcp_port = punch_info.tcp_port as u16;
|
||||
let public_tcp_port = punch_info.public_tcp_port as u16;
|
||||
let ipv6 = if punch_info.ipv6.len() == 16 {
|
||||
let ipv6: [u8; 16] = punch_info.ipv6.try_into().unwrap();
|
||||
Some(Ipv6Addr::from(ipv6))
|
||||
@@ -337,7 +337,9 @@ impl<Device: DeviceWrite> ClientPacketHandler<Device> {
|
||||
ipv6,
|
||||
punch_info.udp_ports.iter().map(|e| *e as u16).collect(),
|
||||
tcp_port,
|
||||
public_tcp_port,
|
||||
punch_info.nat_type.enum_value_or_default().into(),
|
||||
punch_info.punch_model.enum_value_or_default().into(),
|
||||
);
|
||||
{
|
||||
let peer_nat_info = peer_nat_info.clone();
|
||||
@@ -357,8 +359,11 @@ impl<Device: DeviceWrite> ClientPacketHandler<Device> {
|
||||
nat_info.public_ports.iter().map(|e| *e as u32).collect();
|
||||
punch_reply.public_port_range = nat_info.public_port_range as u32;
|
||||
punch_reply.tcp_port = nat_info.tcp_port as u32;
|
||||
punch_reply.public_tcp_port = nat_info.public_tcp_port as u32;
|
||||
punch_reply.nat_type =
|
||||
protobuf::EnumOrUnknown::new(PunchNatType::from(nat_info.nat_type));
|
||||
punch_reply.punch_model =
|
||||
protobuf::EnumOrUnknown::new(nat_info.punch_model.into());
|
||||
punch_reply.local_ip =
|
||||
u32::from(nat_info.local_ipv4().unwrap_or(Ipv4Addr::UNSPECIFIED));
|
||||
punch_reply.local_port = nat_info.udp_ports[0] as u32;
|
||||
|
||||
@@ -80,7 +80,7 @@ impl<Call: VntCallback, Device: DeviceWrite> RecvDataHandler<Call, Device> {
|
||||
client_cipher: Cipher,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device: Device,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
config_info: BaseConfigInfo,
|
||||
nat_test: NatTest,
|
||||
callback: Call,
|
||||
@@ -101,7 +101,7 @@ impl<Call: VntCallback, Device: DeviceWrite> RecvDataHandler<Call, Device> {
|
||||
server_cipher,
|
||||
current_device.clone(),
|
||||
device.clone(),
|
||||
device_list,
|
||||
device_map,
|
||||
config_info,
|
||||
nat_test.clone(),
|
||||
callback,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use anyhow::anyhow;
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
@@ -42,7 +43,7 @@ pub struct ServerPacketHandler<Call, Device> {
|
||||
server_cipher: Cipher,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device: Device,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
config_info: BaseConfigInfo,
|
||||
nat_test: NatTest,
|
||||
callback: Call,
|
||||
@@ -60,7 +61,7 @@ impl<Call, Device> ServerPacketHandler<Call, Device> {
|
||||
server_cipher: Cipher,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device: Device,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
config_info: BaseConfigInfo,
|
||||
nat_test: NatTest,
|
||||
callback: Call,
|
||||
@@ -75,7 +76,7 @@ impl<Call, Device> ServerPacketHandler<Call, Device> {
|
||||
server_cipher,
|
||||
current_device,
|
||||
device,
|
||||
device_list,
|
||||
device_map,
|
||||
config_info,
|
||||
nat_test,
|
||||
callback,
|
||||
@@ -104,12 +105,11 @@ impl<Call: VntCallback, Device: DeviceWrite> PacketHandler for ServerPacketHandl
|
||||
) -> anyhow::Result<()> {
|
||||
if !current_device.is_server_addr(route_key.addr) {
|
||||
//拦截不是服务端的流量
|
||||
log::info!(
|
||||
log::warn!(
|
||||
"route_key={:?},不是来源于服务端地址{}",
|
||||
route_key,
|
||||
current_device.connect_server
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
context
|
||||
.route_table
|
||||
@@ -151,6 +151,8 @@ impl<Call: VntCallback, Device: DeviceWrite> PacketHandler for ServerPacketHandl
|
||||
let response = HandshakeResponse::parse_from_bytes(net_packet.payload())
|
||||
.map_err(|e| anyhow!("HandshakeResponse {:?}", e))?;
|
||||
log::info!("握手响应:{:?},{}", route_key, response);
|
||||
//设置为默认通道
|
||||
context.set_default_route_key(route_key);
|
||||
//如果开启了加密,则发送加密握手请求
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
if let Some(key) = self.server_cipher.key() {
|
||||
@@ -211,7 +213,7 @@ impl<Call: VntCallback, Device: DeviceWrite> PacketHandler for ServerPacketHandl
|
||||
let handshake_info = HandshakeInfo::new_no_secret(response.version);
|
||||
if self.callback.handshake(handshake_info) {
|
||||
//没有加密,则发送注册请求
|
||||
self.register(current_device, context)?;
|
||||
self.register(current_device, context, route_key)?;
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
@@ -246,6 +248,11 @@ impl<Call: VntCallback, Device: DeviceWrite> PacketHandler for ServerPacketHandl
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
ip_turn_packet::Protocol::WGIpv4 => {
|
||||
if self.config_info.allow_wire_guard {
|
||||
self.device.write(net_packet.payload())?;
|
||||
}
|
||||
}
|
||||
ip_turn_packet::Protocol::Ipv4Broadcast => {}
|
||||
ip_turn_packet::Protocol::Unknown(_) => {}
|
||||
}
|
||||
@@ -290,6 +297,10 @@ impl<Call: VntCallback, Device: DeviceWrite> ServerPacketHandler<Call, Device> {
|
||||
let public_port = response.public_port as u16;
|
||||
self.nat_test
|
||||
.update_addr(route_key.index(), public_ip, public_port);
|
||||
if route_key.protocol().is_tcp() {
|
||||
log::info!("更新公网tcp端口 {public_port}");
|
||||
self.nat_test.update_tcp_port(public_port);
|
||||
}
|
||||
let old = current_device;
|
||||
let mut cur = *current_device;
|
||||
loop {
|
||||
@@ -344,7 +355,10 @@ impl<Call: VntCallback, Device: DeviceWrite> ServerPacketHandler<Call, Device> {
|
||||
target_os = "linux",
|
||||
target_os = "macos"
|
||||
))]
|
||||
match crate::tun_tap_device::create_device(device_config) {
|
||||
match crate::tun_tap_device::create_device(
|
||||
device_config,
|
||||
&self.callback,
|
||||
) {
|
||||
Ok(device) => {
|
||||
use tun::device::IFace;
|
||||
let tun_info = crate::handle::callback::DeviceInfo::new(
|
||||
@@ -353,7 +367,8 @@ impl<Call: VntCallback, Device: DeviceWrite> ServerPacketHandler<Call, Device> {
|
||||
);
|
||||
log::info!("tun信息{:?}", tun_info);
|
||||
self.callback.create_tun(tun_info);
|
||||
self.tun_device_helper.start(device)?;
|
||||
self.tun_device_helper
|
||||
.start(device, self.config_info.allow_wire_guard)?;
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?}", e);
|
||||
@@ -373,24 +388,25 @@ impl<Call: VntCallback, Device: DeviceWrite> ServerPacketHandler<Call, Device> {
|
||||
let device_fd = self.callback.generate_tun(device_config);
|
||||
if device_fd == 0 {
|
||||
self.callback.error(ErrorInfo::new_msg(
|
||||
ErrorType::Unknown,
|
||||
ErrorType::FailedToCrateDevice,
|
||||
"device_fd == 0".into(),
|
||||
));
|
||||
} else {
|
||||
match tun::Device::new(device_fd as _) {
|
||||
Ok(device) => {
|
||||
if let Err(e) =
|
||||
self.tun_device_helper.start(Arc::new(device))
|
||||
{
|
||||
if let Err(e) = self.tun_device_helper.start(
|
||||
Arc::new(device),
|
||||
self.config_info.allow_wire_guard,
|
||||
) {
|
||||
self.callback.error(ErrorInfo::new_msg(
|
||||
ErrorType::Unknown,
|
||||
ErrorType::FailedToCrateDevice,
|
||||
format!("{:?}", e),
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
self.callback.error(ErrorInfo::new_msg(
|
||||
ErrorType::Unknown,
|
||||
ErrorType::FailedToCrateDevice,
|
||||
format!("{:?}", e),
|
||||
));
|
||||
}
|
||||
@@ -414,7 +430,7 @@ impl<Call: VntCallback, Device: DeviceWrite> ServerPacketHandler<Call, Device> {
|
||||
service_packet::Protocol::SecretHandshakeResponse => {
|
||||
log::info!("SecretHandshakeResponse");
|
||||
//加密握手结束,发送注册数据
|
||||
self.register(current_device, context)?;
|
||||
self.register(current_device, context, route_key)?;
|
||||
}
|
||||
_ => {
|
||||
log::warn!(
|
||||
@@ -435,14 +451,18 @@ impl<Call: VntCallback, Device: DeviceWrite> ServerPacketHandler<Call, Device> {
|
||||
info.device_status as u8,
|
||||
info.client_secret,
|
||||
info.client_secret_hash,
|
||||
info.wireguard,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
{
|
||||
let mut dev = self.device_list.lock();
|
||||
let mut dev = self.device_map.lock();
|
||||
//这里可能会收到旧的消息,但是随着时间推移总会收到新的
|
||||
dev.0 = epoch;
|
||||
dev.1 = ip_list.clone();
|
||||
dev.1.clear();
|
||||
for info in ip_list.clone() {
|
||||
dev.1.insert(info.virtual_ip, info);
|
||||
}
|
||||
}
|
||||
self.callback.peer_client_list(
|
||||
ip_list
|
||||
@@ -455,11 +475,14 @@ impl<Call: VntCallback, Device: DeviceWrite> ServerPacketHandler<Call, Device> {
|
||||
&self,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
context: &ChannelContext,
|
||||
route_key: RouteKey,
|
||||
) -> anyhow::Result<()> {
|
||||
if current_device.status.online() {
|
||||
log::info!("已连接的不需要注册,{:?}", self.config_info);
|
||||
return Ok(());
|
||||
}
|
||||
//设置为默认通道
|
||||
context.set_default_route_key(route_key);
|
||||
let token = self.config_info.token.clone();
|
||||
let device_id = self.config_info.device_id.clone();
|
||||
let name = self.config_info.name.clone();
|
||||
@@ -506,7 +529,7 @@ impl<Call: VntCallback, Device: DeviceWrite> ServerPacketHandler<Call, Device> {
|
||||
self.callback.error(err);
|
||||
//掉线epoch要归零
|
||||
{
|
||||
let mut dev = self.device_list.lock();
|
||||
let mut dev = self.device_map.lock();
|
||||
dev.0 = 0;
|
||||
drop(dev);
|
||||
}
|
||||
@@ -554,7 +577,7 @@ impl<Call: VntCallback, Device: DeviceWrite> ServerPacketHandler<Call, Device> {
|
||||
let rt = (current_time - pong_packet.time()) as i64;
|
||||
let route = Route::from(route_key, metric, rt);
|
||||
context.route_table.add_route(net_packet.source(), route);
|
||||
let epoch = self.device_list.lock().0;
|
||||
let epoch = self.device_map.lock().0;
|
||||
if pong_packet.epoch() != epoch {
|
||||
//纪元不一致,可能有新客户端连接,向服务端拉取客户端列表
|
||||
let mut poll_device = NetPacket::new_encrypt([0; 12 + ENCRYPTION_RESERVED])?;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use std::collections::HashMap;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use std::{io, thread};
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use packet::icmp::icmp::IcmpPacket;
|
||||
use packet::icmp::Kind;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
@@ -13,6 +13,7 @@ use tun::device::IFace;
|
||||
use tun::Device;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::sender::{send_to_wg, send_to_wg_broadcast};
|
||||
use crate::cipher::Cipher;
|
||||
use crate::compression::Compressor;
|
||||
use crate::external_route::ExternalRoute;
|
||||
@@ -27,7 +28,6 @@ use crate::protocol::body::ENCRYPTION_RESERVED;
|
||||
use crate::protocol::ip_turn_packet::BroadcastPacket;
|
||||
use crate::protocol::{ip_turn_packet, NetPacket, MAX_TTL};
|
||||
use crate::util::StopManager;
|
||||
|
||||
fn icmp(device_writer: &Device, mut ipv4_packet: IpV4Packet<&mut [u8]>) -> anyhow::Result<()> {
|
||||
if ipv4_packet.protocol() == Protocol::Icmp {
|
||||
let mut icmp = IcmpPacket::new(ipv4_packet.payload_mut())?;
|
||||
@@ -53,9 +53,10 @@ pub fn start(
|
||||
#[cfg(feature = "ip_proxy")] ip_proxy_map: Option<IpProxyMap>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
compressor: Compressor,
|
||||
device_stop: DeviceStop,
|
||||
allow_wire_guard: bool,
|
||||
) -> io::Result<()> {
|
||||
thread::Builder::new()
|
||||
.name("tunHandlerS".into())
|
||||
@@ -70,9 +71,10 @@ pub fn start(
|
||||
ip_proxy_map,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
device_list,
|
||||
device_map,
|
||||
compressor,
|
||||
device_stop,
|
||||
allow_wire_guard,
|
||||
) {
|
||||
log::warn!("stop:{}", e);
|
||||
}
|
||||
@@ -86,18 +88,21 @@ fn broadcast(
|
||||
sender: &ChannelContext,
|
||||
net_packet: &mut NetPacket<&mut [u8]>,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
device_list: &Mutex<(u16, Vec<PeerDeviceInfo>)>,
|
||||
device_map: &Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>,
|
||||
) -> anyhow::Result<()> {
|
||||
let list: Vec<Ipv4Addr> = device_list
|
||||
let list: Vec<Ipv4Addr> = device_map
|
||||
.lock()
|
||||
.1
|
||||
.iter()
|
||||
.filter(|info| info.status.is_online())
|
||||
.values()
|
||||
.filter(|info| !info.wireguard && info.status.is_online())
|
||||
.map(|info| info.virtual_ip)
|
||||
.collect();
|
||||
if list.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
const MAX_COUNT: usize = 8;
|
||||
let mut p2p_ips = Vec::with_capacity(8);
|
||||
let mut relay_ips = Vec::with_capacity(8);
|
||||
let mut relay = false;
|
||||
let mut overflow = false;
|
||||
for (index, peer_ip) in list.into_iter().enumerate() {
|
||||
if index > MAX_COUNT {
|
||||
@@ -110,38 +115,22 @@ fn broadcast(
|
||||
continue;
|
||||
}
|
||||
}
|
||||
relay_ips.push(peer_ip);
|
||||
relay = true;
|
||||
}
|
||||
if !overflow && relay_ips.is_empty() {
|
||||
if !overflow && !relay {
|
||||
//全部p2p,不需要服务器中转
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if p2p_ips.is_empty() {
|
||||
//都没有p2p则直接由服务器转发
|
||||
if current_device.status.online() {
|
||||
sender.send_default(&net_packet, current_device.connect_server)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
if !overflow && relay_ips.len() == 2 {
|
||||
// 如果转发的ip数不多就直接发
|
||||
for peer_ip in relay_ips {
|
||||
//非直连的广播要改变目的地址,不然服务端收到了会再次广播
|
||||
net_packet.set_destination(peer_ip);
|
||||
sender.send_ipv4_by_id(
|
||||
&net_packet,
|
||||
&peer_ip,
|
||||
current_device.connect_server,
|
||||
current_device.status.online(),
|
||||
)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
if current_device.status.offline() {
|
||||
//离线的不再转发
|
||||
return Ok(());
|
||||
}
|
||||
if p2p_ips.is_empty() {
|
||||
//都没有p2p则直接由服务器转发
|
||||
sender.send_default(&net_packet, current_device.connect_server)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let buf = vec![0u8; 12 + 1 + p2p_ips.len() * 4 + net_packet.data_len() + ENCRYPTION_RESERVED];
|
||||
//剩余的发送到服务端,需要告知哪些已发送过
|
||||
let mut server_packet = NetPacket::new_encrypt(buf)?;
|
||||
@@ -177,8 +166,9 @@ pub(crate) fn handle(
|
||||
#[cfg(feature = "ip_proxy")] proxy_map: &Option<IpProxyMap>,
|
||||
client_cipher: &Cipher,
|
||||
server_cipher: &Cipher,
|
||||
device_list: &Mutex<(u16, Vec<PeerDeviceInfo>)>,
|
||||
device_map: &Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>,
|
||||
compressor: &Compressor,
|
||||
allow_wire_guard: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
//忽略掉结构不对的情况(ipv6数据、win tap会读到空数据),不然日志打印太多了
|
||||
let ipv4_packet = match IpV4Packet::new(&mut buf[12..data_len]) {
|
||||
@@ -237,6 +227,33 @@ pub(crate) fn handle(
|
||||
dest_ip = Ipv4Addr::BROADCAST;
|
||||
net_packet.set_destination(Ipv4Addr::BROADCAST);
|
||||
}
|
||||
let is_broadcast = dest_ip.is_broadcast() || current_device.broadcast_ip == dest_ip;
|
||||
if allow_wire_guard {
|
||||
if is_broadcast {
|
||||
// wg客户端和vnt客户端分开广播
|
||||
let exists_wg = device_map
|
||||
.lock()
|
||||
.1
|
||||
.values()
|
||||
.any(|v| v.status.is_online() && v.wireguard);
|
||||
if exists_wg {
|
||||
send_to_wg_broadcast(context, &net_packet, server_cipher, ¤t_device)?;
|
||||
}
|
||||
} else {
|
||||
// 如果是wg客户端则发到vnts转发
|
||||
let guard = device_map.lock();
|
||||
if let Some(peer_info) = guard.1.get(&dest_ip) {
|
||||
if peer_info.status.is_offline() {
|
||||
return Ok(());
|
||||
}
|
||||
if peer_info.wireguard {
|
||||
drop(guard);
|
||||
send_to_wg(context, &mut net_packet, server_cipher, ¤t_device)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut net_packet = if compressor.compress(&net_packet, &mut out)? {
|
||||
out.set_default_version();
|
||||
@@ -249,7 +266,7 @@ pub(crate) fn handle(
|
||||
} else {
|
||||
net_packet
|
||||
};
|
||||
if dest_ip.is_broadcast() || current_device.broadcast_ip == dest_ip {
|
||||
if is_broadcast {
|
||||
// 广播 发送到直连目标
|
||||
client_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
broadcast(
|
||||
@@ -257,7 +274,7 @@ pub(crate) fn handle(
|
||||
context,
|
||||
&mut net_packet,
|
||||
¤t_device,
|
||||
device_list,
|
||||
device_map,
|
||||
)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ use mio::event::Source;
|
||||
use mio::unix::SourceFd;
|
||||
use mio::{Events, Interest, Poll, Token, Waker};
|
||||
use parking_lot::Mutex;
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::sync::Arc;
|
||||
use tun::Device;
|
||||
@@ -30,9 +32,10 @@ pub(crate) fn start_simple(
|
||||
#[cfg(feature = "ip_proxy")] ip_proxy_map: Option<IpProxyMap>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
compressor: Compressor,
|
||||
device_stop: DeviceStop,
|
||||
allow_wire_guard: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
let poll = Poll::new()?;
|
||||
let waker = Arc::new(Waker::new(poll.registry(), STOP)?);
|
||||
@@ -61,8 +64,9 @@ pub(crate) fn start_simple(
|
||||
ip_proxy_map,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
device_list,
|
||||
device_map,
|
||||
compressor,
|
||||
allow_wire_guard,
|
||||
) {
|
||||
log::error!("{:?}", e);
|
||||
};
|
||||
@@ -83,8 +87,9 @@ fn start_simple0(
|
||||
#[cfg(feature = "ip_proxy")] ip_proxy_map: Option<IpProxyMap>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
compressor: Compressor,
|
||||
allow_wire_guard: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut buf = [0; BUFFER_SIZE];
|
||||
let mut extend = [0; BUFFER_SIZE];
|
||||
@@ -134,8 +139,9 @@ fn start_simple0(
|
||||
&ip_proxy_map,
|
||||
&client_cipher,
|
||||
&server_cipher,
|
||||
&device_list,
|
||||
&device_map,
|
||||
&compressor,
|
||||
allow_wire_guard,
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
|
||||
@@ -10,6 +10,8 @@ use crate::ip_proxy::IpProxyMap;
|
||||
use crate::util::StopManager;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use std::collections::HashMap;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use tun::device::IFace;
|
||||
use tun::Device;
|
||||
@@ -23,9 +25,10 @@ pub(crate) fn start_simple(
|
||||
#[cfg(feature = "ip_proxy")] ip_proxy_map: Option<IpProxyMap>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
compressor: Compressor,
|
||||
device_stop: DeviceStop,
|
||||
allow_wire_guard: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
let worker = {
|
||||
let device = device.clone();
|
||||
@@ -54,8 +57,9 @@ pub(crate) fn start_simple(
|
||||
ip_proxy_map,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
device_list,
|
||||
device_map,
|
||||
compressor,
|
||||
allow_wire_guard,
|
||||
) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
@@ -74,8 +78,9 @@ fn start_simple0(
|
||||
#[cfg(feature = "ip_proxy")] ip_proxy_map: Option<IpProxyMap>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
compressor: Compressor,
|
||||
allow_wire_guard: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut buf = [0; BUFFER_SIZE];
|
||||
let mut extend = [0; BUFFER_SIZE];
|
||||
@@ -96,8 +101,9 @@ fn start_simple0(
|
||||
&ip_proxy_map,
|
||||
&client_cipher,
|
||||
&server_cipher,
|
||||
&device_list,
|
||||
&device_map,
|
||||
&compressor,
|
||||
allow_wire_guard,
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
|
||||
@@ -13,6 +13,7 @@ use packet::icmp::icmp::HeaderOther;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::socket::{LocalInterface, VntSocketTrait};
|
||||
use crate::cipher::Cipher;
|
||||
use crate::handle::CurrentDeviceInfo;
|
||||
use crate::ip_proxy::ProxyHandler;
|
||||
@@ -30,6 +31,7 @@ impl IcmpProxy {
|
||||
context: ChannelContext,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
client_cipher: Cipher,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<Self> {
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
let icmp_socket = socket2::Socket::new(
|
||||
@@ -50,6 +52,9 @@ impl IcmpProxy {
|
||||
.bind(&socket2::SockAddr::from(addr))
|
||||
.context("bind Socket ICMPV4 failed")?;
|
||||
icmp_socket.set_nonblocking(true)?;
|
||||
if let Err(e) = icmp_socket.set_ip_unicast_if(default_interface) {
|
||||
log::warn!("set_ip_unicast_if {:?}", e)
|
||||
}
|
||||
let std_socket: std::net::UdpSocket = icmp_socket.into();
|
||||
|
||||
let tokio_icmp_socket = UdpSocket::from_std(std_socket.try_clone()?)?;
|
||||
|
||||
@@ -68,14 +68,16 @@ pub fn init_proxy(
|
||||
}
|
||||
|
||||
async fn init_proxy0(
|
||||
_context: ChannelContext,
|
||||
context: ChannelContext,
|
||||
_current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
_client_cipher: Cipher,
|
||||
) -> anyhow::Result<IpProxyMap> {
|
||||
let default_interface = context.default_interface().clone();
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
let icmp_proxy = IcmpProxy::new(_context, _current_device, _client_cipher).await?;
|
||||
let tcp_proxy = TcpProxy::new().await?;
|
||||
let udp_proxy = UdpProxy::new().await?;
|
||||
let icmp_proxy =
|
||||
IcmpProxy::new(context, _current_device, _client_cipher, &default_interface).await?;
|
||||
let tcp_proxy = TcpProxy::new(default_interface.clone()).await?;
|
||||
let udp_proxy = UdpProxy::new(default_interface.clone()).await?;
|
||||
|
||||
Ok(IpProxyMap {
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
|
||||
@@ -5,13 +5,13 @@ use std::time::Duration;
|
||||
use std::{collections::HashMap, io, net::SocketAddr};
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use tokio::net::{TcpListener, TcpSocket, TcpStream};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
|
||||
use crate::channel::socket::{create_tcp, LocalInterface};
|
||||
use crate::ip_proxy::ProxyHandler;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
use packet::tcp::tcp::TcpPacket;
|
||||
|
||||
use crate::ip_proxy::ProxyHandler;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TcpProxy {
|
||||
port: u16,
|
||||
@@ -19,7 +19,7 @@ pub struct TcpProxy {
|
||||
}
|
||||
|
||||
impl TcpProxy {
|
||||
pub async fn new() -> anyhow::Result<Self> {
|
||||
pub async fn new(default_interface: LocalInterface) -> anyhow::Result<Self> {
|
||||
let nat_map: Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>> =
|
||||
Arc::new(Mutex::new(HashMap::with_capacity(16)));
|
||||
let tcp_listener = TcpListener::bind(format!("0.0.0.0:{}", 0))
|
||||
@@ -28,7 +28,7 @@ impl TcpProxy {
|
||||
let port = tcp_listener.local_addr()?.port();
|
||||
{
|
||||
let nat_map = nat_map.clone();
|
||||
tokio::spawn(tcp_proxy(tcp_listener, nat_map));
|
||||
tokio::spawn(tcp_proxy(tcp_listener, nat_map, default_interface));
|
||||
}
|
||||
Ok(Self { port, nat_map })
|
||||
}
|
||||
@@ -79,26 +79,33 @@ impl ProxyHandler for TcpProxy {
|
||||
async fn tcp_proxy(
|
||||
tcp_listener: TcpListener,
|
||||
nat_map: Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>>,
|
||||
default_interface: LocalInterface,
|
||||
) {
|
||||
loop {
|
||||
match tcp_listener.accept().await {
|
||||
Ok((tcp_stream, sender_addr)) => match sender_addr {
|
||||
SocketAddr::V4(sender_addr) => {
|
||||
if let Some(dest_addr) = nat_map.lock().get(&sender_addr).cloned() {
|
||||
let default_interface = default_interface.clone();
|
||||
tokio::spawn(async move {
|
||||
let peer_tcp_stream =
|
||||
match tcp_connect(sender_addr.port(), dest_addr.into()).await {
|
||||
Ok(peer_tcp_stream) => peer_tcp_stream,
|
||||
Err(e) => {
|
||||
log::warn!(
|
||||
"tcp代理异常:{:?},来源:{},目标:{}",
|
||||
e,
|
||||
sender_addr,
|
||||
dest_addr
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let peer_tcp_stream = match tcp_connect(
|
||||
sender_addr.port(),
|
||||
dest_addr.into(),
|
||||
&default_interface,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(peer_tcp_stream) => peer_tcp_stream,
|
||||
Err(e) => {
|
||||
log::warn!(
|
||||
"tcp代理异常:{:?},来源:{},目标:{}",
|
||||
e,
|
||||
sender_addr,
|
||||
dest_addr
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
proxy(sender_addr, dest_addr, tcp_stream, peer_tcp_stream).await
|
||||
});
|
||||
} else {
|
||||
@@ -114,15 +121,19 @@ async fn tcp_proxy(
|
||||
}
|
||||
}
|
||||
/// 优先使用来源端口建立tcp连接
|
||||
async fn tcp_connect(src_port: u16, addr: SocketAddr) -> anyhow::Result<TcpStream> {
|
||||
let socket = TcpSocket::new_v4()?;
|
||||
async fn tcp_connect(
|
||||
src_port: u16,
|
||||
addr: SocketAddr,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<TcpStream> {
|
||||
let socket = create_tcp(true, default_interface)?;
|
||||
if socket
|
||||
.bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, src_port).into())
|
||||
.is_err()
|
||||
{
|
||||
socket.bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0).into())?;
|
||||
}
|
||||
let _ = socket.set_nodelay(false);
|
||||
let _ = socket.set_nodelay(true);
|
||||
let tcp_stream = tokio::time::timeout(Duration::from_secs(5), socket.connect(addr))
|
||||
.await
|
||||
.with_context(|| format!("TCP connection timeout {}", addr))?
|
||||
|
||||
@@ -8,11 +8,11 @@ use std::{collections::HashMap, io, net::SocketAddr};
|
||||
use parking_lot::Mutex;
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
use crate::channel::socket::{bind_udp, LocalInterface};
|
||||
use crate::ip_proxy::ProxyHandler;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
use packet::udp::udp::UdpPacket;
|
||||
|
||||
use crate::ip_proxy::ProxyHandler;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UdpProxy {
|
||||
port: u16,
|
||||
@@ -20,7 +20,7 @@ pub struct UdpProxy {
|
||||
}
|
||||
|
||||
impl UdpProxy {
|
||||
pub async fn new() -> anyhow::Result<Self> {
|
||||
pub async fn new(default_interface: LocalInterface) -> anyhow::Result<Self> {
|
||||
let nat_map: Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>> =
|
||||
Arc::new(Mutex::new(HashMap::with_capacity(16)));
|
||||
let udp = UdpSocket::bind(format!("0.0.0.0:{}", 0))
|
||||
@@ -29,8 +29,8 @@ impl UdpProxy {
|
||||
let port = udp.local_addr()?.port();
|
||||
{
|
||||
let nat_map = nat_map.clone();
|
||||
tokio::spawn(async {
|
||||
if let Err(e) = udp_proxy(udp, nat_map).await {
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = udp_proxy(udp, nat_map, default_interface).await {
|
||||
log::warn!("udp_proxy:{:?}", e);
|
||||
}
|
||||
});
|
||||
@@ -84,7 +84,8 @@ impl ProxyHandler for UdpProxy {
|
||||
async fn udp_proxy(
|
||||
udp: UdpSocket,
|
||||
nat_map: Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>>,
|
||||
) -> io::Result<()> {
|
||||
default_interface: LocalInterface,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut buf = [0u8; 65536];
|
||||
|
||||
let inner_map: Arc<Mutex<HashMap<SocketAddrV4, (Arc<UdpSocket>, Arc<AtomicCell<Instant>>)>>> =
|
||||
@@ -94,9 +95,15 @@ async fn udp_proxy(
|
||||
match udp_socket.recv_from(&mut buf).await {
|
||||
Ok((len, sender_addr)) => match sender_addr {
|
||||
SocketAddr::V4(sender_addr) => {
|
||||
if let Err(e) =
|
||||
udp_proxy0(&buf[..len], sender_addr, &inner_map, &nat_map, &udp_socket)
|
||||
.await
|
||||
if let Err(e) = udp_proxy0(
|
||||
&buf[..len],
|
||||
sender_addr,
|
||||
&inner_map,
|
||||
&nat_map,
|
||||
&udp_socket,
|
||||
&default_interface,
|
||||
)
|
||||
.await
|
||||
{
|
||||
log::warn!("udp proxy {} {:?}", sender_addr, e);
|
||||
}
|
||||
@@ -116,7 +123,8 @@ async fn udp_proxy0(
|
||||
inner_map: &Arc<Mutex<HashMap<SocketAddrV4, (Arc<UdpSocket>, Arc<AtomicCell<Instant>>)>>>,
|
||||
map: &Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>>,
|
||||
udp_socket: &Arc<UdpSocket>,
|
||||
) -> io::Result<()> {
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<()> {
|
||||
let option = inner_map.lock().get(&sender_addr).cloned();
|
||||
if let Some((udp, time)) = option {
|
||||
time.store(Instant::now());
|
||||
@@ -125,11 +133,14 @@ async fn udp_proxy0(
|
||||
let option = map.lock().get(&sender_addr).cloned();
|
||||
if let Some(dest_addr) = option {
|
||||
//先使用相同的端口,冲突了再随机端口
|
||||
let peer_udp_socket =
|
||||
match UdpSocket::bind(format!("0.0.0.0:{}", sender_addr.port())).await {
|
||||
Ok(udp) => udp,
|
||||
Err(_) => UdpSocket::bind("0.0.0.0:0").await?,
|
||||
};
|
||||
let peer_udp_socket = match bind_udp(
|
||||
format!("0.0.0.0:{}", sender_addr.port()).parse().unwrap(),
|
||||
default_interface,
|
||||
) {
|
||||
Ok(udp) => udp,
|
||||
Err(_) => bind_udp("0.0.0.0:0".parse().unwrap(), default_interface)?,
|
||||
};
|
||||
let peer_udp_socket = UdpSocket::from_std(peer_udp_socket.into())?;
|
||||
peer_udp_socket.connect(dest_addr).await?;
|
||||
peer_udp_socket.send(buf).await?;
|
||||
let peer_udp_socket = Arc::new(peer_udp_socket);
|
||||
|
||||
+30
-30
@@ -1,4 +1,4 @@
|
||||
use anyhow::Context;
|
||||
use anyhow::{anyhow, Context};
|
||||
use std::io;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, ToSocketAddrs};
|
||||
use std::net::{SocketAddr, UdpSocket};
|
||||
@@ -10,8 +10,8 @@ use parking_lot::Mutex;
|
||||
use rand::prelude::SliceRandom;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::channel::punch::{NatInfo, NatType};
|
||||
use crate::proto::message::PunchNatType;
|
||||
use crate::channel::punch::{NatInfo, NatType, PunchModel};
|
||||
use crate::channel::socket::LocalInterface;
|
||||
#[cfg(feature = "upnp")]
|
||||
use crate::util::UPnP;
|
||||
|
||||
@@ -116,40 +116,20 @@ pub struct NatTest {
|
||||
tcp_port: u16,
|
||||
#[cfg(feature = "upnp")]
|
||||
upnp: UPnP,
|
||||
}
|
||||
|
||||
impl From<NatType> for PunchNatType {
|
||||
fn from(value: NatType) -> Self {
|
||||
match value {
|
||||
NatType::Symmetric => PunchNatType::Symmetric,
|
||||
NatType::Cone => PunchNatType::Cone,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<NatType> for PunchNatType {
|
||||
fn into(self) -> NatType {
|
||||
match self {
|
||||
PunchNatType::Symmetric => NatType::Symmetric,
|
||||
PunchNatType::Cone => NatType::Cone,
|
||||
}
|
||||
}
|
||||
pub(crate) update_local_ipv4: bool,
|
||||
}
|
||||
|
||||
impl NatTest {
|
||||
pub fn new(
|
||||
_channel_num: usize,
|
||||
mut stun_server: Vec<String>,
|
||||
stun_server: Vec<String>,
|
||||
local_ipv4: Option<Ipv4Addr>,
|
||||
ipv6: Option<Ipv6Addr>,
|
||||
udp_ports: Vec<u16>,
|
||||
tcp_port: u16,
|
||||
update_local_ipv4: bool,
|
||||
punch_model: PunchModel,
|
||||
) -> NatTest {
|
||||
if stun_server.len() > 5 {
|
||||
stun_server.shuffle(&mut rand::thread_rng());
|
||||
stun_server.truncate(5);
|
||||
log::info!("stun_server truncate {:?}", stun_server);
|
||||
}
|
||||
let ports = vec![0; udp_ports.len()];
|
||||
let nat_info = NatInfo::new(
|
||||
Vec::new(),
|
||||
@@ -159,7 +139,9 @@ impl NatTest {
|
||||
ipv6,
|
||||
udp_ports.clone(),
|
||||
tcp_port,
|
||||
0,
|
||||
NatType::Cone,
|
||||
punch_model,
|
||||
);
|
||||
let info = Arc::new(Mutex::new(nat_info));
|
||||
#[cfg(feature = "upnp")]
|
||||
@@ -183,6 +165,7 @@ impl NatTest {
|
||||
tcp_port,
|
||||
#[cfg(feature = "upnp")]
|
||||
upnp,
|
||||
update_local_ipv4,
|
||||
}
|
||||
}
|
||||
pub fn can_update(&self) -> bool {
|
||||
@@ -258,17 +241,34 @@ impl NatTest {
|
||||
let mut guard = self.info.lock();
|
||||
guard.update_addr(index, ip, port)
|
||||
}
|
||||
pub fn update_tcp_port(&self, port: u16) {
|
||||
let mut guard = self.info.lock();
|
||||
guard.update_tcp_port(port)
|
||||
}
|
||||
pub fn re_test(
|
||||
&self,
|
||||
local_ipv4: Option<Ipv4Addr>,
|
||||
ipv6: Option<Ipv6Addr>,
|
||||
) -> io::Result<NatInfo> {
|
||||
let (nat_type, public_ips, port_range) = stun::stun_test_nat(self.stun_server.clone())?;
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<NatInfo> {
|
||||
let mut stun_server = self.stun_server.clone();
|
||||
if stun_server.len() > 5 {
|
||||
stun_server.shuffle(&mut rand::thread_rng());
|
||||
stun_server.truncate(5);
|
||||
log::info!("stun_server truncate {:?}", stun_server);
|
||||
}
|
||||
let (nat_type, public_ips, port_range) =
|
||||
stun::stun_test_nat(stun_server, default_interface)?;
|
||||
if public_ips.is_empty() {
|
||||
Err(anyhow!("public_ips.is_empty"))?
|
||||
}
|
||||
let mut guard = self.info.lock();
|
||||
guard.nat_type = nat_type;
|
||||
guard.public_ips = public_ips;
|
||||
guard.public_port_range = port_range;
|
||||
guard.local_ipv4 = local_ipv4;
|
||||
if local_ipv4.is_some() {
|
||||
guard.local_ipv4 = local_ipv4;
|
||||
}
|
||||
guard.ipv6 = ipv6;
|
||||
|
||||
Ok(guard.clone())
|
||||
|
||||
+13
-4
@@ -4,17 +4,21 @@ use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::channel::punch::NatType;
|
||||
use crate::channel::socket::{bind_udp, LocalInterface};
|
||||
use rand::RngCore;
|
||||
use std::net::UdpSocket;
|
||||
use stun_format::Attr;
|
||||
|
||||
pub fn stun_test_nat(stun_servers: Vec<String>) -> io::Result<(NatType, Vec<Ipv4Addr>, u16)> {
|
||||
pub fn stun_test_nat(
|
||||
stun_servers: Vec<String>,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<(NatType, Vec<Ipv4Addr>, u16)> {
|
||||
let mut nat_type = NatType::Cone;
|
||||
let mut port_range = 0;
|
||||
let mut hash_set = HashSet::new();
|
||||
for _ in 0..2 {
|
||||
let stun_servers = stun_servers.clone();
|
||||
match stun_test_nat0(stun_servers) {
|
||||
match stun_test_nat0(stun_servers, default_interface) {
|
||||
Ok((nat_type_t, ip_list_t, port_range_t)) => {
|
||||
if nat_type_t == NatType::Symmetric {
|
||||
nat_type = NatType::Symmetric;
|
||||
@@ -34,8 +38,13 @@ pub fn stun_test_nat(stun_servers: Vec<String>) -> io::Result<(NatType, Vec<Ipv4
|
||||
Ok((nat_type, hash_set.into_iter().collect(), port_range))
|
||||
}
|
||||
|
||||
pub fn stun_test_nat0(stun_servers: Vec<String>) -> io::Result<(NatType, Vec<Ipv4Addr>, u16)> {
|
||||
let udp = UdpSocket::bind("0.0.0.0:0")?;
|
||||
pub fn stun_test_nat0(
|
||||
stun_servers: Vec<String>,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<(NatType, Vec<Ipv4Addr>, u16)> {
|
||||
let udp = bind_udp("0.0.0.0:0".parse().unwrap(), default_interface)?;
|
||||
udp.set_nonblocking(false)?;
|
||||
let udp: UdpSocket = udp.into();
|
||||
udp.set_read_timeout(Some(Duration::from_millis(500)))?;
|
||||
let mut nat_type = NatType::Cone;
|
||||
let mut min_port = u16::MAX;
|
||||
|
||||
@@ -32,6 +32,7 @@ async fn tcp_mapping_(
|
||||
}
|
||||
|
||||
async fn copy(source_tcp: TcpStream, destination: &String) -> anyhow::Result<()> {
|
||||
// 或许这里也应该绑定最匹配的网卡,不然全局代理会影响映射
|
||||
let dest_tcp = TcpStream::connect(destination)
|
||||
.await
|
||||
.with_context(|| format!("TCP connection target failed {:?}", destination))?;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum Protocol {
|
||||
Ipv4,
|
||||
WGIpv4,
|
||||
Ipv4Broadcast,
|
||||
Unknown(u8),
|
||||
}
|
||||
@@ -12,16 +15,18 @@ impl From<u8> for Protocol {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
4 => Protocol::Ipv4,
|
||||
5 => Protocol::WGIpv4,
|
||||
201 => Protocol::Ipv4Broadcast,
|
||||
val => Protocol::Unknown(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Protocol {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
impl From<Protocol> for u8 {
|
||||
fn from(val: Protocol) -> Self {
|
||||
match val {
|
||||
Protocol::Ipv4 => 4,
|
||||
Protocol::WGIpv4 => 5,
|
||||
Protocol::Ipv4Broadcast => 201,
|
||||
Protocol::Unknown(val) => val,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{DeviceConfig, ErrorInfo, ErrorType};
|
||||
use crate::{DeviceConfig, ErrorInfo, ErrorType, VntCallback};
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
@@ -10,12 +10,15 @@ const DEFAULT_TUN_NAME: &str = "vnt-tun";
|
||||
#[cfg(target_os = "windows")]
|
||||
const DEFAULT_TAP_NAME: &str = "vnt-tap";
|
||||
|
||||
pub fn create_device(config: DeviceConfig) -> Result<Arc<Device>, ErrorInfo> {
|
||||
pub fn create_device<Call: VntCallback>(
|
||||
config: DeviceConfig,
|
||||
call: &Call,
|
||||
) -> Result<Arc<Device>, ErrorInfo> {
|
||||
let device = match create_device0(&config) {
|
||||
Ok(device) => device,
|
||||
Err(e) => {
|
||||
return Err(ErrorInfo::new_msg(
|
||||
ErrorType::Unknown,
|
||||
ErrorType::FailedToCrateDevice,
|
||||
format!("create device {:?}", e),
|
||||
));
|
||||
}
|
||||
@@ -44,7 +47,14 @@ pub fn create_device(config: DeviceConfig) -> Result<Arc<Device>, ErrorInfo> {
|
||||
|
||||
for (dest, mask) in config.external_route {
|
||||
if let Err(e) = device.add_route(dest, mask, 1) {
|
||||
log::warn!("添加路由失败 ={:?}", e);
|
||||
log::warn!("添加路由失败,请检查-i参数是否和现有路由冲突 ={:?}", e);
|
||||
call.error(ErrorInfo::new_msg(
|
||||
ErrorType::Warn,
|
||||
format!(
|
||||
"警告! 添加路由失败,请检查-i参数是否和现有路由冲突 ={:?}",
|
||||
e
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
Ok(device)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
@@ -67,7 +69,7 @@ struct TunDeviceHelperInner {
|
||||
ip_proxy_map: Option<IpProxyMap>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
compressor: Compressor,
|
||||
}
|
||||
|
||||
@@ -80,7 +82,7 @@ impl TunDeviceHelper {
|
||||
#[cfg(feature = "ip_proxy")] ip_proxy_map: Option<IpProxyMap>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
compressor: Compressor,
|
||||
device_adapter: DeviceAdapter,
|
||||
) -> Self {
|
||||
@@ -93,7 +95,7 @@ impl TunDeviceHelper {
|
||||
ip_proxy_map,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
device_list,
|
||||
device_map,
|
||||
compressor,
|
||||
};
|
||||
Self {
|
||||
@@ -117,7 +119,7 @@ impl TunDeviceHelper {
|
||||
}
|
||||
}
|
||||
/// 要保证先stop 再start
|
||||
pub fn start(&self, device: Arc<Device>) -> io::Result<()> {
|
||||
pub fn start(&self, device: Arc<Device>, allow_wire_guard: bool) -> io::Result<()> {
|
||||
self.device_adapter.insert(device.clone());
|
||||
let device_stop = DeviceStop::default();
|
||||
let s = self.device_stop.lock().replace(device_stop.clone());
|
||||
@@ -133,9 +135,10 @@ impl TunDeviceHelper {
|
||||
inner.ip_proxy_map,
|
||||
inner.client_cipher,
|
||||
inner.server_cipher,
|
||||
inner.device_list,
|
||||
inner.device_map,
|
||||
inner.compressor,
|
||||
device_stop,
|
||||
allow_wire_guard,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+36
-15
@@ -5,6 +5,7 @@ use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use std::{io, thread};
|
||||
|
||||
use crate::channel::socket::LocalInterface;
|
||||
use anyhow::Context;
|
||||
use dns_parser::{Builder, Packet, QueryClass, QueryType, RData, ResponseCode};
|
||||
|
||||
@@ -79,6 +80,7 @@ fn address_choose0(addrs: Vec<SocketAddr>) -> anyhow::Result<SocketAddr> {
|
||||
pub fn dns_query_all(
|
||||
domain: &str,
|
||||
mut name_servers: Vec<String>,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<Vec<SocketAddr>> {
|
||||
match SocketAddr::from_str(domain) {
|
||||
Ok(addr) => Ok(vec![addr]),
|
||||
@@ -102,7 +104,7 @@ pub fn dns_query_all(
|
||||
let mut err: Option<anyhow::Error> = None;
|
||||
for name_server in name_servers {
|
||||
if let Some(domain) = txt_domain.as_ref() {
|
||||
match txt_dns(domain, name_server) {
|
||||
match txt_dns(domain, name_server, default_interface) {
|
||||
Ok(addr) => {
|
||||
if !addr.is_empty() {
|
||||
return Ok(addr);
|
||||
@@ -127,12 +129,14 @@ pub fn dns_query_all(
|
||||
let th1 = {
|
||||
let host = host.to_string();
|
||||
let name_server = name_server.clone();
|
||||
thread::spawn(move || a_dns(host, name_server))
|
||||
let default_interface = default_interface.clone();
|
||||
thread::spawn(move || a_dns(host, name_server, &default_interface))
|
||||
};
|
||||
let th2 = {
|
||||
let host = host.to_string();
|
||||
let name_server = name_server.clone();
|
||||
thread::spawn(move || aaaa_dns(host, name_server))
|
||||
let default_interface = default_interface.clone();
|
||||
thread::spawn(move || aaaa_dns(host, name_server, &default_interface))
|
||||
};
|
||||
let mut addr = Vec::new();
|
||||
match th1.join().unwrap() {
|
||||
@@ -230,9 +234,13 @@ fn query<'a>(
|
||||
Ok(pkt)
|
||||
}
|
||||
|
||||
pub fn txt_dns(domain: &str, name_server: String) -> anyhow::Result<Vec<SocketAddr>> {
|
||||
pub fn txt_dns(
|
||||
domain: &str,
|
||||
name_server: String,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<Vec<SocketAddr>> {
|
||||
let name_server: SocketAddr = name_server.parse()?;
|
||||
let udp = bind_udp(name_server)?;
|
||||
let udp = bind_udp(name_server, default_interface)?;
|
||||
let mut buf = [0; 65536];
|
||||
let message = query(&udp, domain, name_server, QueryType::TXT, &mut buf)?;
|
||||
let mut rs = Vec::new();
|
||||
@@ -249,19 +257,28 @@ pub fn txt_dns(domain: &str, name_server: String) -> anyhow::Result<Vec<SocketAd
|
||||
Ok(rs)
|
||||
}
|
||||
|
||||
fn bind_udp(name_server: SocketAddr) -> anyhow::Result<UdpSocket> {
|
||||
let udp = if name_server.is_ipv4() {
|
||||
UdpSocket::bind("0.0.0.0:0")?
|
||||
fn bind_udp(
|
||||
name_server: SocketAddr,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<UdpSocket> {
|
||||
let addr: SocketAddr = if name_server.is_ipv4() {
|
||||
"0.0.0.0:0".parse().unwrap()
|
||||
} else {
|
||||
UdpSocket::bind("[::]:0")?
|
||||
"[::]:0".parse().unwrap()
|
||||
};
|
||||
udp.set_read_timeout(Some(Duration::from_millis(800)))?;
|
||||
Ok(udp)
|
||||
let socket = crate::channel::socket::bind_udp(addr, default_interface)?;
|
||||
socket.set_nonblocking(false)?;
|
||||
socket.set_read_timeout(Some(Duration::from_millis(800)))?;
|
||||
Ok(socket.into())
|
||||
}
|
||||
|
||||
pub fn a_dns(domain: String, name_server: String) -> anyhow::Result<Vec<Ipv4Addr>> {
|
||||
pub fn a_dns(
|
||||
domain: String,
|
||||
name_server: String,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<Vec<Ipv4Addr>> {
|
||||
let name_server: SocketAddr = name_server.parse()?;
|
||||
let udp = bind_udp(name_server)?;
|
||||
let udp = bind_udp(name_server, default_interface)?;
|
||||
let mut buf = [0; 65536];
|
||||
let message = query(&udp, &domain, name_server, QueryType::A, &mut buf)?;
|
||||
let mut rs = Vec::new();
|
||||
@@ -273,9 +290,13 @@ pub fn a_dns(domain: String, name_server: String) -> anyhow::Result<Vec<Ipv4Addr
|
||||
Ok(rs)
|
||||
}
|
||||
|
||||
pub fn aaaa_dns(domain: String, name_server: String) -> anyhow::Result<Vec<Ipv6Addr>> {
|
||||
pub fn aaaa_dns(
|
||||
domain: String,
|
||||
name_server: String,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<Vec<Ipv6Addr>> {
|
||||
let name_server: SocketAddr = name_server.parse()?;
|
||||
let udp = bind_udp(name_server)?;
|
||||
let udp = bind_udp(name_server, default_interface)?;
|
||||
let mut buf = [0; 65536];
|
||||
let message = query(&udp, &domain, name_server, QueryType::AAAA, &mut buf)?;
|
||||
let mut rs = Vec::new();
|
||||
|
||||
+5
-3
@@ -11,6 +11,7 @@ libc = "0.2.153"
|
||||
|
||||
log = { version = "0.4.20", features = [] }
|
||||
rand = "0.8.5"
|
||||
sha2 = { version = "0.10.6", features = ["oid"] }
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
|
||||
ioctl = { version = "0.8", package = "ioctl-sys" }
|
||||
@@ -18,15 +19,16 @@ ioctl = { version = "0.8", package = "ioctl-sys" }
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
libloading = "0.8.0"
|
||||
widestring = "1.0.2"
|
||||
winapi = {version = "0.3",features = [
|
||||
winapi = { version = "0.3", features = [
|
||||
"errhandlingapi",
|
||||
"libloaderapi",
|
||||
"combaseapi",
|
||||
"ioapiset",
|
||||
"winioctl",
|
||||
"setupapi",
|
||||
"synchapi",
|
||||
"netioapi",
|
||||
"fileapi","handleapi","winerror","minwindef","ifdef","basetsd","winnt","winreg","winbase","minwinbase",
|
||||
"fileapi", "handleapi", "winerror", "minwindef", "ifdef", "basetsd", "winnt", "winreg", "winbase", "minwinbase",
|
||||
"impl-default"
|
||||
]}
|
||||
] }
|
||||
|
||||
|
||||
@@ -4,7 +4,16 @@ use std::net::Ipv4Addr;
|
||||
use crate::unix::exe_cmd;
|
||||
|
||||
pub fn add_route(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
|
||||
let cmd = format!("ip route add {:?}/{:?} dev {}", address, netmask, name);
|
||||
let cmd = if netmask.is_broadcast() {
|
||||
format!("route add -host {:?} {}", address, name)
|
||||
} else {
|
||||
format!(
|
||||
"route add -net {}/{} {}",
|
||||
address,
|
||||
u32::from(netmask).count_ones(),
|
||||
name
|
||||
)
|
||||
};
|
||||
exe_cmd(&cmd)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
use libloading::Library;
|
||||
use std::ffi::{c_char, CStr, CString};
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read, Seek};
|
||||
use std::path::PathBuf;
|
||||
use winapi::shared::minwindef::HINSTANCE;
|
||||
use winapi::um::libloaderapi::{GetModuleFileNameA, GetModuleHandleA};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct DosHeader {
|
||||
e_magic: u16,
|
||||
e_cblp: u16,
|
||||
e_cp: u16,
|
||||
e_crlc: u16,
|
||||
e_cparhdr: u16,
|
||||
e_minalloc: u16,
|
||||
e_maxalloc: u16,
|
||||
e_ss: u16,
|
||||
e_sp: u16,
|
||||
e_csum: u16,
|
||||
e_ip: u16,
|
||||
e_cs: u16,
|
||||
e_lfarlc: u16,
|
||||
e_ovno: u16,
|
||||
e_res: [u16; 4],
|
||||
e_oemid: u16,
|
||||
e_oeminfo: u16,
|
||||
e_res2: [u16; 10],
|
||||
e_lfanew: i32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct FileHeader {
|
||||
machine: u16,
|
||||
number_of_sections: u16,
|
||||
time_date_stamp: u32,
|
||||
pointer_to_symbol_table: u32,
|
||||
number_of_symbols: u32,
|
||||
size_of_optional_header: u16,
|
||||
characteristics: u16,
|
||||
}
|
||||
|
||||
const IMAGE_FILE_MACHINE_I386: u16 = 0x014C;
|
||||
const IMAGE_FILE_MACHINE_AMD64: u16 = 0x8664;
|
||||
const IMAGE_FILE_MACHINE_ARM: u16 = 0x01C4;
|
||||
const IMAGE_FILE_MACHINE_ARM64: u16 = 0xAA64;
|
||||
|
||||
fn get_dll_path(dll_name: &str) -> Result<PathBuf, String> {
|
||||
unsafe {
|
||||
// 使用libloading加载DLL
|
||||
|
||||
// 转换DLL名称为C字符串
|
||||
let dll_name_c =
|
||||
CString::new(dll_name).map_err(|e| format!("Failed to convert to CString: {}", e))?;
|
||||
|
||||
// 获取DLL的模块句柄
|
||||
let h_instance: HINSTANCE = GetModuleHandleA(dll_name_c.as_ptr() as *const c_char);
|
||||
|
||||
if h_instance.is_null() {
|
||||
return Err("Failed to get module handle".to_string());
|
||||
}
|
||||
|
||||
// 获取DLL文件路径
|
||||
let mut buffer: [c_char; 260] = [0; 260];
|
||||
let length = GetModuleFileNameA(h_instance, buffer.as_mut_ptr(), buffer.len() as u32);
|
||||
|
||||
if length == 0 {
|
||||
return Err("Failed to get module file name".to_string());
|
||||
}
|
||||
|
||||
let path = CStr::from_ptr(buffer.as_ptr());
|
||||
let path_str = path
|
||||
.to_str()
|
||||
.map_err(|e| format!("Failed to convert to &str: {}", e))?;
|
||||
Ok(PathBuf::from(path_str))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_win_tun_dll() -> io::Result<()> {
|
||||
let _lib = unsafe {
|
||||
Library::new("wintun.dll").map_err(|_| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"wintun.dll not found,Please download https://www.wintun.net",
|
||||
)
|
||||
})
|
||||
};
|
||||
match get_dll_path("wintun.dll") {
|
||||
Ok(path) => match_platform(path),
|
||||
Err(e) => {
|
||||
// 能加载说明存在wintun,这里获取不到路径是代码的问题
|
||||
log::info!("{:?}", e);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn match_platform(path: PathBuf) -> io::Result<()> {
|
||||
let current_arch = if cfg!(target_arch = "x86") {
|
||||
"x86"
|
||||
} else if cfg!(target_arch = "x86_64") {
|
||||
"AMD64"
|
||||
} else if cfg!(target_arch = "arm") {
|
||||
"ARM"
|
||||
} else if cfg!(target_arch = "aarch64") {
|
||||
"ARM64"
|
||||
} else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let mut file = File::open(&path)?;
|
||||
|
||||
// 读取 DOS 头部
|
||||
let mut dos_header = [0u8; std::mem::size_of::<DosHeader>()];
|
||||
file.read_exact(&mut dos_header)?;
|
||||
let dos_header: DosHeader = unsafe { std::ptr::read(dos_header.as_ptr() as *const _) };
|
||||
|
||||
if dos_header.e_magic != 0x5A4D {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("Not a valid PE file {:?}", path),
|
||||
));
|
||||
}
|
||||
|
||||
// 跳转到 PE 头部
|
||||
file.seek(io::SeekFrom::Start(dos_header.e_lfanew as u64))?;
|
||||
|
||||
// 读取 PE 头部
|
||||
let mut pe_signature = [0u8; 4];
|
||||
file.read_exact(&mut pe_signature)?;
|
||||
if &pe_signature != b"PE\0\0" {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("Not a valid PE file {:?}", path),
|
||||
));
|
||||
}
|
||||
|
||||
// 读取文件头部
|
||||
let mut file_header = [0u8; std::mem::size_of::<FileHeader>()];
|
||||
file.read_exact(&mut file_header)?;
|
||||
let file_header: FileHeader = unsafe { std::ptr::read(file_header.as_ptr() as *const _) };
|
||||
let dll_arch = match file_header.machine {
|
||||
IMAGE_FILE_MACHINE_I386 => "x86",
|
||||
IMAGE_FILE_MACHINE_AMD64 => "AMD64",
|
||||
IMAGE_FILE_MACHINE_ARM => "ARM",
|
||||
IMAGE_FILE_MACHINE_ARM64 => "ARM64",
|
||||
_ => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("Unknown machine type: {}", file_header.machine),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
if dll_arch != current_arch {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!(
|
||||
"wintun.dll architecture ({}) does not match the current platform architecture ({}).",
|
||||
dll_arch, current_arch
|
||||
),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -16,6 +16,9 @@ impl Device {
|
||||
Ok(Device::Tun(tun::Device::new(name)?))
|
||||
}
|
||||
}
|
||||
pub fn check_tun_dll() -> io::Result<()> {
|
||||
crate::windows::check::check_win_tun_dll()
|
||||
}
|
||||
}
|
||||
|
||||
impl IFace for Device {
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::os::windows::process::CommandExt;
|
||||
use winapi::shared::minwindef::DWORD;
|
||||
use winapi::um::winbase::CREATE_NO_WINDOW;
|
||||
|
||||
mod check;
|
||||
mod device;
|
||||
mod ffi;
|
||||
mod netsh;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#![allow(dead_code)]
|
||||
use libloading::Library;
|
||||
use sha2::Digest;
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use rand::Rng;
|
||||
use winapi::um::winbase;
|
||||
use winapi::um::{synchapi, winnt};
|
||||
|
||||
@@ -81,8 +80,7 @@ impl Device {
|
||||
if Self::delete_for_name(&win_tun, &name_utf16).is_ok() {
|
||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
}
|
||||
let mut guid_bytes: [u8; 16] = [0u8; 16];
|
||||
rand::thread_rng().fill(&mut guid_bytes);
|
||||
let guid_bytes: [u8; 16] = hash_guid(&name);
|
||||
let guid = u128::from_ne_bytes(guid_bytes);
|
||||
//SAFETY: guid is a unique integer so transmuting either all zeroes or the user's preferred
|
||||
//guid to the winapi guid type is safe and will allow the windows kernel to see our GUID
|
||||
@@ -154,7 +152,15 @@ impl Device {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn hash_guid(input: &str) -> [u8; 16] {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(input.as_bytes());
|
||||
hasher.update(b"VNT");
|
||||
hasher.update(input.as_bytes());
|
||||
hasher.update(b"2024");
|
||||
let hash: [u8; 32] = hasher.finalize().into();
|
||||
hash[..16].try_into().unwrap()
|
||||
}
|
||||
impl IFace for Device {
|
||||
fn version(&self) -> io::Result<String> {
|
||||
let version = unsafe { self.win_tun.WintunGetRunningDriverVersion() };
|
||||
|
||||
Reference in New Issue
Block a user