Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12393f00c5 | ||
|
|
ef4bc1e1e3 | ||
|
|
a1621bcfdd | ||
|
|
47220fe38b | ||
|
|
d3e8ee2a32 | ||
|
|
8e303e93f8 | ||
|
|
471aa5e6ea | ||
|
|
a4c6668760 | ||
|
|
dfaff2c327 | ||
|
|
57fe6986b0 | ||
|
|
6639f40d70 | ||
|
|
d827fd108d | ||
|
|
4daeeaab1a | ||
|
|
2275620060 | ||
|
|
29faf4a950 |
10
.gitignore
vendored
@@ -1,10 +1,8 @@
|
||||
__debug_bin
|
||||
__debug_bin.exe
|
||||
# .vscode
|
||||
test/
|
||||
openp2p.exe*
|
||||
*.log*
|
||||
go.sum
|
||||
*.tar.gz
|
||||
*.zip
|
||||
*.exe
|
||||
@@ -21,4 +19,10 @@ wintun.dll
|
||||
app/.idea/
|
||||
*_debug_bin*
|
||||
cmd/openp2p
|
||||
vendor/
|
||||
vendor/
|
||||
config.json
|
||||
openp2p
|
||||
lib/openp2p.dll
|
||||
cmd/config.json0
|
||||
test/docker/Dockerfile
|
||||
test/docker/get-client.sh
|
||||
|
||||
60
Changelog.md
Normal file
@@ -0,0 +1,60 @@
|
||||
ChangeLog
|
||||
|
||||
v3.25.8更新 (2026.3.13)
|
||||
Feature
|
||||
1. web控制台可以修改公网监听端口
|
||||
1. 可以修改虚拟网络网段
|
||||
1. 回滚至go1.20因为需要支持老版本的macos和windows
|
||||
|
||||
Issue
|
||||
1. 修复客户端重装后强制v6连接失效bug
|
||||
|
||||
|
||||
v3.25.4更新 (2026.2.9)
|
||||
Feature
|
||||
1. 优化websocket读数据卡死问题
|
||||
1. 优化睡眠唤醒客户端恢复慢问题
|
||||
|
||||
Issue
|
||||
1. 修复获取ifconfig异常
|
||||
1. 修复数据同步异常导致设备间连接失败
|
||||
1. 修复底层连接潜在发送数据不完整问题
|
||||
|
||||
v3.24.33更新 (2025.12.10)
|
||||
Feature
|
||||
1. 安装和升级下载文件到临时目录
|
||||
1. openwrt默认100k日志文件
|
||||
1. 使用系统dns失败时将使用223.5.5.5和8.8.8.8,安卓和部分系统有dns问题
|
||||
1. IPv6刷新时上报到服务器
|
||||
1. 设备间连接可以设置强制使用v6
|
||||
1. 编译环境使用go1.25
|
||||
|
||||
Issue
|
||||
1. 修复加载系统证书池bug
|
||||
1. 修复初始化时崩溃
|
||||
1. 修复安卓处理多个网络资源bug
|
||||
1. 修复端口转发编辑目标设备bug
|
||||
1. 修复某些特殊情况IPv6直连失败
|
||||
|
||||
v3.24.23更新 (2025.9.4)
|
||||
Feature
|
||||
1. 公网UDP直连
|
||||
1. upnp定期续期
|
||||
1. 支持边缘服务器
|
||||
1. 优化mp分配算法
|
||||
1. 公网IP不变不再检测nat类型
|
||||
|
||||
Issue
|
||||
1. 修复客户端某些特殊情况卡死bug
|
||||
1. 修复wintun mtu不生效bug,增加缓冲区大小
|
||||
1. 修复广播bug
|
||||
1. 修复配置文件清空bug
|
||||
|
||||
v3.24.13更新 (2025.6.4)
|
||||
Feature
|
||||
1. 虚拟网卡状态上报
|
||||
1. OpenWrt自动安装tun
|
||||
1. docker容器运行路径改为/usr/local/openp2p/
|
||||
|
||||
Issue
|
||||
1. 优化客户端卡死问题
|
||||
40
LICENSE
@@ -1,21 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 OpenP2P.cn
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 OpenP2P.cn
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
22
README-ZH.md
@@ -19,7 +19,7 @@
|
||||
|
||||
[查看详细](#安全性)
|
||||
### 4. 轻量
|
||||
文件大小2MB+,运行内存2MB+;它可以仅跑在应用层,或者配合wintun驱动使用组网功能
|
||||
文件大小不到10MB,cpu占用极低;它可以仅跑在应用层,或者配合kmod-tun/wintun驱动使用组网功能
|
||||
### 5. 跨平台
|
||||
因为轻量,所以很容易支持各个平台。支持主流的操作系统:Windows,Linux,MacOS;和主流的cpu架构:386、amd64、arm、arm64、mipsle、mipsle64、mips、mips64、s390x、ppc64le
|
||||
### 6. 高效
|
||||
@@ -33,11 +33,11 @@ P2P直连可以让你的设备跑满带宽。不论你的设备在任何网络
|
||||
下面是一个远程办公例子:在家里连入办公室Windows电脑。
|
||||
(另外一个快速入门视频 <https://www.bilibili.com/video/BV1Et4y1P7bF/>)
|
||||
### 1.注册
|
||||
前往<https://console.openp2p.cn> 注册新用户,暂无需任何认证
|
||||
前往<https://console.openp2p.cn> 使用邮箱注册新用户,暂无需任何认证
|
||||
|
||||

|
||||
### 2.安装
|
||||
分别在本地和远程电脑下载后双击运行,一键安装
|
||||
分别在本地和远程电脑下载后双击运行,一键安装(如果是windows用户,在浏览器下载后请勿修改文件名!!!)
|
||||
|
||||

|
||||
|
||||
@@ -46,7 +46,7 @@ Windows默认会阻止没有花钱买它家证书签名过的程序,选择“
|
||||

|
||||
|
||||

|
||||
### 3.新建P2P应用
|
||||
### 3.新建端口转发(P2PApp)
|
||||
|
||||

|
||||
|
||||
@@ -54,12 +54,12 @@ Windows默认会阻止没有花钱买它家证书签名过的程序,选择“
|
||||
|
||||

|
||||
|
||||
### 4.使用P2P应用
|
||||
在“MyHomePC”设备上能看到刚才创建的P2P应用,连接下图显示的“本地监听端口”即可。
|
||||
### 4.使用端口转发(P2PApp)
|
||||
在“MyHomePC2”设备上能看到刚才创建的端口转发(P2PApp),连接下图显示的“本地监听端口”即可。
|
||||
|
||||

|
||||
|
||||
在家里Windows电脑,按Win+R输入mstsc打开远程桌面,输入127.0.0.1:23389 /admin
|
||||
在MyHomePC2电脑上,按Win+R输入mstsc打开远程桌面,输入127.0.0.1:23389 /admin
|
||||
|
||||

|
||||
|
||||
@@ -82,8 +82,8 @@ Windows默认会阻止没有花钱买它家证书签名过的程序,选择“
|
||||

|
||||
### 客户端架构
|
||||

|
||||
### P2PApp
|
||||
它是项目里最重要的概念,一个P2PApp就是把远程的一个服务(mstsc/ssh等)通过P2P网络映射到本地监听。二次开发或者我们提供的Restful API,主要工作就是管理P2PApp
|
||||
### 端口转发(P2PApp)
|
||||
它是项目里最重要的概念,一个端口转发(P2PApp)就是把远程的一个服务(mstsc/ssh等)通过P2P网络映射到本地监听。二次开发或者我们提供的Restful API,主要工作就是管理端口转发(P2PApp)
|
||||

|
||||
## 安全性
|
||||
加入OpenP2P共享网络的节点,只能凭授权访问。共享节点只会中转数据,别人无法访问内网任何资源。
|
||||
@@ -123,8 +123,8 @@ CGO_ENABLED=0 env GOOS=linux GOARCH=amd64 go build -o openp2p --ldflags '-s -w '
|
||||
1. ~~支持IPv6~~(100%)
|
||||
2. ~~支持随系统自动启动,安装成系统服务~~(100%)
|
||||
3. ~~提供一些免费服务器给特别差的网络,如广电网络~~(100%)
|
||||
4. ~~建立网站,用户可以在网站管理所有P2PApp和设备。查看设备在线状态,升级,增删查改重启P2PApp等~~(100%)
|
||||
5. 建立公众号,用户可在微信公众号管理所有P2PApp和设备
|
||||
4. ~~建立网站,用户可以在网站管理所有端口转发(P2PApp)和设备。查看设备在线状态,升级,增删查改重启端口转发(P2PApp)等~~(100%)
|
||||
5. 建立公众号,用户可在微信公众号管理所有端口转发(P2PApp)和设备
|
||||
6. 客户端提供WebUI
|
||||
7. ~~支持自有服务器,开源服务器程序~~(100%)
|
||||
8. 共享节点调度模型优化,对不同的运营商优化
|
||||
|
||||
24
README.md
@@ -19,7 +19,7 @@ The code is open source, the P2P tunnel uses TLS1.3+AES double encryption, and t
|
||||
[details](#Safety)
|
||||
|
||||
### 4. Lightweight
|
||||
2MB+ filesize, 2MB+ memory. It could only runs at application layer, or uses wintun driver for SDWAN.
|
||||
10MB filesize, Extremely low CPU usage. It could only runs at application layer, or uses kmod-tun/wintun driver for SDWAN.
|
||||
|
||||
### 5. Cross-platform
|
||||
Benefit from lightweight, it easily supports most of major OS, like Windows, Linux, MacOS, also most of CPU architecture, like 386、amd64、arm、arm64、mipsle、mipsle64、mips、mips64、s390x、ppc64le.
|
||||
@@ -35,11 +35,11 @@ Just 4 simple steps to use.
|
||||
Here's an example of remote work: connecting to an office Windows computer at home.
|
||||
(Another quick started vedio https://www.bilibili.com/video/BV1Et4y1P7bF/)
|
||||
### 1.Register
|
||||
Go to <https://console.openp2p.cn> register a new user
|
||||
Go to <https://console.openp2p.cn> register a new user using email
|
||||
|
||||

|
||||
### 2.Install
|
||||
Download on local and remote computers and double-click to run, one-click installation
|
||||
Download on local and remote computers and double-click to run, one-click installation (Windows user, please do not modify the file name after downloading in the browser!!!)
|
||||
|
||||

|
||||
|
||||
@@ -49,7 +49,7 @@ By default, Windows will block programs that have not been signed by the Microso
|
||||
|
||||

|
||||
|
||||
### 3.New P2PApp
|
||||
### 3.New Port ForWard (P2PApp)
|
||||
|
||||

|
||||
|
||||
@@ -57,12 +57,12 @@ By default, Windows will block programs that have not been signed by the Microso
|
||||
|
||||

|
||||
|
||||
### 4.Use P2PApp
|
||||
You can see the P2P application you just created on the "MyHomePC" device, just connect to the "local listening port" shown in the figure below.
|
||||
### 4.Use Port ForWard (P2PApp)
|
||||
You can see the P2P application you just created on the "MyHomePC2" device, just connect to the "local listening port" shown in the figure below.
|
||||
|
||||

|
||||
|
||||
On MyHomePC, press Win+R and enter MSTSC to open the remote desktop, input `127.0.0.1:23389 /admin`
|
||||
On MyHomePC2, press Win+R and enter MSTSC to open the remote desktop, input `127.0.0.1:23389 /admin`
|
||||
|
||||

|
||||
|
||||
@@ -86,8 +86,8 @@ Especially suitable for large traffic intranet access.
|
||||

|
||||
### Client architecture
|
||||

|
||||
### P2PApp
|
||||
P2PAPP is the most import concept in this project, one P2PApp is able to map the remote service(mstsc/ssh) to the local listening. The main job of re-development or restful API we provide is to manage P2PApp.
|
||||
### Port ForWard (P2PApp)
|
||||
Port ForWard (P2PApp) is the most import concept in this project, one Port ForWard (P2PApp) is able to map the remote service(mstsc/ssh) to the local listening. The main job of re-development or restful API we provide is to manage Port ForWard (P2PApp).
|
||||
|
||||

|
||||
|
||||
@@ -131,8 +131,8 @@ Short-Term:
|
||||
1. ~~Support IPv6.~~(100%)
|
||||
2. ~~Support auto run when system boot, setup system service.~~(100%)
|
||||
3. ~~Provide free servers to some low-performance network.~~(100%)
|
||||
4. ~~Build website, users can manage all P2PApp and devices via it. View devices' online status, upgrade, restart or CURD P2PApp .~~(100%)
|
||||
5. Provide wechat official account, user can manage P2PApp nodes and deivce as same as website.
|
||||
4. ~~Build website, users can manage all Port ForWard (P2PApp) and devices via it. View devices' online status, upgrade, restart or CURD Port ForWard (P2PApp) .~~(100%)
|
||||
5. Provide wechat official account, user can manage Port ForWard (P2PApp) nodes and deivce as same as website.
|
||||
6. Provide WebUI on client side.
|
||||
7. ~~Support private server, open source server program.~~(100%)
|
||||
8. Optimize our share scheduling model for different network operators.
|
||||
@@ -162,3 +162,5 @@ Email: openp2p.cn@gmail.com tenderiron@139.com
|
||||
## Disclaimer
|
||||
This project is open source for everyone to learn and use for free. It is forbidden to be used for illegal purposes. Any loss caused by improper use of this project or accident, this project and related personnel will not bear any responsibility.
|
||||
|
||||
## Thanks
|
||||
[](https://dartnode.com "Powered by DartNode - Free VPS for Open Source")
|
||||
@@ -4,7 +4,6 @@ import android.app.*
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import java.io.IOException
|
||||
import android.net.VpnService
|
||||
import android.os.Binder
|
||||
import android.os.Build
|
||||
@@ -22,6 +21,13 @@ import java.io.FileOutputStream
|
||||
import java.nio.ByteBuffer
|
||||
import kotlinx.coroutines.*
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
import java.net.InetAddress
|
||||
import java.net.NetworkInterface
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import java.nio.channels.FileChannel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
data class Node(val name: String, val ip: String, val resource: String? = null)
|
||||
|
||||
@@ -32,9 +38,10 @@ data class Network(
|
||||
val gateway: String,
|
||||
val Nodes: List<Node>
|
||||
)
|
||||
|
||||
class OpenP2PService : VpnService() {
|
||||
companion object {
|
||||
private val LOG_TAG = OpenP2PService::class.simpleName
|
||||
private val LOG_TAG = "OpenP2PService"
|
||||
}
|
||||
|
||||
inner class LocalBinder : Binder() {
|
||||
@@ -44,12 +51,17 @@ class OpenP2PService : VpnService() {
|
||||
private val binder = LocalBinder()
|
||||
private lateinit var network: openp2p.P2PNetwork
|
||||
private lateinit var mToken: String
|
||||
private var running:Boolean =true
|
||||
private var sdwanRunning:Boolean =false
|
||||
private var running: Boolean = true
|
||||
private var sdwanRunning: Boolean = false
|
||||
private var vpnInterface: ParcelFileDescriptor? = null
|
||||
private var sdwanJob: Job? = null
|
||||
private var sdwanJob: Job? = null
|
||||
private val packetQueue = Channel<ByteBuffer>(capacity = 1024)
|
||||
private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
||||
override fun onCreate() {
|
||||
Log.i(LOG_TAG, "onCreate - Thread ID = " + Thread.currentThread().id)
|
||||
val logDir = File(getExternalFilesDir(null), "log")
|
||||
Logger.init(logDir)
|
||||
Logger.i(LOG_TAG, "onCreate - Thread ID = " + Thread.currentThread().id)
|
||||
var channelId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
createNotificationChannel("kim.hsl", "ForegroundService")
|
||||
} else {
|
||||
@@ -64,7 +76,7 @@ class OpenP2PService : VpnService() {
|
||||
|
||||
val notification = channelId?.let {
|
||||
NotificationCompat.Builder(this, it)
|
||||
// .setSmallIcon(R.mipmap.app_icon)
|
||||
// .setSmallIcon(R.mipmap.app_icon)
|
||||
.setContentTitle("My Awesome App")
|
||||
.setContentText("Doing some work...")
|
||||
.setContentIntent(pendingIntent).build()
|
||||
@@ -76,7 +88,7 @@ class OpenP2PService : VpnService() {
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
Log.i(
|
||||
Logger.i(
|
||||
LOG_TAG,
|
||||
"onStartCommand - startId = " + startId + ", Thread ID = " + Thread.currentThread().id
|
||||
)
|
||||
@@ -86,20 +98,20 @@ class OpenP2PService : VpnService() {
|
||||
|
||||
override fun onBind(p0: Intent?): IBinder? {
|
||||
val token = p0?.getStringExtra("token")
|
||||
Log.i(LOG_TAG, "onBind token=$token")
|
||||
Logger.i(LOG_TAG, "onBind token=$token")
|
||||
startOpenP2P(token)
|
||||
return binder
|
||||
}
|
||||
|
||||
private fun startOpenP2P(token : String?): Boolean {
|
||||
private fun startOpenP2P(token: String?): Boolean {
|
||||
if (sdwanRunning) {
|
||||
return true
|
||||
}
|
||||
|
||||
Log.i(LOG_TAG, "startOpenP2P - Thread ID = " + Thread.currentThread().id + token)
|
||||
Logger.i(LOG_TAG, "startOpenP2P - Thread ID = " + Thread.currentThread().id + token)
|
||||
val oldToken = Openp2p.getToken(getExternalFilesDir(null).toString())
|
||||
Log.i(LOG_TAG, "startOpenP2P oldtoken=$oldToken newtoken=$token")
|
||||
if (oldToken=="0" && token==null){
|
||||
Logger.i(LOG_TAG, "startOpenP2P oldtoken=$oldToken newtoken=$token")
|
||||
if (oldToken == "0" && token == null) {
|
||||
return false
|
||||
}
|
||||
sdwanRunning = true
|
||||
@@ -112,7 +124,7 @@ class OpenP2PService : VpnService() {
|
||||
1
|
||||
) // /storage/emulated/0/Android/data/cn.openp2p/files/
|
||||
val isConnect = network.connect(30000) // ms
|
||||
Log.i(LOG_TAG, "login result: " + isConnect.toString());
|
||||
Logger.i(LOG_TAG, "login result: " + isConnect.toString());
|
||||
do {
|
||||
Thread.sleep(1000)
|
||||
} while (network.connect(30000) && running)
|
||||
@@ -123,152 +135,257 @@ class OpenP2PService : VpnService() {
|
||||
|
||||
private fun refreshSDWAN() {
|
||||
GlobalScope.launch {
|
||||
Log.i(OpenP2PService.LOG_TAG, "refreshSDWAN start");
|
||||
Logger.i(OpenP2PService.LOG_TAG, "refreshSDWAN start");
|
||||
while (true) {
|
||||
Log.i(OpenP2PService.LOG_TAG, "waiting new sdwan config");
|
||||
val buf = ByteArray(4096)
|
||||
Logger.i(OpenP2PService.LOG_TAG, "waiting new sdwan config");
|
||||
val buf = ByteArray(32 * 1024)
|
||||
val buffLen = Openp2p.getAndroidSDWANConfig(buf)
|
||||
Log.i(OpenP2PService.LOG_TAG, "closing running sdwan instance");
|
||||
Logger.i(OpenP2PService.LOG_TAG, "closing running sdwan instance");
|
||||
sdwanRunning = false
|
||||
vpnInterface?.close()
|
||||
vpnInterface = null
|
||||
Thread.sleep(10000)
|
||||
runSDWAN(buf.copyOfRange(0,buffLen.toInt() ))
|
||||
sdwanJob?.join()
|
||||
sdwanJob = serviceScope.launch(context = Dispatchers.IO) {
|
||||
runSDWAN(buf.copyOfRange(0, buffLen.toInt()))
|
||||
}
|
||||
}
|
||||
Log.i(OpenP2PService.LOG_TAG, "refreshSDWAN end");
|
||||
Logger.i(OpenP2PService.LOG_TAG, "refreshSDWAN end");
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun readTunLoop() {
|
||||
val inputStream = FileInputStream(vpnInterface?.fileDescriptor).channel
|
||||
if (inputStream==null){
|
||||
Log.i(OpenP2PService.LOG_TAG, "open FileInputStream error: ");
|
||||
if (inputStream == null) {
|
||||
Logger.i(OpenP2PService.LOG_TAG, "open FileInputStream error: ");
|
||||
return
|
||||
}
|
||||
Log.d(LOG_TAG, "read tun loop start")
|
||||
Logger.i(LOG_TAG, "read tun loop start")
|
||||
val buffer = ByteBuffer.allocate(4096)
|
||||
val byteArrayRead = ByteArray(4096)
|
||||
while (sdwanRunning) {
|
||||
buffer.clear()
|
||||
val readBytes = inputStream.read(buffer)
|
||||
if (readBytes <= 0) {
|
||||
// Log.i(OpenP2PService.LOG_TAG, "inputStream.read error: ")
|
||||
delay(1)
|
||||
continue
|
||||
withContext(Dispatchers.IO) {
|
||||
val readBytes = inputStream.read(buffer)
|
||||
if (readBytes > 0) {
|
||||
buffer.flip()
|
||||
buffer.get(byteArrayRead, 0, readBytes)
|
||||
// Logger.i(OpenP2PService.LOG_TAG, String.format("Openp2p.androidRead: %d", readBytes))
|
||||
Openp2p.androidRead(byteArrayRead, readBytes.toLong())
|
||||
// Logger.i(OpenP2PService.LOG_TAG, "inputStream.read error: ")
|
||||
} else {
|
||||
delay(50)
|
||||
}
|
||||
}
|
||||
buffer.flip()
|
||||
buffer.get(byteArrayRead,0,readBytes)
|
||||
Log.i(OpenP2PService.LOG_TAG, String.format("Openp2p.androidRead: %d", readBytes))
|
||||
Openp2p.androidRead(byteArrayRead, readBytes.toLong())
|
||||
|
||||
}
|
||||
Log.d(LOG_TAG, "read tun loop end")
|
||||
Logger.i(LOG_TAG, "read tun loop end")
|
||||
}
|
||||
|
||||
private fun runSDWAN(buf:ByteArray) {
|
||||
sdwanRunning=true
|
||||
sdwanJob=GlobalScope.launch(context = Dispatchers.IO) {
|
||||
Log.i(OpenP2PService.LOG_TAG, "runSDWAN start:${buf.decodeToString()}");
|
||||
try{
|
||||
var builder = Builder()
|
||||
val jsonObject = JSONObject(buf.decodeToString())
|
||||
val id = jsonObject.getLong("id")
|
||||
val name = jsonObject.getString("name")
|
||||
val gateway = jsonObject.getString("gateway")
|
||||
val nodesArray = jsonObject.getJSONArray("Nodes")
|
||||
|
||||
val nodesList = mutableListOf<JSONObject>()
|
||||
for (i in 0 until nodesArray.length()) {
|
||||
nodesList.add(nodesArray.getJSONObject(i))
|
||||
}
|
||||
private suspend fun runSDWAN(buf: ByteArray) {
|
||||
// val localIps = listOf(
|
||||
// "fe80::14b6:a0ff:fe3e:64de" to 64,
|
||||
// "192.168.100.184" to 24,
|
||||
// "10.93.158.91" to 32,
|
||||
// "192.168.3.66" to 24
|
||||
// )
|
||||
//
|
||||
// // 测试用例
|
||||
// val testCases = listOf(
|
||||
// "192.168.3.11" to true,
|
||||
// "192.168.100.1" to true,
|
||||
// "192.168.101.1" to false,
|
||||
// "10.93.158.91" to true,
|
||||
// "10.93.158.90" to false,
|
||||
// "fe80::14b6:a0ff:fe3e:64de" to true,
|
||||
// "fe80::14b6:a0ff:fe3e:64dd" to true // 在同一子网
|
||||
// )
|
||||
//
|
||||
// for ((ip, expected) in testCases) {
|
||||
// val result = isSameSubnet(ip, localIps)
|
||||
// println("Testing IP: $ip, Expected: $expected, Result: $result")
|
||||
// }
|
||||
sdwanRunning = true
|
||||
|
||||
val myNodeName = Openp2p.getAndroidNodeName()
|
||||
Log.i(OpenP2PService.LOG_TAG, "getAndroidNodeName:${myNodeName}");
|
||||
val nodeList = nodesList.map {
|
||||
val nodeName = it.getString("name")
|
||||
val nodeIp = it.getString("ip")
|
||||
if (nodeName==myNodeName){
|
||||
Logger.i(OpenP2PService.LOG_TAG, "runSDWAN start:${buf.decodeToString()}");
|
||||
try {
|
||||
var builder = Builder()
|
||||
val jsonObject = JSONObject(buf.decodeToString())
|
||||
// debug sdwan info
|
||||
// val jsonObject = JSONObject("""{"id":2817104318517097000,"name":"network1","gateway":"10.2.3.254/24","mode":"central","centralNode":"nanjin-192-168-0-82","enable":1,"tunnelNum":3,"mtu":1420,"Nodes":[{"name":"192-168-24-15","ip":"10.2.3.5"},{"name":"Alpine Linux-172.16","ip":"10.2.3.14","resource":"172.16.0.0/24"},{"name":"ctdeMacBook-Pro.local","ip":"10.2.3.22"},{"name":"dengjiandeMBP.sh.chaitin.net","ip":"10.2.3.32"},{"name":"DESKTOP-WIN11-ARM-self","ip":"10.2.3.19"},{"name":"eastdeMBP.sh.chaitin.net","ip":"10.2.3.3"},{"name":"FN-NAS-HP","ip":"10.2.3.1","resource":"192.168.100.0/24"},{"name":"huangruideMBP.sh.chaitin.net","ip":"10.2.3.30"},{"name":"iStoreOS-virtual-machine","ip":"10.2.3.12"},{"name":"k30s-redmi-10.2.33","ip":"10.2.3.27"},{"name":"lincheng-MacBook-Pro-3.sh.chaitin.net","ip":"10.2.3.15"},{"name":"localhost-mi-13","ip":"10.2.3.8"},{"name":"localhost-华为matepad11","ip":"10.2.3.13"},{"name":"luzhanwendeMacBook-Pro.local","ip":"10.2.3.17"},{"name":"Mi-pad2-local","ip":"10.2.3.9"},{"name":"nanjin-192-168-0-82","ip":"10.2.3.34"},{"name":"R7000P-2021","ip":"10.2.3.7"},{"name":"tanxiaolongsMBP.sh.chaitin.net","ip":"10.2.3.20"},{"name":"TUF-AX3000_V2-3804","ip":"10.2.3.25"},{"name":"WIN-CYZ-10.2.3.16","ip":"10.2.3.16"},{"name":"WODOUYAO","ip":"10.2.3.4"},{"name":"Zstrack01","ip":"10.2.3.51","resource":"192.168.24.0/22,192.168.20.0/24"},{"name":"小米14-localhost","ip":"10.2.3.23"}]}""")
|
||||
val id = jsonObject.getLong("id")
|
||||
val mtu = jsonObject.getInt("mtu")
|
||||
val name = jsonObject.getString("name")
|
||||
val gateway = jsonObject.getString("gateway")
|
||||
val nodesArray = jsonObject.getJSONArray("Nodes")
|
||||
|
||||
val nodesList = mutableListOf<JSONObject>()
|
||||
for (i in 0 until nodesArray.length()) {
|
||||
nodesList.add(nodesArray.getJSONObject(i))
|
||||
}
|
||||
|
||||
val myNodeName = Openp2p.getAndroidNodeName()
|
||||
// 使用本地 IP 和子网判断是否需要添加路由
|
||||
val localIps = getLocalIpAndSubnet()
|
||||
Logger.i(OpenP2PService.LOG_TAG, "getAndroidNodeName:${myNodeName}");
|
||||
val nodeList = nodesList.map {
|
||||
val nodeName = it.getString("name")
|
||||
val nodeIp = it.getString("ip")
|
||||
if (nodeName == myNodeName) {
|
||||
try {
|
||||
Logger.i(LOG_TAG, "Attempting to add address: $nodeIp/24")
|
||||
builder.addAddress(nodeIp, 24)
|
||||
Logger.i(LOG_TAG, "Successfully added address")
|
||||
} catch (e: Exception) {
|
||||
Logger.e(LOG_TAG, "Failed to add address $nodeIp: ${e.message}")
|
||||
throw e // or handle gracefully
|
||||
}
|
||||
val nodeResource = if (it.has("resource")) it.getString("resource") else null
|
||||
val parts = nodeResource?.split("/")
|
||||
if (parts?.size == 2) {
|
||||
val ipAddress = parts[0]
|
||||
val subnetMask = parts[1]
|
||||
builder.addRoute(ipAddress, subnetMask.toInt())
|
||||
Log.i(OpenP2PService.LOG_TAG, "sdwan addRoute:${ipAddress},${subnetMask.toInt()}");
|
||||
}
|
||||
val nodeResource = it.optString("resource", null)
|
||||
if (!nodeResource.isNullOrEmpty()) {
|
||||
// 可能是多个网段,用逗号分隔
|
||||
val resourceList = nodeResource.split(",")
|
||||
for (resource in resourceList) {
|
||||
val parts = resource.split("/")
|
||||
if (parts.size == 2) {
|
||||
val ipAddress = parts[0].trim()
|
||||
val subnetMask = parts[1].trim()
|
||||
// 判断是否属于本机网段
|
||||
if (!isSameSubnet(ipAddress, localIps)) {
|
||||
builder.addRoute(ipAddress, subnetMask.toInt())
|
||||
Logger.i(
|
||||
OpenP2PService.LOG_TAG,
|
||||
"sdwan addRoute:${ipAddress},${subnetMask}"
|
||||
)
|
||||
} else {
|
||||
Logger.i(
|
||||
OpenP2PService.LOG_TAG,
|
||||
"Skipped adding route for ${ipAddress}, already in local subnet"
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Logger.w(OpenP2PService.LOG_TAG, "Invalid resource format: $resource")
|
||||
}
|
||||
}
|
||||
Node(nodeName, nodeIp, nodeResource)
|
||||
}
|
||||
|
||||
val network = Network(id, name, gateway, nodeList)
|
||||
println(network)
|
||||
Log.i(OpenP2PService.LOG_TAG, "onBind");
|
||||
builder.addDnsServer("223.5.5.5")
|
||||
builder.addDnsServer("2400:3200::1") // alicloud dns v6 & v4
|
||||
builder.addRoute("10.2.3.0", 24)
|
||||
// builder.addRoute("0.0.0.0", 0);
|
||||
builder.setSession(LOG_TAG!!)
|
||||
builder.setMtu(1420)
|
||||
vpnInterface = builder.establish()
|
||||
if (vpnInterface==null){
|
||||
Log.e(OpenP2PService.LOG_TAG, "start vpnservice error: ");
|
||||
}
|
||||
val outputStream = FileOutputStream(vpnInterface?.fileDescriptor).channel
|
||||
if (outputStream==null){
|
||||
Log.e(OpenP2PService.LOG_TAG, "open FileOutputStream error: ");
|
||||
return@launch
|
||||
}
|
||||
|
||||
val byteArrayWrite = ByteArray(4096)
|
||||
launch {
|
||||
readTunLoop()
|
||||
}
|
||||
|
||||
Log.d(LOG_TAG, "write tun loop start")
|
||||
while (sdwanRunning) {
|
||||
val len = Openp2p.androidWrite(byteArrayWrite)
|
||||
Log.i(OpenP2PService.LOG_TAG, String.format("Openp2p.androidWrite: %d",len));
|
||||
val writeBytes = outputStream?.write(ByteBuffer.wrap(byteArrayWrite))
|
||||
if (writeBytes != null && writeBytes <= 0) {
|
||||
Log.i(OpenP2PService.LOG_TAG, "outputStream?.write error: ");
|
||||
continue
|
||||
}
|
||||
}
|
||||
outputStream.close()
|
||||
// 关闭 VPN 接口
|
||||
vpnInterface?.close()
|
||||
// 置空变量以释放资源
|
||||
vpnInterface = null
|
||||
Log.d(LOG_TAG, "write tun loop end")
|
||||
}catch (e: Exception) {
|
||||
// 捕获异常并记录
|
||||
Log.e("VPN Connection", "发生异常: ${e.message}")
|
||||
Node(nodeName, nodeIp, nodeResource)
|
||||
}
|
||||
Log.i(OpenP2PService.LOG_TAG, "runSDWAN end");
|
||||
|
||||
val network = Network(id, name, gateway, nodeList)
|
||||
println(network)
|
||||
Logger.i(OpenP2PService.LOG_TAG, "onBind");
|
||||
builder.addDnsServer("119.29.29.29")
|
||||
builder.addDnsServer("2400:3200::1") // alicloud dns v6 & v4
|
||||
// builder.addRoute("10.2.3.0", 24)
|
||||
// builder.addRoute("0.0.0.0", 0);
|
||||
val gatewayStr = jsonObject.optString("gateway", "")
|
||||
val subNet = getNetworkAddress(gatewayStr)
|
||||
if (subNet != null) {
|
||||
val (netIp, prefix) = subNet
|
||||
builder.addRoute(netIp, prefix)
|
||||
Logger.i(OpenP2PService.LOG_TAG, "Added route from gateway: $netIp/$prefix")
|
||||
} else {
|
||||
Logger.w(OpenP2PService.LOG_TAG, "Invalid gateway format: $gatewayStr")
|
||||
}
|
||||
|
||||
builder.setSession(LOG_TAG!!)
|
||||
builder.setMtu(mtu)
|
||||
vpnInterface = builder.establish()
|
||||
if (vpnInterface == null) {
|
||||
Log.e(OpenP2PService.LOG_TAG, "start vpnservice error: ");
|
||||
}
|
||||
|
||||
val byteArrayWrite = ByteArray(4096)
|
||||
serviceScope.launch(Dispatchers.IO) {
|
||||
readTunLoop() // 文件读操作,适合 Dispatchers.IO
|
||||
}
|
||||
|
||||
val outputStream = FileOutputStream(vpnInterface?.fileDescriptor).channel
|
||||
if (outputStream == null) {
|
||||
Log.e(OpenP2PService.LOG_TAG, "open FileOutputStream error: ");
|
||||
return
|
||||
}
|
||||
Logger.i(LOG_TAG, "write tun loop start")
|
||||
while (sdwanRunning) {
|
||||
val len = Openp2p.androidWrite(byteArrayWrite, 3000)
|
||||
if (len > mtu || len.toInt() == 0) {
|
||||
continue
|
||||
}
|
||||
try {
|
||||
val writeBytes =
|
||||
outputStream?.write(ByteBuffer.wrap(byteArrayWrite, 0, len.toInt()))
|
||||
if (writeBytes != null && writeBytes <= 0) {
|
||||
Logger.e(LOG_TAG, "outputStream.write failed: $writeBytes")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Logger.e(LOG_TAG, "outputStream.write exception: ${e.message}")
|
||||
e.printStackTrace()
|
||||
continue
|
||||
}
|
||||
}
|
||||
outputStream.close()
|
||||
vpnInterface?.close()
|
||||
vpnInterface = null
|
||||
Logger.i(LOG_TAG, "write tun loop end")
|
||||
} catch (e: Exception) {
|
||||
Logger.i("VPN Connection", "发生异常: ${e.message}")
|
||||
}
|
||||
|
||||
Logger.i(OpenP2PService.LOG_TAG, "runSDWAN end");
|
||||
}
|
||||
/**
|
||||
* 将 "10.2.3.254/16" 这样的 CIDR 转成正确对齐的网络地址,如 "10.2.0.0/16"
|
||||
*/
|
||||
fun getNetworkAddress(cidr: String): Pair<String, Int>? {
|
||||
val parts = cidr.trim().split("/")
|
||||
if (parts.size != 2) return null
|
||||
|
||||
val ip = parts[0]
|
||||
val prefix = parts[1].toIntOrNull() ?: return null
|
||||
if (prefix !in 0..32) return null
|
||||
|
||||
val octets = ip.split(".").map { it.toInt() }
|
||||
if (octets.size != 4) return null
|
||||
|
||||
// 转成整数
|
||||
val ipInt = (octets[0] shl 24) or (octets[1] shl 16) or (octets[2] shl 8) or octets[3]
|
||||
|
||||
// 生成掩码并计算网络地址
|
||||
val mask = if (prefix == 0) 0 else (-1 shl (32 - prefix))
|
||||
val networkInt = ipInt and mask
|
||||
|
||||
// 转回点分十进制
|
||||
val networkIp = listOf(
|
||||
(networkInt shr 24) and 0xFF,
|
||||
(networkInt shr 16) and 0xFF,
|
||||
(networkInt shr 8) and 0xFF,
|
||||
networkInt and 0xFF
|
||||
).joinToString(".")
|
||||
|
||||
return networkIp to prefix
|
||||
}
|
||||
override fun onDestroy() {
|
||||
Log.i(LOG_TAG, "onDestroy - Thread ID = " + Thread.currentThread().id)
|
||||
super.onDestroy()
|
||||
Logger.i(LOG_TAG, "onDestroy - Canceling service scope")
|
||||
serviceScope.cancel() // 取消所有与服务相关的协程
|
||||
}
|
||||
|
||||
override fun onUnbind(intent: Intent?): Boolean {
|
||||
Log.i(LOG_TAG, "onUnbind - Thread ID = " + Thread.currentThread().id)
|
||||
Logger.i(LOG_TAG, "onUnbind - Thread ID = " + Thread.currentThread().id)
|
||||
stopSelf()
|
||||
return super.onUnbind(intent)
|
||||
}
|
||||
|
||||
fun isConnected(): Boolean {
|
||||
if (!::network.isInitialized) return false
|
||||
return network.connect(1000)
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
running=false
|
||||
running = false
|
||||
stopSelf()
|
||||
Openp2p.stop()
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
private fun createNotificationChannel(channelId: String, channelName: String): String? {
|
||||
val chan = NotificationChannel(
|
||||
@@ -281,4 +398,55 @@ class OpenP2PService : VpnService() {
|
||||
service.createNotificationChannel(chan)
|
||||
return channelId
|
||||
}
|
||||
}
|
||||
|
||||
// 获取本机所有IP地址和对应的子网信息
|
||||
fun getLocalIpAndSubnet(): List<Pair<String, Int>> {
|
||||
val localIps = mutableListOf<Pair<String, Int>>()
|
||||
val networkInterfaces = NetworkInterface.getNetworkInterfaces()
|
||||
// 手动添加测试数据
|
||||
//localIps.add(Pair("192.168.3.33", 24))
|
||||
while (networkInterfaces.hasMoreElements()) {
|
||||
val networkInterface = networkInterfaces.nextElement()
|
||||
if (networkInterface.isUp && !networkInterface.isLoopback) {
|
||||
val interfaceAddresses = networkInterface.interfaceAddresses
|
||||
for (interfaceAddress in interfaceAddresses) {
|
||||
val address = interfaceAddress.address
|
||||
val prefixLength = interfaceAddress.networkPrefixLength
|
||||
if (address is InetAddress) {
|
||||
localIps.add(Pair(address.hostAddress, prefixLength.toInt()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return localIps
|
||||
}
|
||||
|
||||
// 判断某个IP是否与本机某网段匹配
|
||||
fun isSameSubnet(ipAddress: String, localIps: List<Pair<String, Int>>): Boolean {
|
||||
val targetIp = InetAddress.getByName(ipAddress).address
|
||||
for ((localIp, prefixLength) in localIps) {
|
||||
val localIpBytes = InetAddress.getByName(localIp).address
|
||||
val mask = createSubnetMask(prefixLength, localIpBytes.size) // 动态生成掩码
|
||||
|
||||
// 比较目标 IP 和本地 IP 的网络部分
|
||||
if (targetIp.indices.all { i ->
|
||||
(targetIp[i].toInt() and mask[i].toInt()) == (localIpBytes[i].toInt() and mask[i].toInt())
|
||||
}) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 根据前缀长度动态生成子网掩码
|
||||
fun createSubnetMask(prefixLength: Int, addressLength: Int): ByteArray {
|
||||
val mask = ByteArray(addressLength)
|
||||
for (i in 0 until prefixLength / 8) {
|
||||
mask[i] = 0xFF.toByte()
|
||||
}
|
||||
if (prefixLength % 8 != 0) {
|
||||
mask[prefixLength / 8] = (0xFF shl (8 - (prefixLength % 8))).toByte()
|
||||
}
|
||||
return mask
|
||||
}
|
||||
@@ -1,45 +1,91 @@
|
||||
package cn.openp2p
|
||||
import android.content.*
|
||||
import android.util.Log
|
||||
import java.text.SimpleDateFormat
|
||||
import java.io.BufferedWriter
|
||||
import java.io.File
|
||||
import java.io.FileWriter
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import android.app.*
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import java.io.IOException
|
||||
import android.net.VpnService
|
||||
import android.os.Binder
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.app.NotificationCompat
|
||||
import cn.openp2p.ui.login.LoginActivity
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import openp2p.Openp2p
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.nio.ByteBuffer
|
||||
import kotlinx.coroutines.*
|
||||
import org.json.JSONObject
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
object Logger {
|
||||
private val logFile: File = File("app.log")
|
||||
private const val LOG_TAG = "OpenP2PLogger"
|
||||
private var logFile: File? = null
|
||||
private var bufferedWriter: BufferedWriter? = null
|
||||
|
||||
fun log(message: String) {
|
||||
val timestamp = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date())
|
||||
val logMessage = "$timestamp: $message\n"
|
||||
// 初始化日志文件
|
||||
fun init(logDir: File, logFileName: String = "app.log") {
|
||||
if (!logDir.exists()) logDir.mkdirs()
|
||||
logFile = File(logDir, logFileName)
|
||||
|
||||
try {
|
||||
val fileWriter = FileWriter(logFile, true)
|
||||
fileWriter.append(logMessage)
|
||||
fileWriter.close()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
bufferedWriter = BufferedWriter(FileWriter(logFile, true))
|
||||
} catch (e: IOException) {
|
||||
Log.e(LOG_TAG, "Failed to initialize BufferedWriter: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// 写日志(线程安全)
|
||||
@Synchronized
|
||||
fun log(level: String, tag: String, message: String, throwable: Throwable? = null) {
|
||||
val timestamp = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date())
|
||||
val logMessage = "$timestamp $level $tag: $message"
|
||||
|
||||
// 打印到 console
|
||||
when (level) {
|
||||
"ERROR" -> Log.e(tag, message, throwable)
|
||||
"WARN" -> Log.w(tag, message, throwable)
|
||||
"INFO" -> Log.i(tag, message)
|
||||
"DEBUG" -> Log.d(tag, message)
|
||||
"VERBOSE" -> Log.v(tag, message)
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
try {
|
||||
bufferedWriter?.apply {
|
||||
write(logMessage)
|
||||
newLine()
|
||||
flush()
|
||||
}
|
||||
throwable?.let {
|
||||
bufferedWriter?.apply {
|
||||
write(Log.getStackTraceString(it))
|
||||
newLine()
|
||||
flush()
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Log.e(LOG_TAG, "Failed to write log to file: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// 清理资源
|
||||
fun close() {
|
||||
try {
|
||||
bufferedWriter?.close()
|
||||
} catch (e: IOException) {
|
||||
Log.e(LOG_TAG, "Failed to close BufferedWriter: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// 简化方法
|
||||
fun e(tag: String, message: String, throwable: Throwable? = null) {
|
||||
log("ERROR", tag, message, throwable)
|
||||
}
|
||||
|
||||
fun w(tag: String, message: String, throwable: Throwable? = null) {
|
||||
log("WARN", tag, message, throwable)
|
||||
}
|
||||
|
||||
fun i(tag: String, message: String) {
|
||||
log("INFO", tag, message)
|
||||
}
|
||||
|
||||
fun d(tag: String, message: String) {
|
||||
log("DEBUG", tag, message)
|
||||
}
|
||||
|
||||
fun v(tag: String, message: String) {
|
||||
log("VERBOSE", tag, message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,14 @@ allprojects {
|
||||
jcenter() // Warning: this repository is going to shut down soon
|
||||
}
|
||||
}
|
||||
allprojects {
|
||||
repositories {
|
||||
maven { url 'https://maven.aliyun.com/repository/google' }
|
||||
maven { url 'https://maven.aliyun.com/repository/central' }
|
||||
maven { url 'https://maven.aliyun.com/repository/public' }
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
@@ -16,4 +16,6 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
kotlin.code.style=official
|
||||
org.gradle.caching=true
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
op "openp2p/core"
|
||||
op2p "openp2p/core"
|
||||
)
|
||||
|
||||
func main() {
|
||||
op.Run()
|
||||
op2p.Run()
|
||||
}
|
||||
|
||||
131
core/common.go
@@ -2,18 +2,22 @@ package openp2p
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/tls"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -128,19 +132,19 @@ func netInfo() *NetInfo {
|
||||
client := &http.Client{Transport: tr, Timeout: time.Second * 10}
|
||||
r, err := client.Get("https://ifconfig.co/json")
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "netInfo error:", err)
|
||||
gLog.d("netInfo error:%s", err)
|
||||
continue
|
||||
}
|
||||
defer r.Body.Close()
|
||||
buf := make([]byte, 1024*64)
|
||||
n, err := r.Body.Read(buf)
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "netInfo error:", err)
|
||||
if err != nil && err != io.EOF {
|
||||
gLog.d("error reading response body: %s", err)
|
||||
continue
|
||||
}
|
||||
rsp := NetInfo{}
|
||||
if err = json.Unmarshal(buf[:n], &rsp); err != nil {
|
||||
gLog.Printf(LvERROR, "wrong NetInfo:%s", err)
|
||||
gLog.e("wrong NetInfo:%s", err)
|
||||
continue
|
||||
}
|
||||
return &rsp
|
||||
@@ -280,3 +284,122 @@ func calculateChecksum(data []byte) uint16 {
|
||||
|
||||
return uint16(^sum)
|
||||
}
|
||||
|
||||
func min(nums ...int32) int32 {
|
||||
if len(nums) == 0 {
|
||||
return 0 // 如果没有输入,返回最大值
|
||||
}
|
||||
|
||||
minVal := nums[0]
|
||||
for _, num := range nums[1:] {
|
||||
if num < minVal {
|
||||
minVal = num
|
||||
}
|
||||
}
|
||||
return minVal
|
||||
}
|
||||
|
||||
func calcRetryTimeRelay(x float64) float64 {
|
||||
return 10 + math.Exp(0.8*(x-3.6))
|
||||
}
|
||||
func calcRetryTimeDirect(x float64) float64 {
|
||||
return 10 + math.Exp(2.8*(x-4))
|
||||
}
|
||||
|
||||
func isAndroid() bool {
|
||||
if runtime.GOOS == "android" {
|
||||
return true
|
||||
}
|
||||
data, err := os.ReadFile("/proc/version")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return strings.Contains(string(data), "Android")
|
||||
}
|
||||
|
||||
func moveFile(src, dst string) error {
|
||||
err := os.Rename(src, dst)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
// windows could not rename running executable, so copy then delete
|
||||
if runtime.GOOS == "windows" {
|
||||
err = copyFile(src, dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
os.Remove(src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyFile(src, dst string) error {
|
||||
sourceFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sourceFile.Close()
|
||||
|
||||
destFile, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer destFile.Close()
|
||||
|
||||
_, err = io.Copy(destFile, sourceFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return destFile.Sync()
|
||||
}
|
||||
|
||||
func resolveServerIP(host string) ([]string, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// 先系统 DNS
|
||||
ips, err := net.DefaultResolver.LookupHost(ctx, host)
|
||||
if err == nil && len(ips) > 0 {
|
||||
gLog.i("system dns resolved %s -> %v", host, ips)
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
gLog.e("system dns resolve failed for %s: %v", host, err)
|
||||
gLog.i("retry with fallback dns...")
|
||||
|
||||
// 再 fallback dns
|
||||
return lookupWithCustomDNS(ctx, host)
|
||||
}
|
||||
func lookupWithCustomDNS(ctx context.Context, domain string) ([]string, error) {
|
||||
resolver := &net.Resolver{
|
||||
PreferGo: true,
|
||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
dialer := &net.Dialer{Timeout: 5 * time.Second}
|
||||
|
||||
// 先 119.29.29.29
|
||||
conn, err := dialer.DialContext(ctx, network, "119.29.29.29:53")
|
||||
if err == nil {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// 再 8.8.8.8
|
||||
return dialer.DialContext(ctx, network, "8.8.8.8:53")
|
||||
},
|
||||
}
|
||||
|
||||
return resolver.LookupHost(ctx, domain)
|
||||
}
|
||||
|
||||
func writeFull(w io.Writer, data []byte) error {
|
||||
totalWritten := 0
|
||||
for totalWritten < len(data) {
|
||||
n, err := w.Write(data[totalWritten:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("write failed after %d bytes: %w", totalWritten, err)
|
||||
}
|
||||
totalWritten += n
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -127,3 +127,59 @@ func TestNodeID(t *testing.T) {
|
||||
fmt.Printf("%s >= %s\n", node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalcRetryTime(t *testing.T) {
|
||||
// 0-2 < 13s
|
||||
// 3-5:300
|
||||
// 6-10:600
|
||||
tests := []struct {
|
||||
retryNum float64
|
||||
want float64
|
||||
}{
|
||||
{1.0, 10},
|
||||
{5.0, 13},
|
||||
{10.0, 180},
|
||||
{15.0, 9000},
|
||||
{18.0, 90000},
|
||||
// 可以添加更多测试用例
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
got := calcRetryTimeRelay(tt.retryNum)
|
||||
if got < tt.want*0.85 || got > tt.want*1.15 {
|
||||
t.Errorf("calcRetryTime(%f) = %f, want %f", tt.retryNum, got, tt.want)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < 20; i++ {
|
||||
log.Printf("%d retryTime=%fs", i, calcRetryTimeRelay(float64(i)))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalcRetryTimeDirect(t *testing.T) {
|
||||
// 0-2 < 13s
|
||||
// 3-5:300
|
||||
// 6-10:600
|
||||
tests := []struct {
|
||||
retryNum float64
|
||||
want float64
|
||||
}{
|
||||
{1.0, 10},
|
||||
{5.0, 13},
|
||||
{10.0, 180},
|
||||
{15.0, 9000},
|
||||
{18.0, 90000},
|
||||
// 可以添加更多测试用例
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
got := calcRetryTimeRelay(tt.retryNum)
|
||||
if got < tt.want*0.85 || got > tt.want*1.15 {
|
||||
t.Errorf("calcRetryTime(%f) = %f, want %f", tt.retryNum, got, tt.want)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < 20; i++ {
|
||||
log.Printf("%d retryTime=%fs", i, calcRetryTimeDirect(float64(i)))
|
||||
}
|
||||
}
|
||||
|
||||
228
core/config.go
@@ -51,9 +51,10 @@ type AppConfig struct {
|
||||
}
|
||||
|
||||
const (
|
||||
PunchPriorityTCPFirst = 1
|
||||
PunchPriorityUDPDisable = 1 << 1
|
||||
PunchPriorityTCPDisable = 1 << 2
|
||||
PunchPriorityUDPFirst = 0
|
||||
PunchPriorityTCPFirst = 1
|
||||
PunchPriorityTCPOnly = 1 << 1
|
||||
PunchPriorityUDPOnly = 1 << 2
|
||||
)
|
||||
|
||||
func (c *AppConfig) ID() uint64 {
|
||||
@@ -77,14 +78,17 @@ type Config struct {
|
||||
Network NetworkConfig `json:"network"`
|
||||
Apps []*AppConfig `json:"apps"`
|
||||
|
||||
LogLevel int
|
||||
MaxLogSize int
|
||||
daemonMode bool
|
||||
mtx sync.Mutex
|
||||
sdwanMtx sync.Mutex
|
||||
sdwan SDWANInfo
|
||||
delNodes []*SDWANNode
|
||||
addNodes []*SDWANNode
|
||||
LogLevel int
|
||||
MaxLogSize int
|
||||
TLSInsecureSkipVerify bool
|
||||
Forcev6 bool
|
||||
daemonMode bool
|
||||
mtx sync.RWMutex
|
||||
fileMtx sync.Mutex
|
||||
sdwanMtx sync.Mutex
|
||||
sdwan SDWANInfo
|
||||
delNodes []*SDWANNode
|
||||
addNodes []*SDWANNode
|
||||
}
|
||||
|
||||
func (c *Config) getSDWAN() SDWANInfo {
|
||||
@@ -144,6 +148,12 @@ func (c *Config) setSDWAN(s SDWANInfo) {
|
||||
}
|
||||
}
|
||||
c.sdwan = s
|
||||
if c.sdwan.TunnelNum < 2 {
|
||||
c.sdwan.TunnelNum = 2 // DEBUG
|
||||
}
|
||||
if c.sdwan.TunnelNum > 3 {
|
||||
c.sdwan.TunnelNum = 3
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) switchApp(app AppConfig, enabled int) {
|
||||
@@ -160,26 +170,16 @@ func (c *Config) switchApp(app AppConfig, enabled int) {
|
||||
c.save()
|
||||
}
|
||||
|
||||
// TODO: move to p2pnetwork
|
||||
func (c *Config) retryApp(peerNode string) {
|
||||
GNetwork.apps.Range(func(id, i interface{}) bool {
|
||||
app := i.(*p2pApp)
|
||||
if app.config.PeerNode == peerNode {
|
||||
gLog.Println(LvDEBUG, "retry app ", app.config.LogPeerNode())
|
||||
app.config.retryNum = 0
|
||||
app.config.nextRetryTime = time.Now()
|
||||
app.retryRelayNum = 0
|
||||
app.nextRetryRelayTime = time.Now()
|
||||
app.hbMtx.Lock()
|
||||
app.hbTimeRelay = time.Now().Add(-TunnelHeartbeatTime * 3)
|
||||
app.hbMtx.Unlock()
|
||||
app.Retry(true)
|
||||
}
|
||||
if app.config.RelayNode == peerNode {
|
||||
gLog.Println(LvDEBUG, "retry app ", app.config.LogPeerNode())
|
||||
app.retryRelayNum = 0
|
||||
app.nextRetryRelayTime = time.Now()
|
||||
app.hbMtx.Lock()
|
||||
app.hbTimeRelay = time.Now().Add(-TunnelHeartbeatTime * 3)
|
||||
app.hbMtx.Unlock()
|
||||
app.Retry(false)
|
||||
gLog.d("retry app relay=%s", app.config.LogPeerNode())
|
||||
}
|
||||
return true
|
||||
})
|
||||
@@ -188,14 +188,7 @@ func (c *Config) retryApp(peerNode string) {
|
||||
func (c *Config) retryAllApp() {
|
||||
GNetwork.apps.Range(func(id, i interface{}) bool {
|
||||
app := i.(*p2pApp)
|
||||
gLog.Println(LvDEBUG, "retry app ", app.config.LogPeerNode())
|
||||
app.config.retryNum = 0
|
||||
app.config.nextRetryTime = time.Now()
|
||||
app.retryRelayNum = 0
|
||||
app.nextRetryRelayTime = time.Now()
|
||||
app.hbMtx.Lock()
|
||||
defer app.hbMtx.Unlock()
|
||||
app.hbTimeRelay = time.Now().Add(-TunnelHeartbeatTime * 3)
|
||||
app.Retry(true)
|
||||
return true
|
||||
})
|
||||
}
|
||||
@@ -206,22 +199,22 @@ func (c *Config) retryAllMemApp() {
|
||||
if app.config.SrcPort != 0 {
|
||||
return true
|
||||
}
|
||||
gLog.Println(LvDEBUG, "retry app ", app.config.LogPeerNode())
|
||||
app.config.retryNum = 0
|
||||
app.config.nextRetryTime = time.Now()
|
||||
app.retryRelayNum = 0
|
||||
app.nextRetryRelayTime = time.Now()
|
||||
app.hbMtx.Lock()
|
||||
defer app.hbMtx.Unlock()
|
||||
app.hbTimeRelay = time.Now().Add(-TunnelHeartbeatTime * 3)
|
||||
if app.tunnelNum != int(gConf.sdwan.TunnelNum) {
|
||||
gLog.d("memapp %s tunnelNum changed from %d to %d, delete it and not retry", app.config.LogPeerNode(), app.tunnelNum, gConf.sdwan.TunnelNum)
|
||||
GNetwork.DeleteApp(app.config)
|
||||
return true
|
||||
}
|
||||
app.Retry(true)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Config) add(app AppConfig, override bool) {
|
||||
if app.AppName == "" {
|
||||
app.AppName = fmt.Sprintf("%d", app.ID())
|
||||
}
|
||||
c.mtx.Lock()
|
||||
defer c.mtx.Unlock()
|
||||
defer c.save()
|
||||
if override {
|
||||
for i := 0; i < len(c.Apps); i++ {
|
||||
if c.Apps[i].PeerNode == app.PeerNode && c.Apps[i].Protocol == app.Protocol && c.Apps[i].SrcPort == app.SrcPort {
|
||||
@@ -231,12 +224,14 @@ func (c *Config) add(app AppConfig, override bool) {
|
||||
}
|
||||
}
|
||||
c.Apps = append(c.Apps, &app)
|
||||
if app.SrcPort != 0 {
|
||||
c.save()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) delete(app AppConfig) {
|
||||
c.mtx.Lock()
|
||||
defer c.mtx.Unlock()
|
||||
defer c.save()
|
||||
for i := 0; i < len(c.Apps); i++ {
|
||||
if (app.SrcPort != 0 && c.Apps[i].Protocol == app.Protocol && c.Apps[i].SrcPort == app.SrcPort) || // normal app
|
||||
(app.SrcPort == 0 && c.Apps[i].SrcPort == 0 && c.Apps[i].PeerNode == app.PeerNode) { // memapp
|
||||
@@ -245,37 +240,51 @@ func (c *Config) delete(app AppConfig) {
|
||||
} else {
|
||||
c.Apps = append(c.Apps[:i], c.Apps[i+1:]...)
|
||||
}
|
||||
return
|
||||
break
|
||||
}
|
||||
}
|
||||
if app.SrcPort != 0 {
|
||||
c.save()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) save() {
|
||||
// c.mtx.Lock()
|
||||
// defer c.mtx.Unlock() // internal call
|
||||
c.fileMtx.Lock()
|
||||
defer c.fileMtx.Unlock()
|
||||
if c.Network.Token == 0 {
|
||||
gLog.e("c.Network.Token == 0 skip save")
|
||||
return
|
||||
}
|
||||
data, _ := json.MarshalIndent(c, "", " ")
|
||||
err := os.WriteFile("config.json", data, 0644)
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "save config.json error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) saveCache() {
|
||||
// c.mtx.Lock()
|
||||
// defer c.mtx.Unlock() // internal call
|
||||
if c.Network.Token == 0 {
|
||||
data, err := json.MarshalIndent(c, "", " ")
|
||||
if err != nil || len(data) < 16 {
|
||||
gLog.e("MarshalIndent config.json error:%v, len=%d", err, len(data))
|
||||
return
|
||||
}
|
||||
data, _ := json.MarshalIndent(c, "", " ")
|
||||
err := os.WriteFile("config.json0", data, 0644)
|
||||
err = os.WriteFile("config.json0", data, 0644)
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "save config.json0 error:", err)
|
||||
gLog.e("save config.json error:%v", err)
|
||||
}
|
||||
|
||||
// verify if the file is written correctly
|
||||
data, err = os.ReadFile("config.json0")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var tmpConfig Config
|
||||
err = json.Unmarshal(data, &tmpConfig)
|
||||
if err != nil {
|
||||
gLog.e("parse config.json error:", err)
|
||||
return
|
||||
}
|
||||
err = os.Rename("config.json0", "config.json")
|
||||
if err != nil {
|
||||
gLog.e("rename config file error:%v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// -d run, then worker serverport always WsPort.
|
||||
// func init() {
|
||||
func init() {
|
||||
gConf.LogLevel = int(LvINFO)
|
||||
gConf.MaxLogSize = 1024 * 1024
|
||||
@@ -286,19 +295,19 @@ func init() {
|
||||
}
|
||||
|
||||
func (c *Config) load() error {
|
||||
c.mtx.Lock()
|
||||
defer c.mtx.Unlock()
|
||||
c.fileMtx.Lock()
|
||||
defer c.fileMtx.Unlock()
|
||||
data, err := os.ReadFile("config.json")
|
||||
if err != nil {
|
||||
return c.loadCache()
|
||||
return err
|
||||
}
|
||||
c.mtx.Lock()
|
||||
defer c.mtx.Unlock()
|
||||
err = json.Unmarshal(data, &c)
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "parse config.json error:", err)
|
||||
// try cache
|
||||
return c.loadCache()
|
||||
gLog.e("parse config.json error:", err)
|
||||
return err
|
||||
}
|
||||
// load ok. cache it
|
||||
var filteredApps []*AppConfig // filter memapp
|
||||
for _, app := range c.Apps {
|
||||
if app.SrcPort != 0 {
|
||||
@@ -306,19 +315,7 @@ func (c *Config) load() error {
|
||||
}
|
||||
}
|
||||
c.Apps = filteredApps
|
||||
c.saveCache()
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Config) loadCache() error {
|
||||
data, err := os.ReadFile("config.json0")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = json.Unmarshal(data, &c)
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "parse config.json0 error:", err)
|
||||
}
|
||||
c.Network.natType = NATUnknown
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -326,7 +323,6 @@ func (c *Config) loadCache() error {
|
||||
func (c *Config) setToken(token uint64) {
|
||||
c.mtx.Lock()
|
||||
defer c.mtx.Unlock()
|
||||
defer c.save()
|
||||
if token != 0 {
|
||||
c.Network.Token = token
|
||||
}
|
||||
@@ -334,16 +330,19 @@ func (c *Config) setToken(token uint64) {
|
||||
func (c *Config) setUser(user string) {
|
||||
c.mtx.Lock()
|
||||
defer c.mtx.Unlock()
|
||||
defer c.save()
|
||||
c.Network.User = user
|
||||
}
|
||||
func (c *Config) setNode(node string) {
|
||||
c.mtx.Lock()
|
||||
defer c.mtx.Unlock()
|
||||
defer c.save()
|
||||
c.Network.Node = node
|
||||
c.Network.nodeID = NodeNameToID(c.Network.Node)
|
||||
}
|
||||
func (c *Config) setForcev6(force bool) {
|
||||
c.mtx.Lock()
|
||||
defer c.mtx.Unlock()
|
||||
c.Forcev6 = force
|
||||
}
|
||||
func (c *Config) nodeID() uint64 {
|
||||
c.mtx.Lock()
|
||||
defer c.mtx.Unlock()
|
||||
@@ -379,22 +378,27 @@ type NetworkConfig struct {
|
||||
mac string
|
||||
os string
|
||||
publicIP string
|
||||
previousIP string // for publicIP change detect
|
||||
natType int
|
||||
hasIPv4 int
|
||||
publicIPv6 string // must lowwer-case not save json
|
||||
hasUPNPorNATPMP int
|
||||
ShareBandwidth int
|
||||
// server info
|
||||
ServerHost string
|
||||
ServerPort int
|
||||
UDPPort1 int
|
||||
UDPPort2 int
|
||||
TCPPort int
|
||||
ServerHost string
|
||||
ServerIP string
|
||||
ServerPort int
|
||||
natDetectPort1 int
|
||||
natDetectPort2 int
|
||||
PublicIPPort int // both tcp and udp
|
||||
specTunnel int
|
||||
}
|
||||
|
||||
func parseParams(subCommand string, cmd string) {
|
||||
fset := flag.NewFlagSet(subCommand, flag.ExitOnError)
|
||||
installPath := fset.String("installpath", "", "custom install path")
|
||||
serverHost := fset.String("serverhost", "api.openp2p.cn", "server host ")
|
||||
insecure := fset.Bool("insecure", false, "not verify TLS certificate")
|
||||
serverPort := fset.Int("serverport", WsPort, "server port ")
|
||||
// serverHost := flag.String("serverhost", "127.0.0.1", "server host ") // for debug
|
||||
token := fset.Uint64("token", 0, "token")
|
||||
@@ -404,7 +408,7 @@ func parseParams(subCommand string, cmd string) {
|
||||
whiteList := fset.String("whitelist", "", "whitelist for p2pApp ")
|
||||
dstPort := fset.Int("dstport", 0, "destination port ")
|
||||
srcPort := fset.Int("srcport", 0, "source port ")
|
||||
tcpPort := fset.Int("tcpport", 0, "tcp port for upnp or publicip")
|
||||
publicIPPort := fset.Int("publicipport", 0, "public ip port for upnp or publicip")
|
||||
protocol := fset.String("protocol", "tcp", "tcp or udp")
|
||||
underlayProtocol := fset.String("underlay_protocol", "quic", "quic or kcp")
|
||||
punchPriority := fset.Int("punch_priority", 0, "bitwise DisableTCP|DisableUDP|UDPFirst 0:tcp and udp both enable, tcp first")
|
||||
@@ -423,12 +427,10 @@ func parseParams(subCommand string, cmd string) {
|
||||
fset.Parse(os.Args[2:])
|
||||
}
|
||||
} else {
|
||||
gLog.Println(LvINFO, "cmd=", cmd)
|
||||
args := strings.Split(cmd, " ")
|
||||
fset.Parse(args)
|
||||
}
|
||||
|
||||
gLog.setMaxSize(int64(*maxLogSize))
|
||||
config := AppConfig{Enabled: 1}
|
||||
config.PeerNode = *peerNode
|
||||
config.DstHost = *dstIP
|
||||
@@ -440,6 +442,19 @@ func parseParams(subCommand string, cmd string) {
|
||||
config.PunchPriority = *punchPriority
|
||||
config.AppName = *appName
|
||||
config.RelayNode = *relayNode
|
||||
if *installPath != "" {
|
||||
defaultInstallPath = *installPath
|
||||
}
|
||||
if subCommand == "install" {
|
||||
if err := os.MkdirAll(defaultInstallPath, 0775); err != nil {
|
||||
gLog.e("parseParams MkdirAll %s error:%s", defaultInstallPath, err)
|
||||
return
|
||||
}
|
||||
if err := os.Chdir(defaultInstallPath); err != nil {
|
||||
gLog.e("parseParams Chdir error:%s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if !*newconfig {
|
||||
gConf.load() // load old config. otherwise will clear all apps
|
||||
}
|
||||
@@ -465,17 +480,26 @@ func parseParams(subCommand string, cmd string) {
|
||||
if f.Name == "maxlogsize" {
|
||||
gConf.MaxLogSize = *maxLogSize
|
||||
}
|
||||
if f.Name == "tcpport" {
|
||||
gConf.Network.TCPPort = *tcpPort
|
||||
if f.Name == "publicipport" {
|
||||
gConf.Network.PublicIPPort = *publicIPPort
|
||||
}
|
||||
if f.Name == "token" {
|
||||
gConf.setToken(*token)
|
||||
}
|
||||
if f.Name == "serverport" {
|
||||
gConf.Network.ServerPort = *serverPort
|
||||
}
|
||||
if f.Name == "insecure" {
|
||||
gConf.TLSInsecureSkipVerify = *insecure
|
||||
}
|
||||
})
|
||||
// set default value
|
||||
if gConf.Network.ServerHost == "" {
|
||||
gConf.Network.ServerHost = *serverHost
|
||||
}
|
||||
if gConf.Network.ServerPort == 0 {
|
||||
gConf.Network.ServerPort = *serverPort
|
||||
}
|
||||
if *node != "" {
|
||||
gConf.setNode(*node)
|
||||
} else {
|
||||
@@ -487,12 +511,12 @@ func parseParams(subCommand string, cmd string) {
|
||||
gConf.setNode(defaultNodeName())
|
||||
}
|
||||
}
|
||||
if gConf.Network.TCPPort == 0 {
|
||||
if *tcpPort == 0 {
|
||||
p := int(gConf.nodeID()%15000 + 50000)
|
||||
tcpPort = &p
|
||||
if gConf.Network.PublicIPPort == 0 {
|
||||
if *publicIPPort == 0 {
|
||||
p := int(gConf.nodeID()%8192 + 1025)
|
||||
publicIPPort = &p
|
||||
}
|
||||
gConf.Network.TCPPort = *tcpPort
|
||||
gConf.Network.PublicIPPort = *publicIPPort
|
||||
}
|
||||
if *token == 0 {
|
||||
envToken := os.Getenv("OPENP2P_TOKEN")
|
||||
@@ -502,13 +526,13 @@ func parseParams(subCommand string, cmd string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
gConf.Network.ServerPort = *serverPort
|
||||
gConf.Network.UDPPort1 = UDPPort1
|
||||
gConf.Network.UDPPort2 = UDPPort2
|
||||
|
||||
gConf.Network.natDetectPort1 = NATDetectPort1
|
||||
gConf.Network.natDetectPort2 = NATDetectPort2
|
||||
gLog.setLevel(LogLevel(gConf.LogLevel))
|
||||
gLog.setMaxSize(int64(gConf.MaxLogSize))
|
||||
if *notVerbose {
|
||||
gLog.setMode(LogFile)
|
||||
}
|
||||
// gConf.mtx.Unlock()
|
||||
gConf.save()
|
||||
}
|
||||
|
||||
122
core/daemon.go
@@ -1,9 +1,9 @@
|
||||
package openp2p
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/openp2p-cn/service"
|
||||
@@ -15,34 +15,32 @@ type daemon struct {
|
||||
}
|
||||
|
||||
func (d *daemon) Start(s service.Service) error {
|
||||
gLog.Println(LvINFO, "daemon start")
|
||||
gLog.i("system service start")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *daemon) Stop(s service.Service) error {
|
||||
gLog.Println(LvINFO, "service stop")
|
||||
gLog.i("system service stop")
|
||||
d.running = false
|
||||
if d.proc != nil {
|
||||
gLog.Println(LvINFO, "stop worker")
|
||||
gLog.i("stop worker")
|
||||
d.proc.Kill()
|
||||
}
|
||||
if service.Interactive() {
|
||||
gLog.Println(LvINFO, "stop daemon")
|
||||
gLog.i("stop daemon")
|
||||
os.Exit(0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *daemon) run() {
|
||||
gLog.Println(LvINFO, "daemon run start")
|
||||
defer gLog.Println(LvINFO, "daemon run end")
|
||||
gLog.close()
|
||||
baseDir := filepath.Dir(os.Args[0])
|
||||
gLog = NewLogger(baseDir, "daemon", LogLevel(gConf.LogLevel), 1024*1024, LogFile|LogConsole)
|
||||
gLog.i("daemon run start")
|
||||
defer gLog.i("daemon run end")
|
||||
d.running = true
|
||||
binPath, _ := os.Executable()
|
||||
mydir, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
gLog.Println(LvINFO, mydir)
|
||||
conf := &service.Config{
|
||||
Name: ProductName,
|
||||
DisplayName: ProductName,
|
||||
@@ -64,34 +62,55 @@ func (d *daemon) run() {
|
||||
args = append(args, "-nv")
|
||||
for {
|
||||
// start worker
|
||||
tmpDump := filepath.Join("log", "dump.log.tmp")
|
||||
dumpFile := filepath.Join("log", "dump.log")
|
||||
f, err := os.Create(filepath.Join(tmpDump))
|
||||
tmpDump := filepath.Join(filepath.Dir(binPath), "log", "dump.log.tmp")
|
||||
dumpFile := filepath.Join(filepath.Dir(binPath), "log", "dump.log")
|
||||
// f, err := os.Create(filepath.Join(tmpDump))
|
||||
f, err := os.OpenFile(filepath.Join(tmpDump), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0775)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "start worker error:%s", err)
|
||||
gLog.e("OpenFile %s error:%s", tmpDump, err)
|
||||
return
|
||||
}
|
||||
gLog.Println(LvINFO, "start worker process, args:", args)
|
||||
gLog.i("start worker process, args:%v", args)
|
||||
execSpec := &os.ProcAttr{Env: append(os.Environ(), "GOTRACEBACK=crash"), Files: []*os.File{os.Stdin, os.Stdout, f}}
|
||||
lastRebootTime := time.Now()
|
||||
p, err := os.StartProcess(binPath, args, execSpec)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "start worker error:%s", err)
|
||||
gLog.e("start worker error:%s", err)
|
||||
return
|
||||
}
|
||||
d.proc = p
|
||||
_, _ = p.Wait()
|
||||
processState, err := p.Wait()
|
||||
if err != nil {
|
||||
gLog.e("wait process error:%s", err)
|
||||
}
|
||||
|
||||
if processState != nil {
|
||||
exitCode := processState.ExitCode()
|
||||
gLog.i("worker process exited with code: %d", exitCode)
|
||||
|
||||
if exitCode == 9 {
|
||||
gLog.i("worker process update with code: %d", exitCode)
|
||||
// os.Exit(9) // old client installed system service will not auto restart. fuck
|
||||
}
|
||||
}
|
||||
// Write the current time to the end of the dump file
|
||||
currentTime := time.Now().Format("2006-01-02 15:04:05")
|
||||
_, err = f.WriteString("\nProcess ended at: " + currentTime + "\n")
|
||||
if err != nil {
|
||||
gLog.e("Failed to write time to dump file: %s", err)
|
||||
}
|
||||
|
||||
f.Close()
|
||||
time.Sleep(time.Second)
|
||||
err = os.Rename(tmpDump, dumpFile)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "rename dump error:%s", err)
|
||||
gLog.e("rename dump error:%s", err)
|
||||
}
|
||||
if !d.running {
|
||||
return
|
||||
}
|
||||
if time.Since(lastRebootTime) < time.Second*10 {
|
||||
gLog.Printf(LvERROR, "worker stop, restart it after 10s")
|
||||
gLog.e("worker stop, restart it after 10s")
|
||||
time.Sleep(time.Second * 10)
|
||||
}
|
||||
|
||||
@@ -99,13 +118,7 @@ func (d *daemon) run() {
|
||||
}
|
||||
|
||||
func (d *daemon) Control(ctrlComm string, exeAbsPath string, args []string) error {
|
||||
svcConfig := &service.Config{
|
||||
Name: ProductName,
|
||||
DisplayName: ProductName,
|
||||
Description: ProductName,
|
||||
Executable: exeAbsPath,
|
||||
Arguments: args,
|
||||
}
|
||||
svcConfig := getServiceConfig(exeAbsPath, args)
|
||||
|
||||
s, e := service.New(d, svcConfig)
|
||||
if e != nil {
|
||||
@@ -118,3 +131,56 @@ func (d *daemon) Control(ctrlComm string, exeAbsPath string, args []string) erro
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getServiceConfig(exeAbsPath string, args []string) *service.Config {
|
||||
config := &service.Config{
|
||||
Name: ProductName,
|
||||
DisplayName: ProductName,
|
||||
Description: ProductName,
|
||||
Executable: exeAbsPath,
|
||||
Arguments: args,
|
||||
Option: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
setupWindowsConfig(config)
|
||||
} else {
|
||||
setupLinuxConfig(config)
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func setupWindowsConfig(config *service.Config) {
|
||||
failureActions := []map[string]interface{}{
|
||||
{
|
||||
"Type": "restart",
|
||||
"Delay": "10000",
|
||||
},
|
||||
{
|
||||
"Type": "restart",
|
||||
"Delay": "10000",
|
||||
},
|
||||
{
|
||||
"Type": "restart",
|
||||
"Delay": "10000",
|
||||
},
|
||||
}
|
||||
|
||||
config.Option = map[string]interface{}{
|
||||
"OnFailure": "restart",
|
||||
"OnFailureDelay": "10s",
|
||||
"OnFailureResetPeriod": "3600",
|
||||
"FailureActions": failureActions,
|
||||
"DelayedAutoStart": true,
|
||||
}
|
||||
}
|
||||
|
||||
func setupLinuxConfig(config *service.Config) {
|
||||
config.Option = map[string]interface{}{
|
||||
"Restart": "always",
|
||||
"RestartSec": "10",
|
||||
"StartLimitBurst": 64,
|
||||
"SuccessExitStatus": "1 2 8 SIGKILL",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,4 +30,7 @@ var (
|
||||
ErrBuildTunnelBusy = errors.New("build tunnel busy")
|
||||
ErrMemAppTunnelNotFound = errors.New("memapp tunnel not found")
|
||||
ErrRemoteServiceUnable = errors.New("remote service unable")
|
||||
ErrAppWithoutTunnel = errors.New("p2papp has no available tunnel")
|
||||
ErrWriteWindowFull = errors.New("writeWindow full")
|
||||
ErrHeaderDataLen = errors.New("header datalen error")
|
||||
)
|
||||
|
||||
@@ -4,12 +4,14 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"time"
|
||||
|
||||
"github.com/openp2p-cn/totp"
|
||||
@@ -21,119 +23,135 @@ func handlePush(subType uint16, msg []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "handle push msg type:%d, push header:%+v", subType, pushHead)
|
||||
// gLog.d("handle push msg type:%d, push header:%+v", subType, pushHead)
|
||||
switch subType {
|
||||
case MsgPushConnectReq:
|
||||
err = handleConnectReq(msg)
|
||||
case MsgPushRsp:
|
||||
rsp := PushRsp{}
|
||||
if err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp); err != nil {
|
||||
gLog.Printf(LvERROR, "wrong pushRsp:%s", err)
|
||||
gLog.e("Unmarshal pushRsp:%s", err)
|
||||
return err
|
||||
}
|
||||
if rsp.Error == 0 {
|
||||
gLog.Printf(LvDEBUG, "push ok, detail:%s", rsp.Detail)
|
||||
gLog.dev("push ok, detail:%s", rsp.Detail)
|
||||
} else {
|
||||
gLog.Printf(LvERROR, "push error:%d, detail:%s", rsp.Error, rsp.Detail)
|
||||
gLog.e("push error:%d, detail:%s", rsp.Error, rsp.Detail)
|
||||
}
|
||||
case MsgPushAddRelayTunnelReq:
|
||||
req := AddRelayTunnelReq{}
|
||||
if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil {
|
||||
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
|
||||
gLog.e("Unmarshal %v:%s", reflect.TypeOf(req), err)
|
||||
return err
|
||||
}
|
||||
config := AppConfig{}
|
||||
config.PeerNode = req.RelayName
|
||||
config.peerToken = req.RelayToken
|
||||
config.relayMode = req.RelayMode
|
||||
config.PunchPriority = req.PunchPriority
|
||||
config.UnderlayProtocol = req.UnderlayProtocol
|
||||
go func(r AddRelayTunnelReq) {
|
||||
t, errDt := GNetwork.addDirectTunnel(config, 0)
|
||||
if errDt == nil {
|
||||
t, errDt := GNetwork.addDirectTunnel(config, 0, nil)
|
||||
if errDt == nil && t != nil {
|
||||
// notify peer relay ready
|
||||
msg := TunnelMsg{ID: t.id}
|
||||
GNetwork.push(r.From, MsgPushAddRelayTunnelRsp, msg)
|
||||
appConfig := config
|
||||
appConfig.PeerNode = req.From
|
||||
} else {
|
||||
gLog.Printf(LvERROR, "addDirectTunnel error:%s", errDt)
|
||||
gLog.w("addDirectTunnel error:%s", errDt)
|
||||
GNetwork.push(r.From, MsgPushAddRelayTunnelRsp, "error") // compatible with old version client, trigger unmarshal error
|
||||
}
|
||||
}(req)
|
||||
case MsgPushServerSideSaveMemApp:
|
||||
req := ServerSideSaveMemApp{}
|
||||
if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil {
|
||||
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
|
||||
gLog.e("Unmarshal %v:%s", reflect.TypeOf(req), err)
|
||||
return err
|
||||
}
|
||||
gLog.Println(LvDEBUG, "handle MsgPushServerSideSaveMemApp:", prettyJson(req))
|
||||
gLog.d("handle MsgPushServerSideSaveMemApp:%s", prettyJson(req))
|
||||
if req.RelayIndex > uint32(gConf.sdwan.TunnelNum-1) {
|
||||
return errors.New("wrong relay index")
|
||||
}
|
||||
var existTunnel *P2PTunnel
|
||||
i, ok := GNetwork.allTunnels.Load(req.TunnelID)
|
||||
if !ok {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
time.Sleep(time.Millisecond * 3000)
|
||||
i, ok = GNetwork.allTunnels.Load(req.TunnelID) // retry sometimes will receive MsgPushServerSideSaveMemApp but p2ptunnel not store yet.
|
||||
if !ok {
|
||||
gLog.Println(LvERROR, "handle MsgPushServerSideSaveMemApp error:", ErrMemAppTunnelNotFound)
|
||||
gLog.e("handle MsgPushServerSideSaveMemApp error:%s", ErrMemAppTunnelNotFound)
|
||||
return ErrMemAppTunnelNotFound
|
||||
}
|
||||
}
|
||||
existTunnel = i.(*P2PTunnel)
|
||||
peerID := NodeNameToID(req.From)
|
||||
existApp, appok := GNetwork.apps.Load(peerID)
|
||||
appIdx := peerID
|
||||
if req.SrcPort != 0 {
|
||||
appIdx = req.AppID
|
||||
}
|
||||
existApp, appok := GNetwork.apps.Load(appIdx)
|
||||
var app *p2pApp
|
||||
if appok {
|
||||
app := existApp.(*p2pApp)
|
||||
app = existApp.(*p2pApp)
|
||||
if app.tunnelNum != int(req.TunnelNum) {
|
||||
gLog.d("memapp tunnelNum changed from %d to %d", app.tunnelNum, req.TunnelNum)
|
||||
GNetwork.DeleteApp(app.config)
|
||||
app = nil
|
||||
}
|
||||
}
|
||||
if app != nil {
|
||||
app.config.AppName = fmt.Sprintf("%d", peerID)
|
||||
app.id = req.AppID
|
||||
app.setRelayTunnelID(req.RelayTunnelID)
|
||||
app.relayMode = req.RelayMode
|
||||
app.hbTimeRelay = time.Now()
|
||||
if req.RelayTunnelID == 0 {
|
||||
app.setDirectTunnel(existTunnel)
|
||||
} else {
|
||||
app.setRelayTunnel(existTunnel)
|
||||
app.key = req.AppKey
|
||||
app.PreCalcKeyBytes()
|
||||
app.relayMode[req.RelayIndex] = req.RelayMode
|
||||
app.hbTime[req.RelayIndex] = time.Now()
|
||||
app.SetTunnel(existTunnel, int(req.RelayIndex))
|
||||
if req.RelayTunnelID != 0 {
|
||||
app.SetRelayTunnelID(req.RelayTunnelID, int(req.RelayIndex)) // direct tunnel rtid=0, no need set rtid
|
||||
}
|
||||
gLog.Println(LvDEBUG, "find existing memapp, update it")
|
||||
gLog.d("found existing memapp, update it")
|
||||
} else {
|
||||
appConfig := existTunnel.config
|
||||
appConfig.SrcPort = 0
|
||||
appConfig.SrcPort = int(req.SrcPort)
|
||||
appConfig.Protocol = ""
|
||||
appConfig.AppName = fmt.Sprintf("%d", peerID)
|
||||
appConfig.PeerNode = req.From
|
||||
app := p2pApp{
|
||||
id: req.AppID,
|
||||
config: appConfig,
|
||||
relayMode: req.RelayMode,
|
||||
running: true,
|
||||
hbTimeRelay: time.Now(),
|
||||
app = &p2pApp{
|
||||
id: req.AppID,
|
||||
config: appConfig,
|
||||
running: true,
|
||||
// asyncWriteChan: make(chan []byte, WriteDataChanSize),
|
||||
key: req.AppKey,
|
||||
}
|
||||
if req.RelayTunnelID == 0 {
|
||||
app.setDirectTunnel(existTunnel)
|
||||
} else {
|
||||
app.setRelayTunnel(existTunnel)
|
||||
app.setRelayTunnelID(req.RelayTunnelID)
|
||||
app.PreCalcKeyBytes()
|
||||
tunnelNum := 2
|
||||
if req.TunnelNum > uint32(tunnelNum) {
|
||||
tunnelNum = int(req.TunnelNum)
|
||||
}
|
||||
app.Init(tunnelNum)
|
||||
app.relayMode[req.RelayIndex] = req.RelayMode
|
||||
app.hbTime[req.RelayIndex] = time.Now()
|
||||
app.SetTunnel(existTunnel, int(req.RelayIndex))
|
||||
if req.RelayTunnelID != 0 {
|
||||
app.relayNode = req.Node
|
||||
app.SetRelayTunnelID(req.RelayTunnelID, int(req.RelayIndex))
|
||||
app.relayNode[req.RelayIndex] = req.Node
|
||||
}
|
||||
GNetwork.apps.Store(NodeNameToID(req.From), &app)
|
||||
app.Start(false)
|
||||
GNetwork.apps.Store(appIdx, app)
|
||||
gLog.d("store memapp %d %d", appIdx, req.SrcPort)
|
||||
}
|
||||
|
||||
return nil
|
||||
case MsgPushAPPKey:
|
||||
req := APPKeySync{}
|
||||
if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil {
|
||||
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
|
||||
return err
|
||||
}
|
||||
SaveKey(req.AppID, req.AppKey)
|
||||
case MsgPushUpdate:
|
||||
gLog.Println(LvINFO, "MsgPushUpdate")
|
||||
gLog.i("MsgPushUpdate")
|
||||
err := update(gConf.Network.ServerHost, gConf.Network.ServerPort)
|
||||
if err == nil {
|
||||
os.Exit(0)
|
||||
os.Exit(9) // 9 tell daemon this exit because of update
|
||||
}
|
||||
return err
|
||||
case MsgPushRestart:
|
||||
gLog.Println(LvINFO, "MsgPushRestart")
|
||||
gLog.i("MsgPushRestart")
|
||||
os.Exit(0)
|
||||
return err
|
||||
case MsgPushReportApps:
|
||||
@@ -144,43 +162,62 @@ func handlePush(subType uint16, msg []byte) error {
|
||||
err = handleLog(msg)
|
||||
case MsgPushReportGoroutine:
|
||||
err = handleReportGoroutine()
|
||||
case MsgPushReportHeap:
|
||||
err = handleReportHeap()
|
||||
case MsgPushCheckRemoteService:
|
||||
err = handleCheckRemoteService(msg)
|
||||
case MsgPushEditApp:
|
||||
err = handleEditApp(msg)
|
||||
case MsgPushEditNode:
|
||||
gLog.Println(LvINFO, "MsgPushEditNode")
|
||||
gLog.i("MsgPushEditNode")
|
||||
req := EditNode{}
|
||||
if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil {
|
||||
gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:]))
|
||||
gLog.e("Unmarshal %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:]))
|
||||
return err
|
||||
}
|
||||
gConf.setNode(req.NewName)
|
||||
gConf.setShareBandwidth(req.Bandwidth)
|
||||
if req.PublicIPPort != 0 {
|
||||
gConf.Network.PublicIPPort = req.PublicIPPort
|
||||
}
|
||||
gConf.Forcev6 = (req.Forcev6 != 0)
|
||||
gLog.i("set forcev6 to %v", gConf.Forcev6)
|
||||
gConf.save()
|
||||
os.Exit(0)
|
||||
case MsgPushSwitchApp:
|
||||
gLog.Println(LvINFO, "MsgPushSwitchApp")
|
||||
gLog.i("MsgPushSwitchApp")
|
||||
app := AppInfo{}
|
||||
if err = json.Unmarshal(msg[openP2PHeaderSize:], &app); err != nil {
|
||||
gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(app), err, string(msg[openP2PHeaderSize:]))
|
||||
gLog.e("Unmarshal %v:%s %s", reflect.TypeOf(app), err, string(msg[openP2PHeaderSize:]))
|
||||
return err
|
||||
}
|
||||
config := AppConfig{Enabled: app.Enabled, SrcPort: app.SrcPort, Protocol: app.Protocol}
|
||||
gLog.Println(LvINFO, app.AppName, " switch to ", app.Enabled)
|
||||
config := AppConfig{PeerNode: app.PeerNode, Enabled: app.Enabled, SrcPort: app.SrcPort, Protocol: app.Protocol}
|
||||
gLog.i("%s switch to %d", app.AppName, app.Enabled)
|
||||
gConf.switchApp(config, app.Enabled)
|
||||
if app.Enabled == 0 {
|
||||
// disable APP
|
||||
GNetwork.DeleteApp(config)
|
||||
}
|
||||
case MsgPushDstNodeOnline:
|
||||
gLog.Println(LvINFO, "MsgPushDstNodeOnline")
|
||||
req := PushDstNodeOnline{}
|
||||
if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil {
|
||||
gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:]))
|
||||
gLog.e("Unmarshal %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:]))
|
||||
return err
|
||||
}
|
||||
gLog.Println(LvINFO, "retry peerNode ", req.Node)
|
||||
gLog.i("%s online, retryApp", req.Node)
|
||||
gConf.retryApp(req.Node)
|
||||
case MsgPushSpecTunnel:
|
||||
req := SpecTunnel{}
|
||||
if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil {
|
||||
gLog.e("Unmarshal %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:]))
|
||||
return err
|
||||
}
|
||||
gLog.i("SpecTunnel %d", req.TunnelIndex)
|
||||
gConf.Network.specTunnel = int(req.TunnelIndex)
|
||||
case MsgPushSDWanRefresh:
|
||||
GNetwork.write(MsgSDWAN, MsgSDWANInfoReq, nil)
|
||||
case MsgPushNat4Detect:
|
||||
handleNat4Detect(msg)
|
||||
default:
|
||||
i, ok := GNetwork.msgMap.Load(pushHead.From)
|
||||
if !ok {
|
||||
@@ -192,11 +229,50 @@ func handlePush(subType uint16, msg []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func handleNat4Detect(msg []byte) (err error) {
|
||||
gLog.d("handleNat4Detect")
|
||||
nd := Nat4Detect{}
|
||||
if err = json.Unmarshal(msg[openP2PHeaderSize:], &nd); err != nil {
|
||||
gLog.e("Unmarshal %v:%s %s", reflect.TypeOf(nd), err, string(msg[openP2PHeaderSize:]))
|
||||
return err
|
||||
}
|
||||
detectNatPort := func(protocol, server string, serverPort, localPort int) int {
|
||||
natPort := 0
|
||||
if protocol == "tcp" {
|
||||
_, natPort, _, _ = natDetectTCP(server, serverPort, localPort)
|
||||
} else {
|
||||
_, natPort, _ = natDetectUDP(server, serverPort, localPort)
|
||||
}
|
||||
// gLog.i("%s %s %d %d %d", protocol, server, serverPort, localPort, natPort)
|
||||
return natPort
|
||||
}
|
||||
|
||||
result := ""
|
||||
if nd.Num > 0 {
|
||||
for i := 0; i < int(nd.Num); i++ {
|
||||
natPort := detectNatPort(nd.Protocol, nd.Server, int(nd.ServerPort), int(nd.LocalPort)+i)
|
||||
if i > 0 {
|
||||
result += ","
|
||||
}
|
||||
result += fmt.Sprintf("%d", natPort)
|
||||
}
|
||||
} else {
|
||||
for idx, item := range nd.CustomData {
|
||||
natPort := detectNatPort(item.Protocol, item.Server, int(item.ServerPort), int(item.LocalPort))
|
||||
if idx > 0 {
|
||||
result += ","
|
||||
}
|
||||
result += fmt.Sprintf("%d", natPort)
|
||||
}
|
||||
}
|
||||
return GNetwork.write(MsgReport, MsgPushReportLog, &result)
|
||||
}
|
||||
|
||||
func handleEditApp(msg []byte) (err error) {
|
||||
gLog.Println(LvINFO, "MsgPushEditApp")
|
||||
gLog.i("MsgPushEditApp")
|
||||
newApp := AppInfo{}
|
||||
if err = json.Unmarshal(msg[openP2PHeaderSize:], &newApp); err != nil {
|
||||
gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(newApp), err, string(msg[openP2PHeaderSize:]))
|
||||
gLog.e("Unmarshal %v:%s %s", reflect.TypeOf(newApp), err, string(msg[openP2PHeaderSize:]))
|
||||
return err
|
||||
}
|
||||
oldConf := AppConfig{Enabled: 1}
|
||||
@@ -212,13 +288,16 @@ func handleEditApp(msg []byte) (err error) {
|
||||
gConf.delete(oldConf)
|
||||
}
|
||||
|
||||
// AddApp
|
||||
newConf := oldConf
|
||||
newConf.Protocol = newApp.Protocol
|
||||
newConf.SrcPort = newApp.SrcPort
|
||||
newConf.RelayNode = newApp.SpecRelayNode
|
||||
newConf.PunchPriority = newApp.PunchPriority
|
||||
gConf.add(newConf, false)
|
||||
if newApp.SrcPort != 0 { // delete app
|
||||
// AddApp
|
||||
newConf := oldConf
|
||||
newConf.Protocol = newApp.Protocol
|
||||
newConf.SrcPort = newApp.SrcPort
|
||||
newConf.RelayNode = newApp.SpecRelayNode
|
||||
newConf.PunchPriority = newApp.PunchPriority
|
||||
gConf.add(newConf, false)
|
||||
}
|
||||
|
||||
if newApp.Protocol0 != "" && newApp.SrcPort0 != 0 { // not edit
|
||||
GNetwork.DeleteApp(oldConf) // DeleteApp may cost some times, execute at the end
|
||||
}
|
||||
@@ -228,13 +307,12 @@ func handleEditApp(msg []byte) (err error) {
|
||||
func handleConnectReq(msg []byte) (err error) {
|
||||
req := PushConnectReq{}
|
||||
if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil {
|
||||
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
|
||||
gLog.e("Unmarshal %v:%s", reflect.TypeOf(req), err)
|
||||
return err
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "%s is connecting...", req.From)
|
||||
gLog.Println(LvDEBUG, "push connect response to ", req.From)
|
||||
gLog.d("%s is connecting... push connect response", req.From)
|
||||
if compareVersion(req.Version, LeastSupportVersion) < 0 {
|
||||
gLog.Println(LvERROR, ErrVersionNotCompatible.Error(), ":", req.From)
|
||||
gLog.e("%s:%s", ErrVersionNotCompatible.Error(), req.From)
|
||||
rsp := PushConnectRsp{
|
||||
Error: 10,
|
||||
Detail: ErrVersionNotCompatible.Error(),
|
||||
@@ -247,7 +325,7 @@ func handleConnectReq(msg []byte) (err error) {
|
||||
// verify totp token or token
|
||||
t := totp.TOTP{Step: totp.RelayTOTPStep}
|
||||
if t.Verify(req.Token, gConf.Network.Token, time.Now().Unix()-GNetwork.dt/int64(time.Second)) { // localTs may behind, auto adjust ts
|
||||
gLog.Printf(LvINFO, "Access Granted")
|
||||
gLog.d("handleConnectReq Access Granted")
|
||||
config := AppConfig{}
|
||||
config.peerNatType = req.NatType
|
||||
config.peerConeNatPort = req.ConeNatPort
|
||||
@@ -263,16 +341,16 @@ func handleConnectReq(msg []byte) (err error) {
|
||||
config.UnderlayProtocol = req.UnderlayProtocol
|
||||
// share relay node will limit bandwidth
|
||||
if req.Token != gConf.Network.Token {
|
||||
gLog.Printf(LvINFO, "set share bandwidth %d mbps", gConf.Network.ShareBandwidth)
|
||||
gLog.i("set share bandwidth %d mbps", gConf.Network.ShareBandwidth)
|
||||
config.shareBandwidth = gConf.Network.ShareBandwidth
|
||||
}
|
||||
// go GNetwork.AddTunnel(config, req.ID)
|
||||
go func() {
|
||||
GNetwork.addDirectTunnel(config, req.ID)
|
||||
GNetwork.addDirectTunnel(config, req.ID, nil)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
gLog.Println(LvERROR, "Access Denied:", req.From)
|
||||
gLog.e("handleConnectReq Access Denied:%s", req.From)
|
||||
rsp := PushConnectRsp{
|
||||
Error: 1,
|
||||
Detail: fmt.Sprintf("connect to %s error: Access Denied", gConf.Network.Node),
|
||||
@@ -283,10 +361,10 @@ func handleConnectReq(msg []byte) (err error) {
|
||||
}
|
||||
|
||||
func handleReportApps() (err error) {
|
||||
gLog.Println(LvINFO, "MsgPushReportApps")
|
||||
gLog.i("MsgPushReportApps")
|
||||
req := ReportApps{}
|
||||
gConf.mtx.Lock()
|
||||
defer gConf.mtx.Unlock()
|
||||
gConf.mtx.RLock()
|
||||
defer gConf.mtx.RUnlock()
|
||||
|
||||
for _, config := range gConf.Apps {
|
||||
appActive := 0
|
||||
@@ -296,28 +374,24 @@ func handleReportApps() (err error) {
|
||||
linkMode := LinkModeUDPPunch
|
||||
var connectTime string
|
||||
var retryTime string
|
||||
var app *p2pApp
|
||||
i, ok := GNetwork.apps.Load(config.ID())
|
||||
if ok {
|
||||
app = i.(*p2pApp)
|
||||
if app.isActive() {
|
||||
app := GNetwork.findApp(config)
|
||||
if app != nil {
|
||||
|
||||
if app.IsActive() {
|
||||
appActive = 1
|
||||
}
|
||||
if app.config.SrcPort == 0 { // memapp
|
||||
continue
|
||||
}
|
||||
specRelayNode = app.config.RelayNode
|
||||
if !app.isDirect() { // TODO: should always report relay node for app edit
|
||||
relayNode = app.relayNode
|
||||
relayMode = app.relayMode
|
||||
t, tidx := app.AvailableTunnel()
|
||||
if tidx != 0 { // TODO: should always report relay node for app edit
|
||||
relayNode = app.relayNode[tidx]
|
||||
relayMode = app.relayMode[tidx]
|
||||
}
|
||||
|
||||
if app.Tunnel() != nil {
|
||||
linkMode = app.Tunnel().linkModeWeb
|
||||
if t != nil {
|
||||
linkMode = t.linkModeWeb
|
||||
}
|
||||
retryTime = app.RetryTime().Local().Format("2006-01-02T15:04:05-0700")
|
||||
connectTime = app.ConnectTime().Local().Format("2006-01-02T15:04:05-0700")
|
||||
|
||||
}
|
||||
appInfo := AppInfo{
|
||||
AppName: config.AppName,
|
||||
@@ -348,10 +422,8 @@ func handleReportApps() (err error) {
|
||||
}
|
||||
|
||||
func handleReportMemApps() (err error) {
|
||||
gLog.Println(LvINFO, "handleReportMemApps")
|
||||
gLog.i("handleReportMemApps")
|
||||
req := ReportApps{}
|
||||
gConf.mtx.Lock()
|
||||
defer gConf.mtx.Unlock()
|
||||
GNetwork.sdwan.sysRoute.Range(func(key, value interface{}) bool {
|
||||
node := value.(*sdwanNode)
|
||||
appActive := 0
|
||||
@@ -361,13 +433,16 @@ func handleReportMemApps() (err error) {
|
||||
|
||||
i, ok := GNetwork.apps.Load(node.id)
|
||||
var app *p2pApp
|
||||
var t *P2PTunnel
|
||||
var tidx int
|
||||
if ok {
|
||||
app = i.(*p2pApp)
|
||||
if app.isActive() {
|
||||
t, tidx = app.AvailableTunnel()
|
||||
if app.IsActive() {
|
||||
appActive = 1
|
||||
}
|
||||
if !app.isDirect() {
|
||||
relayMode = app.relayMode
|
||||
if tidx != 0 {
|
||||
relayMode = app.relayMode[tidx]
|
||||
}
|
||||
retryTime = app.RetryTime().Local().Format("2006-01-02T15:04:05-0700")
|
||||
connectTime = app.ConnectTime().Local().Format("2006-01-02T15:04:05-0700")
|
||||
@@ -384,12 +459,13 @@ func handleReportMemApps() (err error) {
|
||||
appInfo.Protocol = app.config.Protocol
|
||||
appInfo.Whitelist = app.config.Whitelist
|
||||
appInfo.SrcPort = app.config.SrcPort
|
||||
if !app.isDirect() {
|
||||
appInfo.RelayNode = app.relayNode
|
||||
|
||||
if tidx != 0 {
|
||||
appInfo.RelayNode = app.relayNode[tidx]
|
||||
}
|
||||
|
||||
if app.Tunnel() != nil {
|
||||
appInfo.LinkMode = app.Tunnel().linkModeWeb
|
||||
if t != nil {
|
||||
appInfo.LinkMode = t.linkModeWeb
|
||||
}
|
||||
appInfo.DstHost = app.config.DstHost
|
||||
appInfo.DstPort = app.config.DstPort
|
||||
@@ -402,17 +478,19 @@ func handleReportMemApps() (err error) {
|
||||
req.Apps = append(req.Apps, appInfo)
|
||||
return true
|
||||
})
|
||||
gLog.Println(LvDEBUG, "handleReportMemApps res:", prettyJson(req))
|
||||
req.TunError = GNetwork.sdwan.tunErr
|
||||
gLog.d("handleReportMemApps res:%s", prettyJson(req))
|
||||
gConf.retryAllMemApp()
|
||||
return GNetwork.write(MsgReport, MsgReportMemApps, &req)
|
||||
}
|
||||
|
||||
func handleLog(msg []byte) (err error) {
|
||||
gLog.Println(LvDEBUG, "MsgPushReportLog")
|
||||
gLog.d("MsgPushReportLog")
|
||||
const defaultLen = 1024 * 128
|
||||
const maxLen = 1024 * 1024
|
||||
req := ReportLogReq{}
|
||||
if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil {
|
||||
gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:]))
|
||||
gLog.e("Unmarshal %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:]))
|
||||
return err
|
||||
}
|
||||
if req.FileName == "" {
|
||||
@@ -420,9 +498,12 @@ func handleLog(msg []byte) (err error) {
|
||||
} else {
|
||||
req.FileName = sanitizeFileName(req.FileName)
|
||||
}
|
||||
if req.IsSetLogLevel == 1 {
|
||||
gLog.setLevel(LogLevel(req.LogLevel))
|
||||
}
|
||||
f, err := os.Open(filepath.Join("log", req.FileName))
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "read log file error:", err)
|
||||
gLog.e("read log file error:%s", err)
|
||||
return err
|
||||
}
|
||||
fi, err := f.Stat()
|
||||
@@ -445,7 +526,7 @@ func handleLog(msg []byte) (err error) {
|
||||
readLength, err := f.Read(buff)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "read log content error:", err)
|
||||
gLog.e("read log content error:%s", err)
|
||||
return err
|
||||
}
|
||||
rsp := ReportLogRsp{}
|
||||
@@ -457,17 +538,27 @@ func handleLog(msg []byte) (err error) {
|
||||
}
|
||||
|
||||
func handleReportGoroutine() (err error) {
|
||||
gLog.Println(LvDEBUG, "handleReportGoroutine")
|
||||
gLog.d("handleReportGoroutine")
|
||||
buf := make([]byte, 1024*128)
|
||||
stackLen := runtime.Stack(buf, true)
|
||||
return GNetwork.write(MsgReport, MsgPushReportLog, string(buf[:stackLen]))
|
||||
return GNetwork.write(MsgReport, MsgReportResponse, string(buf[:stackLen]))
|
||||
}
|
||||
|
||||
func handleReportHeap() error {
|
||||
var buf bytes.Buffer
|
||||
err := pprof.Lookup("heap").WriteTo(&buf, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return GNetwork.write(MsgReport, MsgReportResponse, buf.String())
|
||||
}
|
||||
|
||||
func handleCheckRemoteService(msg []byte) (err error) {
|
||||
gLog.Println(LvDEBUG, "handleCheckRemoteService")
|
||||
gLog.d("handleCheckRemoteService")
|
||||
req := CheckRemoteService{}
|
||||
if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil {
|
||||
gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:]))
|
||||
gLog.e("Unmarshal %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:]))
|
||||
return err
|
||||
}
|
||||
rsp := PushRsp{Error: 0}
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
)
|
||||
|
||||
func handshakeC2C(t *P2PTunnel) (err error) {
|
||||
gLog.Printf(LvDEBUG, "handshakeC2C %s:%d:%d to %s:%d", gConf.Network.Node, t.coneLocalPort, t.coneNatPort, t.config.peerIP, t.config.peerConeNatPort)
|
||||
defer gLog.Printf(LvDEBUG, "handshakeC2C end")
|
||||
gLog.d("handshakeC2C %s:%d:%d to %s:%d", gConf.Network.Node, t.coneLocalPort, t.coneNatPort, t.config.peerIP, t.config.peerConeNatPort)
|
||||
defer gLog.d("handshakeC2C end")
|
||||
conn, err := net.ListenUDP("udp", t.localHoleAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -20,12 +20,12 @@ func handshakeC2C(t *P2PTunnel) (err error) {
|
||||
defer conn.Close()
|
||||
_, err = UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id})
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshake error:", err)
|
||||
gLog.d("handshakeC2C write MsgPunchHandshake error:%s", err)
|
||||
return err
|
||||
}
|
||||
ra, head, buff, _, err := UDPRead(conn, HandshakeTimeout)
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "handshakeC2C read MsgPunchHandshake error:", err)
|
||||
gLog.d("handshakeC2C read MsgPunchHandshake error:%s", err)
|
||||
return err
|
||||
}
|
||||
t.remoteHoleAddr, _ = net.ResolveUDPAddr("udp", ra.String())
|
||||
@@ -39,29 +39,29 @@ func handshakeC2C(t *P2PTunnel) (err error) {
|
||||
tunnelID = t.id
|
||||
}
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake && tunnelID == t.id {
|
||||
gLog.Printf(LvDEBUG, "read %d handshake ", t.id)
|
||||
gLog.d("read tunnelid:%d handshake ", t.id)
|
||||
UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
_, head, _, _, err = UDPRead(conn, HandshakeTimeout)
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err)
|
||||
gLog.d("handshakeC2C write MsgPunchHandshakeAck error:", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck && tunnelID == t.id {
|
||||
gLog.Printf(LvDEBUG, "read %d handshake ack ", t.id)
|
||||
gLog.d("read tunnelID:%d handshake ack ", t.id)
|
||||
_, err = UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err)
|
||||
gLog.d("handshakeC2C write MsgPunchHandshakeAck error:%s", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
gLog.Printf(LvINFO, "handshakeC2C ok")
|
||||
gLog.i("handshakeC2C ok")
|
||||
return nil
|
||||
}
|
||||
|
||||
func handshakeC2S(t *P2PTunnel) error {
|
||||
gLog.Printf(LvDEBUG, "handshakeC2S start")
|
||||
defer gLog.Printf(LvDEBUG, "handshakeC2S end")
|
||||
gLog.d("tid:%d handshakeC2S start", t.id)
|
||||
defer gLog.d("tid:%d handshakeC2S end", t.id)
|
||||
if !buildTunnelMtx.TryLock() {
|
||||
// time.Sleep(time.Second * 3)
|
||||
return ErrBuildTunnelBusy
|
||||
@@ -77,7 +77,7 @@ func handshakeC2S(t *P2PTunnel) error {
|
||||
defer conn.Close()
|
||||
|
||||
go func() error {
|
||||
gLog.Printf(LvDEBUG, "send symmetric handshake to %s from %d:%d start", t.config.peerIP, t.coneLocalPort, t.coneNatPort)
|
||||
gLog.d("tid:%d send symmetric handshake to %s from %d:%d start", t.id, t.config.peerIP, t.coneLocalPort, t.coneNatPort)
|
||||
for i := 0; i < SymmetricHandshakeNum; i++ {
|
||||
// time.Sleep(SymmetricHandshakeInterval)
|
||||
dst, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", t.config.peerIP, randPorts[i]+2))
|
||||
@@ -86,29 +86,29 @@ func handshakeC2S(t *P2PTunnel) error {
|
||||
}
|
||||
_, err = UDPWrite(conn, dst, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id})
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "handshakeC2S write MsgPunchHandshake error:", err)
|
||||
gLog.d("tid:%d handshakeC2S write MsgPunchHandshake error:%s", t.id, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
gLog.Println(LvDEBUG, "send symmetric handshake end")
|
||||
gLog.d("tid:%d send symmetric handshake end", t.id)
|
||||
return nil
|
||||
}()
|
||||
err = conn.SetReadDeadline(time.Now().Add(HandshakeTimeout))
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "SymmetricHandshakeAckTimeout SetReadDeadline error")
|
||||
gLog.d("tid:%d SymmetricHandshakeAckTimeout SetReadDeadline error", t.id)
|
||||
return err
|
||||
}
|
||||
// read response of the punching hole ok port
|
||||
buff := make([]byte, 1024)
|
||||
_, dst, err := conn.ReadFrom(buff)
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "handshakeC2S wait timeout")
|
||||
gLog.d("tid:%d handshakeC2S wait timeout", t.id)
|
||||
return err
|
||||
}
|
||||
head := &openP2PHeader{}
|
||||
err = binary.Read(bytes.NewReader(buff[:openP2PHeaderSize]), binary.LittleEndian, head)
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "parse p2pheader error:", err)
|
||||
gLog.e("tid:%d parse p2pheader error:%s", t.id, err)
|
||||
return err
|
||||
}
|
||||
t.remoteHoleAddr, _ = net.ResolveUDPAddr("udp", dst.String())
|
||||
@@ -122,12 +122,12 @@ func handshakeC2S(t *P2PTunnel) error {
|
||||
tunnelID = t.id
|
||||
}
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake && tunnelID == t.id {
|
||||
gLog.Printf(LvDEBUG, "handshakeC2S read %d handshake ", t.id)
|
||||
gLog.d("tid:%d handshakeC2S read handshake ", t.id)
|
||||
UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
for {
|
||||
_, head, buff, _, err = UDPRead(conn, HandshakeTimeout)
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "handshakeC2S handshake error")
|
||||
gLog.d("tid:%d handshakeC2S handshake error", t.id)
|
||||
return err
|
||||
}
|
||||
var tunnelID uint64
|
||||
@@ -146,35 +146,35 @@ func handshakeC2S(t *P2PTunnel) error {
|
||||
}
|
||||
}
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
|
||||
gLog.Printf(LvDEBUG, "handshakeC2S read %d handshake ack %s", t.id, t.remoteHoleAddr.String())
|
||||
gLog.d("tid:%d handshakeC2S read handshake ack %s", t.id, t.remoteHoleAddr.String())
|
||||
_, err = UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
return err
|
||||
} else {
|
||||
gLog.Println(LvDEBUG, "handshakeS2C read msg but not MsgPunchHandshakeAck")
|
||||
gLog.d("tid:%d handshakeS2C read msg but not MsgPunchHandshakeAck", t.id)
|
||||
}
|
||||
gLog.Printf(LvINFO, "handshakeC2S ok. cost %d ms", time.Since(startTime)/time.Millisecond)
|
||||
gLog.i("tid:%d handshakeC2S ok. cost %d ms", t.id, time.Since(startTime)/time.Millisecond)
|
||||
return nil
|
||||
}
|
||||
|
||||
func handshakeS2C(t *P2PTunnel) error {
|
||||
gLog.Printf(LvDEBUG, "handshakeS2C start")
|
||||
defer gLog.Printf(LvDEBUG, "handshakeS2C end")
|
||||
gLog.d("tid:%d handshakeS2C start", t.id)
|
||||
defer gLog.d("tid:%d handshakeS2C end", t.id)
|
||||
if !buildTunnelMtx.TryLock() {
|
||||
// time.Sleep(time.Second * 3)
|
||||
return ErrBuildTunnelBusy
|
||||
}
|
||||
defer buildTunnelMtx.Unlock()
|
||||
startTime := time.Now()
|
||||
gotCh := make(chan *net.UDPAddr, 5)
|
||||
gotCh := make(chan *net.UDPAddr, 50)
|
||||
// sequencely udp send handshake, do not parallel send
|
||||
gLog.Printf(LvDEBUG, "send symmetric handshake to %s:%d start", t.config.peerIP, t.config.peerConeNatPort)
|
||||
gLog.d("tid:%d send symmetric handshake to %s:%d start", t.id, t.config.peerIP, t.config.peerConeNatPort)
|
||||
gotIt := false
|
||||
for i := 0; i < SymmetricHandshakeNum; i++ {
|
||||
// time.Sleep(SymmetricHandshakeInterval)
|
||||
go func(t *P2PTunnel) error {
|
||||
conn, err := net.ListenUDP("udp", nil) // TODO: system allocated port really random?
|
||||
if err != nil {
|
||||
gLog.Printf(LvDEBUG, "listen error")
|
||||
gLog.d("tid:%d listen error", t.id)
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
@@ -198,13 +198,13 @@ func handshakeS2C(t *P2PTunnel) error {
|
||||
}
|
||||
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake && tunnelID == t.id {
|
||||
gLog.Printf(LvDEBUG, "handshakeS2C read %d handshake ", t.id)
|
||||
gLog.d("tid:%d handshakeS2C read handshake ", t.id)
|
||||
UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
// may read several MsgPunchHandshake
|
||||
for {
|
||||
_, head, buff, _, err = UDPRead(conn, HandshakeTimeout)
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "handshakeS2C handshake error")
|
||||
gLog.d("tid:%d handshakeS2C handshake error", t.id)
|
||||
return err
|
||||
}
|
||||
if len(buff) > openP2PHeaderSize {
|
||||
@@ -218,36 +218,35 @@ func handshakeS2C(t *P2PTunnel) error {
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck && tunnelID == t.id {
|
||||
break
|
||||
} else {
|
||||
gLog.Println(LvDEBUG, "handshakeS2C read msg but not MsgPunchHandshakeAck")
|
||||
gLog.d("tid:%d handshakeS2C read msg but not MsgPunchHandshakeAck", t.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
|
||||
gLog.Printf(LvDEBUG, "handshakeS2C read %d handshake ack %s", t.id, conn.LocalAddr().String())
|
||||
gLog.d("tid:%d handshakeS2C read handshake ack %s", t.id, conn.LocalAddr().String())
|
||||
UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
gotIt = true
|
||||
la, _ := net.ResolveUDPAddr("udp", conn.LocalAddr().String())
|
||||
gotCh <- la
|
||||
return nil
|
||||
} else {
|
||||
gLog.Println(LvDEBUG, "handshakeS2C read msg but not MsgPunchHandshakeAck")
|
||||
gLog.d("tid:%d handshakeS2C read msg but not MsgPunchHandshakeAck", t.id)
|
||||
}
|
||||
return nil
|
||||
}(t)
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "send symmetric handshake end")
|
||||
gLog.d("tid:%d send symmetric handshake end", t.id)
|
||||
if compareVersion(t.config.peerVersion, SymmetricSimultaneouslySendVersion) < 0 { // compatible with old client
|
||||
gLog.Println(LvDEBUG, "handshakeS2C ready, notify peer connect")
|
||||
gLog.d("tid:%d handshakeS2C ready, notify peer connect", t.id)
|
||||
GNetwork.push(t.config.PeerNode, MsgPushHandshakeStart, TunnelMsg{ID: t.id})
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(HandshakeTimeout):
|
||||
return fmt.Errorf("wait handshake timeout")
|
||||
return fmt.Errorf("tid:%d wait handshake timeout", t.id)
|
||||
case la := <-gotCh:
|
||||
t.localHoleAddr = la
|
||||
gLog.Println(LvDEBUG, "symmetric handshake ok", la)
|
||||
gLog.Printf(LvINFO, "handshakeS2C ok. cost %dms", time.Since(startTime)/time.Millisecond)
|
||||
gLog.i("tid:%d handshakeS2C ok. cost %dms", t.id, time.Since(startTime)/time.Millisecond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,26 +11,14 @@ import (
|
||||
)
|
||||
|
||||
func install() {
|
||||
gLog.Println(LvINFO, "openp2p start. version: ", OpenP2PVersion)
|
||||
gLog.Println(LvINFO, "Contact: QQ group 16947733, Email openp2p.cn@gmail.com")
|
||||
gLog.Println(LvINFO, "install start")
|
||||
defer gLog.Println(LvINFO, "install end")
|
||||
// auto uninstall
|
||||
err := os.MkdirAll(defaultInstallPath, 0775)
|
||||
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "MkdirAll %s error:%s", defaultInstallPath, err)
|
||||
return
|
||||
}
|
||||
err = os.Chdir(defaultInstallPath)
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "cd error:", err)
|
||||
return
|
||||
}
|
||||
|
||||
uninstall()
|
||||
// save config file
|
||||
gLog.i("openp2p start. version: %s", OpenP2PVersion)
|
||||
gLog.i("Contact: QQ group 16947733, Email openp2p.cn@gmail.com")
|
||||
gLog.i("install start")
|
||||
defer gLog.i("install end")
|
||||
parseParams("install", "")
|
||||
// auto uninstall
|
||||
uninstall(false)
|
||||
gLog.i("install path: %s", defaultInstallPath)
|
||||
targetPath := filepath.Join(defaultInstallPath, defaultBinName)
|
||||
d := daemon{}
|
||||
// copy files
|
||||
@@ -38,38 +26,42 @@ func install() {
|
||||
binPath, _ := os.Executable()
|
||||
src, errFiles := os.Open(binPath) // can not use args[0], on Windows call openp2p is ok(=openp2p.exe)
|
||||
if errFiles != nil {
|
||||
gLog.Printf(LvERROR, "os.OpenFile %s error:%s", os.Args[0], errFiles)
|
||||
gLog.e("os.Open %s error:%s", os.Args[0], errFiles)
|
||||
return
|
||||
}
|
||||
|
||||
dst, errFiles := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0775)
|
||||
if errFiles != nil {
|
||||
gLog.Printf(LvERROR, "os.OpenFile %s error:%s", targetPath, errFiles)
|
||||
return
|
||||
time.Sleep(time.Second * 5) // maybe windows defender occupied the file, retry
|
||||
dst, errFiles = os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0775)
|
||||
if errFiles != nil {
|
||||
gLog.e("os.OpenFile %s error:%s", targetPath, errFiles)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_, errFiles = io.Copy(dst, src)
|
||||
if errFiles != nil {
|
||||
gLog.Printf(LvERROR, "io.Copy error:%s", errFiles)
|
||||
gLog.e("io.Copy error:%s", errFiles)
|
||||
return
|
||||
}
|
||||
src.Close()
|
||||
dst.Close()
|
||||
|
||||
// install system service
|
||||
gLog.Println(LvINFO, "targetPath:", targetPath)
|
||||
err = d.Control("install", targetPath, []string{"-d"})
|
||||
err := d.Control("install", targetPath, []string{"-d"})
|
||||
if err == nil {
|
||||
gLog.Println(LvINFO, "install system service ok.")
|
||||
gLog.i("install system service ok.")
|
||||
}
|
||||
time.Sleep(time.Second * 2)
|
||||
err = d.Control("start", targetPath, []string{"-d"})
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "start openp2p service error:", err)
|
||||
gLog.e("start openp2p service error:%s", err)
|
||||
} else {
|
||||
gLog.Println(LvINFO, "start openp2p service ok.")
|
||||
gLog.i("start openp2p service ok.")
|
||||
}
|
||||
gLog.Println(LvINFO, "Visit WebUI on https://console.openp2p.cn")
|
||||
gConf.save()
|
||||
gLog.i("Visit WebUI on https://console.openp2p.cn")
|
||||
}
|
||||
|
||||
func installByFilename() {
|
||||
@@ -79,7 +71,7 @@ func installByFilename() {
|
||||
}
|
||||
serverHost := params[1]
|
||||
token := params[2]
|
||||
gLog.Println(LvINFO, "install start")
|
||||
gLog.i("install start")
|
||||
targetPath := os.Args[0]
|
||||
args := []string{"install"}
|
||||
args = append(args, "-serverhost")
|
||||
@@ -94,31 +86,38 @@ func installByFilename() {
|
||||
cmd.Env = env
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "install by filename, start process error:", err)
|
||||
gLog.e("install by filename, start process error:%s", err)
|
||||
return
|
||||
}
|
||||
gLog.Println(LvINFO, "install end")
|
||||
gLog.Println(LvINFO, "Visit WebUI on https://console.openp2p.cn")
|
||||
gLog.i("install end")
|
||||
gLog.i("Visit WebUI on https://console.openp2p.cn")
|
||||
fmt.Println("Press the Any Key to exit")
|
||||
fmt.Scanln()
|
||||
os.Exit(0)
|
||||
}
|
||||
func uninstall() {
|
||||
gLog.Println(LvINFO, "uninstall start")
|
||||
defer gLog.Println(LvINFO, "uninstall end")
|
||||
|
||||
func uninstall(rmFiles bool) {
|
||||
gLog.i("uninstall start")
|
||||
defer gLog.i("uninstall end")
|
||||
d := daemon{}
|
||||
err := d.Control("stop", "", nil)
|
||||
if err != nil { // service maybe not install
|
||||
return
|
||||
gLog.d("stop service error:%s", err)
|
||||
}
|
||||
err = d.Control("uninstall", "", nil)
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "uninstall system service error:", err)
|
||||
gLog.d("uninstall system service error:%s", err)
|
||||
} else {
|
||||
gLog.Println(LvINFO, "uninstall system service ok.")
|
||||
gLog.i("uninstall system service ok.")
|
||||
}
|
||||
time.Sleep(time.Second * 3)
|
||||
binPath := filepath.Join(defaultInstallPath, defaultBinName)
|
||||
os.Remove(binPath + "0")
|
||||
os.Remove(binPath)
|
||||
// os.RemoveAll(defaultInstallPath) // reserve config.json
|
||||
if rmFiles {
|
||||
if err := os.RemoveAll(defaultInstallPath); err != nil {
|
||||
gLog.e("RemoveAll %s error:%s", defaultInstallPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
201
core/log.go
@@ -3,38 +3,33 @@ package openp2p
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type LogLevel int
|
||||
type LogLevel int32
|
||||
|
||||
var gLog *logger
|
||||
|
||||
const (
|
||||
LvDev LogLevel = -1
|
||||
LvDEBUG LogLevel = iota
|
||||
LvINFO
|
||||
LvWARN
|
||||
LvERROR
|
||||
LvDEBUG LogLevel = 0
|
||||
LvINFO LogLevel = 1
|
||||
LvWARN LogLevel = 2
|
||||
LvERROR LogLevel = 3
|
||||
)
|
||||
|
||||
var (
|
||||
logFileNames map[LogLevel]string
|
||||
loglevel map[LogLevel]string
|
||||
)
|
||||
|
||||
func init() {
|
||||
logFileNames = make(map[LogLevel]string)
|
||||
loglevel = make(map[LogLevel]string)
|
||||
logFileNames[0] = ".log"
|
||||
loglevel[LvDEBUG] = "DEBUG"
|
||||
loglevel[LvINFO] = "INFO"
|
||||
loglevel[LvWARN] = "WARN"
|
||||
loglevel[LvERROR] = "ERROR"
|
||||
loglevel[LvDev] = "Dev"
|
||||
const logFileNames string = ".log"
|
||||
|
||||
var loglevel = map[LogLevel]string{
|
||||
LvDEBUG: "DEBUG",
|
||||
LvINFO: "INFO",
|
||||
LvWARN: "WARN",
|
||||
LvERROR: "ERROR",
|
||||
LvDev: "Dev",
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -43,62 +38,54 @@ const (
|
||||
)
|
||||
|
||||
type logger struct {
|
||||
loggers map[LogLevel]*log.Logger
|
||||
files map[LogLevel]*os.File
|
||||
level LogLevel
|
||||
logDir string
|
||||
mtx *sync.Mutex
|
||||
lineEnding string
|
||||
pid int
|
||||
maxLogSize int64
|
||||
mode int
|
||||
stdLogger *log.Logger
|
||||
logger *log.Logger
|
||||
files *os.File
|
||||
level atomic.Int32
|
||||
logDir string
|
||||
mtx sync.Mutex
|
||||
lineEnding string
|
||||
pid int
|
||||
maxLogSize atomic.Int64
|
||||
mode int
|
||||
stdLogger *log.Logger
|
||||
checkFileRunning bool
|
||||
}
|
||||
|
||||
func NewLogger(path string, filePrefix string, level LogLevel, maxLogSize int64, mode int) *logger {
|
||||
loggers := make(map[LogLevel]*log.Logger)
|
||||
logfiles := make(map[LogLevel]*os.File)
|
||||
var (
|
||||
logdir string
|
||||
)
|
||||
if path == "" {
|
||||
logdir = "log/"
|
||||
} else {
|
||||
logdir = path + "/log/"
|
||||
logdir := filepath.Join(path, "log")
|
||||
if err := os.MkdirAll(logdir, 0755); err != nil && mode&LogFile != 0 {
|
||||
return nil
|
||||
}
|
||||
os.MkdirAll(logdir, 0777)
|
||||
for lv := range logFileNames {
|
||||
logFilePath := logdir + filePrefix + logFileNames[lv]
|
||||
f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
os.Chmod(logFilePath, 0644)
|
||||
logfiles[lv] = f
|
||||
loggers[lv] = log.New(f, "", log.LstdFlags|log.Lmicroseconds)
|
||||
logFilePath := filepath.Join(logdir, filePrefix+logFileNames)
|
||||
f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil && mode&LogFile != 0 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var le string
|
||||
stdLog := log.New(f, "", log.LstdFlags|log.Lmicroseconds)
|
||||
le := "\n"
|
||||
if runtime.GOOS == "windows" {
|
||||
le = "\r\n"
|
||||
} else {
|
||||
le = "\n"
|
||||
}
|
||||
pLog := &logger{loggers, logfiles, level, logdir, &sync.Mutex{}, le, os.Getpid(), maxLogSize, mode, log.New(os.Stdout, "", 0)}
|
||||
pLog := &logger{logger: stdLog,
|
||||
files: f,
|
||||
logDir: logdir,
|
||||
lineEnding: le,
|
||||
pid: os.Getpid(),
|
||||
mode: mode,
|
||||
stdLogger: log.New(os.Stdout, "", 0)}
|
||||
pLog.setMaxSize(maxLogSize)
|
||||
pLog.setLevel(level)
|
||||
pLog.stdLogger.SetFlags(log.LstdFlags | log.Lmicroseconds)
|
||||
go pLog.checkFile()
|
||||
return pLog
|
||||
}
|
||||
|
||||
func (l *logger) setLevel(level LogLevel) {
|
||||
l.mtx.Lock()
|
||||
defer l.mtx.Unlock()
|
||||
l.level = level
|
||||
l.level.Store(int32(level))
|
||||
}
|
||||
|
||||
func (l *logger) setMaxSize(size int64) {
|
||||
l.mtx.Lock()
|
||||
defer l.mtx.Unlock()
|
||||
l.maxLogSize = size
|
||||
l.maxLogSize.Store(size)
|
||||
}
|
||||
|
||||
func (l *logger) setMode(mode int) {
|
||||
@@ -107,49 +94,61 @@ func (l *logger) setMode(mode int) {
|
||||
l.mode = mode
|
||||
}
|
||||
|
||||
func (l *logger) close() {
|
||||
l.checkFileRunning = false
|
||||
l.files.Close()
|
||||
}
|
||||
|
||||
func (l *logger) checkFile() {
|
||||
if l.maxLogSize <= 0 {
|
||||
if l.maxLogSize.Load() <= 0 {
|
||||
return
|
||||
}
|
||||
l.checkFileRunning = true
|
||||
ticker := time.NewTicker(time.Minute)
|
||||
for {
|
||||
for l.checkFileRunning {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
l.mtx.Lock()
|
||||
for lv, logFile := range l.files {
|
||||
f, e := logFile.Stat()
|
||||
if e != nil {
|
||||
continue
|
||||
}
|
||||
if f.Size() <= l.maxLogSize {
|
||||
continue
|
||||
}
|
||||
logFile.Close()
|
||||
fname := f.Name()
|
||||
backupPath := l.logDir + fname + ".0"
|
||||
os.Remove(backupPath)
|
||||
os.Rename(l.logDir+fname, backupPath)
|
||||
newFile, e := os.OpenFile(l.logDir+fname, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if e == nil {
|
||||
l.loggers[lv].SetOutput(newFile)
|
||||
l.files[lv] = newFile
|
||||
}
|
||||
f, e := l.files.Stat()
|
||||
if e != nil {
|
||||
continue
|
||||
}
|
||||
l.mtx.Unlock()
|
||||
if f.Size() <= l.maxLogSize.Load() {
|
||||
continue
|
||||
}
|
||||
l.mtx.Lock()
|
||||
l.files.Close()
|
||||
fname := f.Name()
|
||||
backupPath := filepath.Join(l.logDir, fname+".0")
|
||||
err := os.Remove(backupPath)
|
||||
if err != nil {
|
||||
log.Println("remove openp2p.log0 error:", err)
|
||||
}
|
||||
if err = os.Rename(filepath.Join(l.logDir, fname), backupPath); err != nil {
|
||||
log.Println("rename openp2p.log error:", err)
|
||||
}
|
||||
if newFile, e := os.OpenFile(filepath.Join(l.logDir, fname), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644); e == nil {
|
||||
|
||||
l.logger.SetOutput(newFile)
|
||||
l.files = newFile
|
||||
l.mtx.Unlock()
|
||||
}
|
||||
case <-time.After(time.Second * 1):
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (l *logger) Printf(level LogLevel, format string, params ...interface{}) {
|
||||
l.mtx.Lock()
|
||||
defer l.mtx.Unlock()
|
||||
if level < l.level {
|
||||
if level < LogLevel(l.level.Load()) {
|
||||
return
|
||||
}
|
||||
l.mtx.Lock()
|
||||
defer l.mtx.Unlock()
|
||||
|
||||
pidAndLevel := []interface{}{l.pid, loglevel[level]}
|
||||
params = append(pidAndLevel, params...)
|
||||
if l.mode&LogFile != 0 {
|
||||
l.loggers[0].Printf("%d %s "+format+l.lineEnding, params...)
|
||||
l.logger.Printf("%d %s "+format+l.lineEnding, params...)
|
||||
}
|
||||
if l.mode&LogConsole != 0 {
|
||||
l.stdLogger.Printf("%d %s "+format+l.lineEnding, params...)
|
||||
@@ -157,18 +156,44 @@ func (l *logger) Printf(level LogLevel, format string, params ...interface{}) {
|
||||
}
|
||||
|
||||
func (l *logger) Println(level LogLevel, params ...interface{}) {
|
||||
l.mtx.Lock()
|
||||
defer l.mtx.Unlock()
|
||||
if level < l.level {
|
||||
if level < LogLevel(l.level.Load()) {
|
||||
return
|
||||
}
|
||||
l.mtx.Lock()
|
||||
defer l.mtx.Unlock()
|
||||
pidAndLevel := []interface{}{l.pid, " ", loglevel[level], " "}
|
||||
params = append(pidAndLevel, params...)
|
||||
params = append(params, l.lineEnding)
|
||||
if l.mode&LogFile != 0 {
|
||||
l.loggers[0].Print(params...)
|
||||
l.logger.Print(params...)
|
||||
}
|
||||
if l.mode&LogConsole != 0 {
|
||||
l.stdLogger.Print(params...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *logger) d(format string, params ...interface{}) {
|
||||
l.Printf(LvDEBUG, format, params...)
|
||||
}
|
||||
|
||||
func (l *logger) i(format string, params ...interface{}) {
|
||||
l.Printf(LvINFO, format, params...)
|
||||
}
|
||||
|
||||
func (l *logger) w(format string, params ...interface{}) {
|
||||
l.Printf(LvWARN, format, params...)
|
||||
}
|
||||
|
||||
func (l *logger) e(format string, params ...interface{}) {
|
||||
l.Printf(LvERROR, format, params...)
|
||||
}
|
||||
|
||||
func (l *logger) dev(format string, params ...interface{}) {
|
||||
l.Printf(LvDev, format, params...)
|
||||
}
|
||||
|
||||
func InitForUnitTest(lv LogLevel) {
|
||||
baseDir := filepath.Dir(os.Args[0])
|
||||
os.Chdir(baseDir) // for system service
|
||||
gLog = NewLogger(baseDir, ProductName, lv, 1024*1024, LogFile|LogConsole)
|
||||
}
|
||||
|
||||
165
core/nat.go
@@ -9,53 +9,60 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
upnp "openp2p/pkg/upnp"
|
||||
|
||||
reuse "github.com/openp2p-cn/go-reuseport"
|
||||
)
|
||||
|
||||
func natTCP(serverHost string, serverPort int) (publicIP string, publicPort int, localPort int) {
|
||||
// dialer := &net.Dialer{
|
||||
// LocalAddr: &net.TCPAddr{
|
||||
// IP: net.ParseIP("0.0.0.0"),
|
||||
// Port: localPort,
|
||||
// },
|
||||
// }
|
||||
conn, err := reuse.DialTimeout("tcp4", fmt.Sprintf("%s:%d", "0.0.0.0", 0), fmt.Sprintf("%s:%d", serverHost, serverPort), NatTestTimeout)
|
||||
// conn, err := net.Dial("tcp4", fmt.Sprintf("%s:%d", serverHost, serverPort))
|
||||
// log.Println(LvINFO, conn.LocalAddr())
|
||||
func natDetectTCP(serverHost string, serverPort int, lp int) (publicIP string, publicPort int, localPort int, err error) {
|
||||
gLog.dev("natDetectTCP start")
|
||||
defer gLog.dev("natDetectTCP end")
|
||||
conn, err := reuse.DialTimeout("tcp4", fmt.Sprintf("0.0.0.0:%d", lp), fmt.Sprintf("%s:%d", serverHost, serverPort), NatDetectTimeout)
|
||||
if err != nil {
|
||||
fmt.Printf("Dial tcp4 %s:%d error:%s", serverHost, serverPort, err)
|
||||
err = fmt.Errorf("dial tcp4 %s:%d error: %w", serverHost, serverPort, err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
localPort, _ = strconv.Atoi(strings.Split(conn.LocalAddr().String(), ":")[1])
|
||||
_, wrerr := conn.Write([]byte("1"))
|
||||
if wrerr != nil {
|
||||
fmt.Printf("Write error: %s\n", wrerr)
|
||||
return
|
||||
}
|
||||
b := make([]byte, 1000)
|
||||
conn.SetReadDeadline(time.Now().Add(NatTestTimeout))
|
||||
n, rderr := conn.Read(b)
|
||||
if rderr != nil {
|
||||
fmt.Printf("Read error: %s\n", rderr)
|
||||
return
|
||||
}
|
||||
arr := strings.Split(string(b[:n]), ":")
|
||||
if len(arr) < 2 {
|
||||
return
|
||||
}
|
||||
publicIP = arr[0]
|
||||
port, _ := strconv.ParseInt(arr[1], 10, 32)
|
||||
publicPort = int(port)
|
||||
return
|
||||
|
||||
localAddr := conn.LocalAddr().(*net.TCPAddr)
|
||||
localPort = localAddr.Port
|
||||
|
||||
if _, err = conn.Write([]byte("1")); err != nil {
|
||||
err = fmt.Errorf("write error: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
b := make([]byte, 1000)
|
||||
conn.SetReadDeadline(time.Now().Add(NatDetectTimeout))
|
||||
n, err := conn.Read(b)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("read error: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
response := strings.Split(string(b[:n]), ":")
|
||||
if len(response) < 2 {
|
||||
err = fmt.Errorf("invalid response format: %s", string(b[:n]))
|
||||
return
|
||||
}
|
||||
|
||||
publicIP = response[0]
|
||||
port, err := strconv.Atoi(response[1])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("invalid port format: %w", err)
|
||||
return
|
||||
}
|
||||
publicPort = port
|
||||
|
||||
return
|
||||
}
|
||||
func natTest(serverHost string, serverPort int, localPort int) (publicIP string, publicPort int, err error) {
|
||||
gLog.Println(LvDEBUG, "natTest start")
|
||||
defer gLog.Println(LvDEBUG, "natTest end")
|
||||
|
||||
func natDetectUDP(serverHost string, serverPort int, localPort int) (publicIP string, publicPort int, err error) {
|
||||
gLog.dev("natDetectUDP start")
|
||||
defer gLog.dev("natDetectUDP end")
|
||||
conn, err := net.ListenPacket("udp", fmt.Sprintf(":%d", localPort))
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "natTest listen udp error:", err)
|
||||
gLog.e("natDetectUDP listen udp error:%s", err)
|
||||
return "", 0, err
|
||||
}
|
||||
defer conn.Close()
|
||||
@@ -71,7 +78,7 @@ func natTest(serverHost string, serverPort int, localPort int) (publicIP string,
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
deadline := time.Now().Add(NatTestTimeout)
|
||||
deadline := time.Now().Add(NatDetectTimeout)
|
||||
err = conn.SetReadDeadline(deadline)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
@@ -79,7 +86,7 @@ func natTest(serverHost string, serverPort int, localPort int) (publicIP string,
|
||||
buffer := make([]byte, 1024)
|
||||
nRead, _, err := conn.ReadFrom(buffer)
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "NAT detect error:", err)
|
||||
gLog.e("NAT detect error:%s", err)
|
||||
return "", 0, err
|
||||
}
|
||||
natRsp := NatDetectRsp{}
|
||||
@@ -88,19 +95,27 @@ func natTest(serverHost string, serverPort int, localPort int) (publicIP string,
|
||||
return natRsp.IP, natRsp.Port, nil
|
||||
}
|
||||
|
||||
func getNATType(host string, udp1 int, udp2 int) (publicIP string, NATType int, err error) {
|
||||
func getNATType(host string, detectPort1 int, detectPort2 int) (publicIP string, NATType int, err error) {
|
||||
setUPNP(gConf.Network.PublicIPPort)
|
||||
// the random local port may be used by other.
|
||||
localPort := int(rand.Uint32()%15000 + 50000)
|
||||
|
||||
ip1, port1, err := natTest(host, udp1, localPort)
|
||||
ip1, port1, err := natDetectUDP(host, detectPort1, localPort)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
// udp block try tcp
|
||||
gLog.w("udp block, try tcp nat detect")
|
||||
if ip1, port1, _, err = natDetectTCP(host, detectPort1, localPort); err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
}
|
||||
_, port2, err := natTest(host, udp2, localPort) // 2rd nat test not need testing publicip
|
||||
gLog.Printf(LvDEBUG, "local port:%d nat port:%d", localPort, port2)
|
||||
_, port2, err := natDetectUDP(host, detectPort2, localPort) // 2rd nat test not need testing publicip
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
gLog.w("udp block, try tcp nat detect")
|
||||
if _, port2, _, err = natDetectTCP(host, detectPort2, localPort); err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
}
|
||||
gLog.d("local port:%d nat port:%d", localPort, port2)
|
||||
natType := NATSymmetric
|
||||
if port1 == port2 {
|
||||
natType = NATCone
|
||||
@@ -113,11 +128,11 @@ func publicIPTest(publicIP string, echoPort int) (hasPublicIP int, hasUPNPorNATP
|
||||
return
|
||||
}
|
||||
var echoConn *net.UDPConn
|
||||
gLog.Println(LvDEBUG, "echo server start")
|
||||
gLog.d("echo server start")
|
||||
var err error
|
||||
echoConn, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: echoPort})
|
||||
if err != nil { // listen error
|
||||
gLog.Println(LvERROR, "echo server listen error:", err)
|
||||
gLog.e("echo server listen error:%s", err)
|
||||
return
|
||||
}
|
||||
defer echoConn.Close()
|
||||
@@ -125,34 +140,18 @@ func publicIPTest(publicIP string, echoPort int) (hasPublicIP int, hasUPNPorNATP
|
||||
for i := 0; i < 2; i++ {
|
||||
if i == 1 {
|
||||
// test upnp or nat-pmp
|
||||
gLog.Println(LvDEBUG, "upnp test start")
|
||||
nat, err := Discover()
|
||||
if err != nil || nat == nil {
|
||||
gLog.Println(LvDEBUG, "could not perform UPNP discover:", err)
|
||||
break
|
||||
}
|
||||
ext, err := nat.GetExternalAddress()
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "could not perform UPNP external address:", err)
|
||||
break
|
||||
}
|
||||
gLog.Println(LvINFO, "PublicIP:", ext)
|
||||
|
||||
externalPort, err := nat.AddPortMapping("udp", echoPort, echoPort, "openp2p", 30) // 30 seconds fot upnp testing
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "could not add udp UPNP port mapping", externalPort)
|
||||
break
|
||||
} else {
|
||||
nat.AddPortMapping("tcp", echoPort, echoPort, "openp2p", 604800) // 7 days for tcp connection
|
||||
}
|
||||
gLog.d("upnp test start")
|
||||
// 7 days for udp connection
|
||||
// 7 days for tcp connection
|
||||
setUPNP(echoPort)
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "public ip test start %s:%d", publicIP, echoPort)
|
||||
gLog.d("public ip test start %s:%d", publicIP, echoPort)
|
||||
conn, err := net.ListenUDP("udp", nil)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
defer conn.Close()
|
||||
dst, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", gConf.Network.ServerHost, gConf.Network.ServerPort))
|
||||
dst, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", gConf.Network.ServerIP, gConf.Network.ServerPort))
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
@@ -169,21 +168,21 @@ func publicIPTest(publicIP string, echoPort int) (hasPublicIP int, hasUPNPorNATP
|
||||
echoConn.SetReadDeadline(time.Now().Add(PublicIPEchoTimeout))
|
||||
nRead, _, err := echoConn.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "PublicIP detect error:", err)
|
||||
gLog.d("publicIPTest echoConn read timeout:%s", err)
|
||||
continue
|
||||
}
|
||||
natRsp := NatDetectRsp{}
|
||||
err = json.Unmarshal(buf[openP2PHeaderSize:nRead], &natRsp)
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "PublicIP detect error:", err)
|
||||
gLog.d("publicIPTest Unmarshal error:%s", err)
|
||||
continue
|
||||
}
|
||||
if natRsp.Port == echoPort {
|
||||
if i == 1 {
|
||||
gLog.Println(LvDEBUG, "UPNP or NAT-PMP:YES")
|
||||
gLog.d("UPNP or NAT-PMP:YES")
|
||||
hasUPNPorNATPMP = 1
|
||||
} else {
|
||||
gLog.Println(LvDEBUG, "public ip:YES")
|
||||
gLog.d("public ip:YES")
|
||||
hasPublicIP = 1
|
||||
}
|
||||
break
|
||||
@@ -191,3 +190,25 @@ func publicIPTest(publicIP string, echoPort int) (hasPublicIP int, hasUPNPorNATP
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setUPNP(echoPort int) {
|
||||
nat := upnp.Any() // Initialize the NAT interface
|
||||
if nat == nil {
|
||||
gLog.d("NAT interface is not available")
|
||||
return
|
||||
}
|
||||
ext, err := nat.ExternalIP()
|
||||
if err != nil {
|
||||
gLog.d("could not perform UPNP external address:%s", err)
|
||||
return
|
||||
}
|
||||
gLog.i("PublicIP:%v", ext)
|
||||
|
||||
externalPort, err := nat.AddMapping("udp", echoPort, echoPort, "openp2p", 604800)
|
||||
if err != nil {
|
||||
gLog.d("could not add udp UPNP port mapping %d", externalPort)
|
||||
return
|
||||
} else {
|
||||
nat.AddMapping("tcp", echoPort, echoPort, "openp2p", 604800)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package openp2p
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -25,15 +26,33 @@ func Run() {
|
||||
install()
|
||||
return
|
||||
case "uninstall":
|
||||
uninstall()
|
||||
uninstall(true)
|
||||
return
|
||||
case "start":
|
||||
d := daemon{}
|
||||
err := d.Control("start", "", nil)
|
||||
if err != nil {
|
||||
log.Println("openp2p start error:", err)
|
||||
return
|
||||
}
|
||||
log.Println("openp2p start ok")
|
||||
return
|
||||
case "stop":
|
||||
d := daemon{}
|
||||
err := d.Control("stop", "", nil)
|
||||
if err != nil {
|
||||
log.Println("openp2p stop error:", err)
|
||||
return
|
||||
}
|
||||
log.Println("openp2p stop ok")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
installByFilename()
|
||||
}
|
||||
parseParams("", "")
|
||||
gLog.Println(LvINFO, "openp2p start. version: ", OpenP2PVersion)
|
||||
gLog.Println(LvINFO, "Contact: QQ group 16947733, Email openp2p.cn@gmail.com")
|
||||
gLog.i("openp2p start. version: %s", OpenP2PVersion)
|
||||
gLog.i("Contact: QQ group 16947733, Email openp2p.cn@gmail.com")
|
||||
|
||||
if gConf.daemonMode {
|
||||
d := daemon{}
|
||||
@@ -41,18 +60,18 @@ func Run() {
|
||||
return
|
||||
}
|
||||
|
||||
gLog.Println(LvINFO, &gConf)
|
||||
gLog.i("node=%s, serverHost=%s, serverPort=%d", gConf.Network.Node, gConf.Network.ServerHost, gConf.Network.ServerPort)
|
||||
setFirewall()
|
||||
err := setRLimit()
|
||||
if err != nil {
|
||||
gLog.Println(LvINFO, "setRLimit error:", err)
|
||||
gLog.i("setRLimit error:%s", err)
|
||||
}
|
||||
GNetwork = P2PNetworkInstance()
|
||||
P2PNetworkInstance()
|
||||
if ok := GNetwork.Connect(30000); !ok {
|
||||
gLog.Println(LvERROR, "P2PNetwork login error")
|
||||
gLog.e("P2PNetwork login error")
|
||||
return
|
||||
}
|
||||
// gLog.Println(LvINFO, "waiting for connection...")
|
||||
// gLog.i("waiting for connection...")
|
||||
forever := make(chan bool)
|
||||
<-forever
|
||||
}
|
||||
@@ -76,16 +95,16 @@ func RunAsModule(baseDir string, token string, bw int, logLevel int) *P2PNetwork
|
||||
}
|
||||
// gLog.setLevel(LogLevel(logLevel))
|
||||
gConf.setShareBandwidth(bw)
|
||||
gLog.Println(LvINFO, "openp2p start. version: ", OpenP2PVersion)
|
||||
gLog.Println(LvINFO, "Contact: QQ group 16947733, Email openp2p.cn@gmail.com")
|
||||
gLog.Println(LvINFO, &gConf)
|
||||
gLog.i("openp2p start. version: %s", OpenP2PVersion)
|
||||
gLog.i("Contact: QQ group 16947733, Email openp2p.cn@gmail.com")
|
||||
gLog.i("node=%s, serverHost=%s, serverPort=%d", gConf.Network.Node, gConf.Network.ServerHost, gConf.Network.ServerPort)
|
||||
|
||||
GNetwork = P2PNetworkInstance()
|
||||
P2PNetworkInstance()
|
||||
if ok := GNetwork.Connect(30000); !ok {
|
||||
gLog.Println(LvERROR, "P2PNetwork login error")
|
||||
gLog.e("P2PNetwork login error")
|
||||
return nil
|
||||
}
|
||||
// gLog.Println(LvINFO, "waiting for connection...")
|
||||
// gLog.i("waiting for connection...")
|
||||
return GNetwork
|
||||
}
|
||||
|
||||
@@ -99,11 +118,11 @@ func RunCmd(cmd string) {
|
||||
setFirewall()
|
||||
err := setRLimit()
|
||||
if err != nil {
|
||||
gLog.Println(LvINFO, "setRLimit error:", err)
|
||||
gLog.i("setRLimit error:%s", err)
|
||||
}
|
||||
GNetwork = P2PNetworkInstance()
|
||||
P2PNetworkInstance()
|
||||
if ok := GNetwork.Connect(30000); !ok {
|
||||
gLog.Println(LvERROR, "P2PNetwork login error")
|
||||
gLog.e("P2PNetwork login error")
|
||||
return
|
||||
}
|
||||
forever := make(chan bool)
|
||||
|
||||
@@ -4,7 +4,10 @@ import (
|
||||
"github.com/openp2p-cn/wireguard-go/tun"
|
||||
)
|
||||
|
||||
const optunMTU = 1420
|
||||
|
||||
var AndroidSDWANConfig chan []byte
|
||||
var preAndroidSDWANConfig string
|
||||
|
||||
type optun struct {
|
||||
tunName string
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
package openp2p
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
tunIfaceName = "optun"
|
||||
PIHeaderSize = 0
|
||||
tunIfaceName = "optun"
|
||||
PIHeaderSize = 0
|
||||
ReadTunBuffSize = 2048
|
||||
ReadTunBuffNum = 16
|
||||
)
|
||||
|
||||
var AndroidReadTun chan []byte // TODO: multi channel
|
||||
@@ -35,27 +37,35 @@ func (t *optun) Write(bufs [][]byte, offset int) (int, error) {
|
||||
func AndroidRead(data []byte, len int) {
|
||||
head := PacketHeader{}
|
||||
parseHeader(data, &head)
|
||||
gLog.Printf(LvDev, "AndroidRead tun dst ip=%s,len=%d", net.IP{byte(head.dst >> 24), byte(head.dst >> 16), byte(head.dst >> 8), byte(head.dst)}.String(), len)
|
||||
// gLog.dev("AndroidRead tun dst ip=%s,len=%d", net.IP{byte(head.dst >> 24), byte(head.dst >> 16), byte(head.dst >> 8), byte(head.dst)}.String(), len)
|
||||
buf := make([]byte, len)
|
||||
copy(buf, data)
|
||||
AndroidReadTun <- buf
|
||||
}
|
||||
|
||||
func AndroidWrite(buf []byte) int {
|
||||
p := <-AndroidWriteTun
|
||||
copy(buf, p)
|
||||
return len(p)
|
||||
func AndroidWrite(buf []byte, timeoutMs int) int {
|
||||
timeout := time.Duration(timeoutMs) * time.Millisecond
|
||||
select {
|
||||
case p := <-AndroidWriteTun:
|
||||
if len(p) > int(gConf.sdwan.Mtu) {
|
||||
gLog.e("AndroidWrite packet too large %d", len(p))
|
||||
}
|
||||
copy(buf, p)
|
||||
return len(p)
|
||||
case <-time.After(timeout):
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func GetAndroidSDWANConfig(buf []byte) int {
|
||||
p := <-AndroidSDWANConfig
|
||||
copy(buf, p)
|
||||
gLog.Printf(LvINFO, "AndroidSDWANConfig=%s", p)
|
||||
gLog.i("AndroidSDWANConfig=%s", p)
|
||||
return len(p)
|
||||
}
|
||||
|
||||
func GetAndroidNodeName() string {
|
||||
gLog.Printf(LvINFO, "GetAndroidNodeName=%s", gConf.Network.Node)
|
||||
gLog.i("GetAndroidNodeName=%s", gConf.Network.Node)
|
||||
return gConf.Network.Node
|
||||
}
|
||||
|
||||
|
||||
@@ -10,14 +10,16 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
tunIfaceName = "utun"
|
||||
PIHeaderSize = 4 // utun has no IFF_NO_PI
|
||||
tunIfaceName = "utun"
|
||||
PIHeaderSize = 4 // utun has no IFF_NO_PI
|
||||
ReadTunBuffSize = 2048
|
||||
ReadTunBuffNum = 16
|
||||
)
|
||||
|
||||
func (t *optun) Start(localAddr string, detail *SDWANInfo) error {
|
||||
var err error
|
||||
t.tunName = tunIfaceName
|
||||
t.dev, err = tun.CreateTUN(t.tunName, 1420)
|
||||
t.dev, err = tun.CreateTUN(t.tunName, int(detail.Mtu))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -72,10 +74,10 @@ func delRoutesByGateway(gateway string) error {
|
||||
cmd := exec.Command("route", "delete", fields[0], gateway)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "Delete route %s error:%s", fields[0], err)
|
||||
gLog.e("Delete route %s error:%s", fields[0], err)
|
||||
continue
|
||||
}
|
||||
gLog.Printf(LvINFO, "Delete route ok: %s %s\n", fields[0], gateway)
|
||||
gLog.i("Delete route ok: %s %s\n", fields[0], gateway)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -7,8 +7,7 @@ package openp2p
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"os"
|
||||
|
||||
"github.com/openp2p-cn/wireguard-go/tun"
|
||||
"github.com/vishvananda/netlink"
|
||||
@@ -17,6 +16,9 @@ import (
|
||||
const (
|
||||
tunIfaceName = "optun"
|
||||
PIHeaderSize = 0
|
||||
// sdwan
|
||||
ReadTunBuffSize = 2048
|
||||
ReadTunBuffNum = 16
|
||||
)
|
||||
|
||||
var previousIP = ""
|
||||
@@ -24,10 +26,14 @@ var previousIP = ""
|
||||
func (t *optun) Start(localAddr string, detail *SDWANInfo) error {
|
||||
var err error
|
||||
t.tunName = tunIfaceName
|
||||
t.dev, err = tun.CreateTUN(t.tunName, 1420)
|
||||
t.dev, err = tun.CreateTUN(t.tunName, int(detail.Mtu))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte("1"), 0644)
|
||||
if err != nil {
|
||||
gLog.e("write ip_forward error:%s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -44,8 +50,8 @@ func setTunAddr(ifname, localAddr, remoteAddr string, wintun interface{}) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
netlink.LinkSetMTU(ifce, 1375)
|
||||
netlink.LinkSetTxQLen(ifce, 100)
|
||||
netlink.LinkSetMTU(ifce, int(gConf.getSDWAN().Mtu))
|
||||
netlink.LinkSetTxQLen(ifce, 1000)
|
||||
netlink.LinkSetUp(ifce)
|
||||
|
||||
ln, err := netlink.ParseIPNet(localAddr)
|
||||
@@ -108,26 +114,24 @@ func delRoute(dst, gw string) error {
|
||||
}
|
||||
|
||||
func delRoutesByGateway(gateway string) error {
|
||||
cmd := exec.Command("route", "-n")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return err
|
||||
ipGW := net.ParseIP(gateway)
|
||||
if ipGW == nil {
|
||||
return fmt.Errorf("invalid gateway IP: %s", gateway)
|
||||
}
|
||||
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
if !strings.Contains(line, gateway) {
|
||||
continue
|
||||
}
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) >= 8 && fields[1] == "0.0.0.0" && fields[7] == gateway {
|
||||
delCmd := exec.Command("route", "del", "-net", fields[0], "gw", gateway)
|
||||
err := delCmd.Run()
|
||||
routes, err := netlink.RouteList(nil, netlink.FAMILY_V4)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list routes: %v", err)
|
||||
}
|
||||
|
||||
for _, route := range routes {
|
||||
if route.Gw != nil && route.Gw.Equal(ipGW) || (route.Dst != nil && route.Dst.IP.Equal(ipGW)) {
|
||||
err := netlink.RouteDel(&route)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "Delete route %s error:%s", fields[0], err)
|
||||
gLog.e("Failed to delete route: %v, error: %v", route, err)
|
||||
continue
|
||||
}
|
||||
gLog.Printf(LvINFO, "Delete route ok: %s %s %s\n", fields[0], fields[1], gateway)
|
||||
gLog.i("Deleted route: %v", route)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -3,40 +3,133 @@
|
||||
|
||||
package openp2p
|
||||
|
||||
import "github.com/openp2p-cn/wireguard-go/tun"
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/openp2p-cn/wireguard-go/tun"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
const (
|
||||
tunIfaceName = "optun"
|
||||
PIHeaderSize = 0
|
||||
tunIfaceName = "optun"
|
||||
PIHeaderSize = 0
|
||||
ReadTunBuffSize = 2048
|
||||
ReadTunBuffNum = 16
|
||||
)
|
||||
|
||||
var previousIP = ""
|
||||
|
||||
func (t *optun) Start(localAddr string, detail *SDWANInfo) error {
|
||||
var err error
|
||||
t.tunName = tunIfaceName
|
||||
t.dev, err = tun.CreateTUN(t.tunName, 1420)
|
||||
|
||||
t.dev, err = tun.CreateTUN(t.tunName, int(detail.Mtu))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = setTunAddr(t.tunName, localAddr, detail.Gateway, t.dev)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *optun) Read(bufs [][]byte, sizes []int, offset int) (n int, err error) {
|
||||
return t.dev.Read(bufs, sizes, offset)
|
||||
}
|
||||
|
||||
func (t *optun) Write(bufs [][]byte, offset int) (int, error) {
|
||||
return t.dev.Write(bufs, offset)
|
||||
}
|
||||
|
||||
func setTunAddr(ifname, localAddr, remoteAddr string, wintun interface{}) error {
|
||||
ifce, err := netlink.LinkByName(ifname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
netlink.LinkSetMTU(ifce, int(gConf.getSDWAN().Mtu))
|
||||
netlink.LinkSetTxQLen(ifce, 1000)
|
||||
netlink.LinkSetUp(ifce)
|
||||
|
||||
ln, err := netlink.ParseIPNet(localAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ln.Mask = net.CIDRMask(32, 32)
|
||||
rn, err := netlink.ParseIPNet(remoteAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rn.Mask = net.CIDRMask(32, 32)
|
||||
|
||||
addr := &netlink.Addr{
|
||||
IPNet: ln,
|
||||
Peer: rn,
|
||||
}
|
||||
if previousIP != "" {
|
||||
lnDel, err := netlink.ParseIPNet(previousIP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lnDel.Mask = net.CIDRMask(32, 32)
|
||||
|
||||
addrDel := &netlink.Addr{
|
||||
IPNet: lnDel,
|
||||
Peer: rn,
|
||||
}
|
||||
netlink.AddrDel(ifce, addrDel)
|
||||
}
|
||||
previousIP = localAddr
|
||||
return netlink.AddrAdd(ifce, addr)
|
||||
}
|
||||
|
||||
func addRoute(dst, gw, ifname string) error {
|
||||
return nil
|
||||
_, networkid, err := net.ParseCIDR(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ipGW := net.ParseIP(gw)
|
||||
if ipGW == nil {
|
||||
return fmt.Errorf("parse gateway %s failed", gw)
|
||||
}
|
||||
route := &netlink.Route{
|
||||
Dst: networkid,
|
||||
Gw: ipGW,
|
||||
}
|
||||
return netlink.RouteAdd(route)
|
||||
}
|
||||
|
||||
func delRoute(dst, gw string) error {
|
||||
return nil
|
||||
}
|
||||
func addTunAddr(localAddr, remoteAddr string) error {
|
||||
return nil
|
||||
_, networkid, err := net.ParseCIDR(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
route := &netlink.Route{
|
||||
Dst: networkid,
|
||||
}
|
||||
return netlink.RouteDel(route)
|
||||
}
|
||||
|
||||
func delTunAddr(localAddr, remoteAddr string) error {
|
||||
func delRoutesByGateway(gateway string) error {
|
||||
cmd := exec.Command("route", "-n")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
if !strings.Contains(line, gateway) {
|
||||
continue
|
||||
}
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) >= 8 && fields[1] == "0.0.0.0" && fields[7] == gateway {
|
||||
delCmd := exec.Command("route", "del", "-net", fields[0], "gw", gateway)
|
||||
err := delCmd.Run()
|
||||
if err != nil {
|
||||
gLog.e("Delete route %s error:%s", fields[0], err)
|
||||
continue
|
||||
}
|
||||
gLog.i("Delete route ok: %s %s %s\n", fields[0], fields[1], gateway)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@ import (
|
||||
const (
|
||||
tunIfaceName = "optun"
|
||||
PIHeaderSize = 0
|
||||
// sdwan
|
||||
ReadTunBuffSize = 1024 * 64 // wintun will read date len > mtu, default 64k
|
||||
ReadTunBuffNum = 4
|
||||
)
|
||||
|
||||
func (t *optun) Start(localAddr string, detail *SDWANInfo) error {
|
||||
@@ -42,9 +45,9 @@ func (t *optun) Start(localAddr string, detail *SDWANInfo) error {
|
||||
Data3: 0x4567,
|
||||
Data4: [8]byte{0x80, 0x42, 0x83, 0x7e, 0xf4, 0x56, 0xce, 0x13},
|
||||
}
|
||||
t.dev, err = tun.CreateTUNWithRequestedGUID(t.tunName, uuid, 1420)
|
||||
t.dev, err = tun.CreateTUNWithRequestedGUID(t.tunName, uuid, int(detail.Mtu))
|
||||
if err != nil { // retry
|
||||
t.dev, err = tun.CreateTUNWithRequestedGUID(t.tunName, uuid, 1420)
|
||||
t.dev, err = tun.CreateTUNWithRequestedGUID(t.tunName, uuid, int(detail.Mtu))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -67,12 +70,12 @@ func setTunAddr(ifname, localAddr, remoteAddr string, wintun interface{}) error
|
||||
link := winipcfg.LUID(nativeTunDevice.LUID())
|
||||
ip, err := netip.ParsePrefix(localAddr)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "ParsePrefix error:%s, luid:%d,localAddr:%s", err, nativeTunDevice.LUID(), localAddr)
|
||||
gLog.e("ParsePrefix error:%s, luid:%d,localAddr:%s", err, nativeTunDevice.LUID(), localAddr)
|
||||
return err
|
||||
}
|
||||
err = link.SetIPAddresses([]netip.Prefix{ip})
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "SetIPAddresses error:%s, netip.Prefix:%+v", err, []netip.Prefix{ip})
|
||||
gLog.e("SetIPAddresses error:%s, netip.Prefix:%+v", err, []netip.Prefix{ip})
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -133,10 +136,10 @@ func delRoutesByGateway(gateway string) error {
|
||||
cmd := exec.Command("route", "delete", fields[0], "mask", fields[1], gateway)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "Delete route %s error:%s", fields[0], err)
|
||||
gLog.e("Delete route %s error:%s", fields[0], err)
|
||||
continue
|
||||
}
|
||||
gLog.Printf(LvINFO, "Delete route ok: %s %s %s\n", fields[0], fields[1], gateway)
|
||||
gLog.i("Delete route ok: %s %s %s\n", fields[0], fields[1], gateway)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -21,18 +22,24 @@ func (e *DeadlineExceededError) Error() string { return "i/o timeout" }
|
||||
func (e *DeadlineExceededError) Timeout() bool { return true }
|
||||
func (e *DeadlineExceededError) Temporary() bool { return true }
|
||||
|
||||
var overlayConns sync.Map // both TCP and UDP
|
||||
func closeOverlayConns(appID uint64) {
|
||||
overlayConns.Range(func(_, i interface{}) bool {
|
||||
oConn := i.(*overlayConn)
|
||||
if oConn.app.id == appID {
|
||||
oConn.Close()
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// implement io.Writer
|
||||
type overlayConn struct {
|
||||
tunnel *P2PTunnel // TODO: del
|
||||
app *p2pApp
|
||||
connTCP net.Conn
|
||||
id uint64
|
||||
rtid uint64
|
||||
running bool
|
||||
isClient bool
|
||||
appID uint64 // TODO: del
|
||||
appKey uint64 // TODO: del
|
||||
appKeyBytes []byte // TODO: del
|
||||
app *p2pApp
|
||||
connTCP net.Conn
|
||||
id uint64
|
||||
running bool
|
||||
isClient bool
|
||||
// for udp
|
||||
connUDP *net.UDPConn
|
||||
remoteAddr net.Addr
|
||||
@@ -41,42 +48,31 @@ type overlayConn struct {
|
||||
}
|
||||
|
||||
func (oConn *overlayConn) run() {
|
||||
gLog.Printf(LvDEBUG, "%d overlayConn run start", oConn.id)
|
||||
defer gLog.Printf(LvDEBUG, "%d overlayConn run end", oConn.id)
|
||||
gLog.d("oid:%d overlayConn run start", oConn.id)
|
||||
defer gLog.d("oid:%d overlayConn run end", oConn.id)
|
||||
oConn.lastReadUDPTs = time.Now()
|
||||
buffer := make([]byte, ReadBuffLen+PaddingSize) // 16 bytes for padding
|
||||
reuseBuff := buffer[:ReadBuffLen]
|
||||
encryptData := make([]byte, ReadBuffLen+PaddingSize) // 16 bytes for padding
|
||||
tunnelHead := new(bytes.Buffer)
|
||||
relayHead := new(bytes.Buffer)
|
||||
binary.Write(relayHead, binary.LittleEndian, oConn.rtid)
|
||||
binary.Write(tunnelHead, binary.LittleEndian, oConn.id)
|
||||
for oConn.running && oConn.tunnel.isRuning() {
|
||||
overlayHead := new(bytes.Buffer)
|
||||
|
||||
binary.Write(overlayHead, binary.LittleEndian, oConn.id)
|
||||
for oConn.running && oConn.app.running {
|
||||
readBuff, dataLen, err := oConn.Read(reuseBuff)
|
||||
if err != nil {
|
||||
if ne, ok := err.(net.Error); ok && ne.Timeout() {
|
||||
continue
|
||||
}
|
||||
// overlay tcp connection normal close, debug log
|
||||
gLog.Printf(LvDEBUG, "overlayConn %d read error:%s,close it", oConn.id, err)
|
||||
gLog.d("oid:%d overlayConn read error:%s,close it", oConn.id, err)
|
||||
break
|
||||
}
|
||||
payload := readBuff[:dataLen]
|
||||
if oConn.appKey != 0 {
|
||||
payload, _ = encryptBytes(oConn.appKeyBytes, encryptData, readBuff[:dataLen], dataLen)
|
||||
}
|
||||
writeBytes := append(tunnelHead.Bytes(), payload...)
|
||||
// TODO: app.write
|
||||
if oConn.rtid == 0 {
|
||||
oConn.tunnel.conn.WriteBytes(MsgP2P, MsgOverlayData, writeBytes)
|
||||
gLog.Printf(LvDev, "write overlay data to tid:%d,oid:%d bodylen=%d", oConn.tunnel.id, oConn.id, len(writeBytes))
|
||||
} else {
|
||||
// write raley data
|
||||
all := append(relayHead.Bytes(), encodeHeader(MsgP2P, MsgOverlayData, uint32(len(writeBytes)))...)
|
||||
all = append(all, writeBytes...)
|
||||
oConn.tunnel.conn.WriteBytes(MsgP2P, MsgRelayData, all)
|
||||
gLog.Printf(LvDev, "write relay data to tid:%d,rtid:%d,oid:%d bodylen=%d", oConn.tunnel.id, oConn.rtid, oConn.id, len(writeBytes))
|
||||
if oConn.app.key != 0 {
|
||||
payload, _ = encryptBytes(oConn.app.appKeyBytes, encryptData, readBuff[:dataLen], dataLen)
|
||||
}
|
||||
writeBytes := append(overlayHead.Bytes(), payload...)
|
||||
oConn.app.WriteBytes(writeBytes)
|
||||
}
|
||||
if oConn.connTCP != nil {
|
||||
oConn.connTCP.Close()
|
||||
@@ -84,10 +80,10 @@ func (oConn *overlayConn) run() {
|
||||
if oConn.connUDP != nil {
|
||||
oConn.connUDP.Close()
|
||||
}
|
||||
oConn.tunnel.overlayConns.Delete(oConn.id)
|
||||
overlayConns.Delete(oConn.id)
|
||||
// notify peer disconnect
|
||||
req := OverlayDisconnectReq{ID: oConn.id}
|
||||
oConn.tunnel.WriteMessage(oConn.rtid, MsgP2P, MsgOverlayDisconnectReq, &req)
|
||||
oConn.app.WriteMessage(MsgP2P, MsgOverlayDisconnectReq, &req)
|
||||
}
|
||||
|
||||
func (oConn *overlayConn) Read(reuseBuff []byte) (buff []byte, dataLen int, err error) {
|
||||
@@ -145,7 +141,8 @@ func (oConn *overlayConn) Write(buff []byte) (n int, err error) {
|
||||
return
|
||||
}
|
||||
if oConn.connTCP != nil {
|
||||
n, err = oConn.connTCP.Write(buff)
|
||||
err = writeFull(oConn.connTCP, buff)
|
||||
n = len(buff)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
760
core/p2papp.go
@@ -2,6 +2,7 @@ package openp2p
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -14,22 +15,27 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const WriteDataChanSize int = 3000
|
||||
const WriteDataChanSize int = 8192
|
||||
|
||||
var buildTunnelMtx sync.Mutex
|
||||
|
||||
const (
|
||||
StatusIdle = 0
|
||||
StatusWriting = 1
|
||||
)
|
||||
|
||||
type P2PTunnel struct {
|
||||
conn underlay
|
||||
hbTime time.Time
|
||||
hbMtx sync.Mutex
|
||||
whbTime time.Time
|
||||
config AppConfig
|
||||
localHoleAddr *net.UDPAddr // local hole address
|
||||
remoteHoleAddr *net.UDPAddr // remote hole address
|
||||
overlayConns sync.Map // both TCP and UDP
|
||||
id uint64 // client side alloc rand.uint64 = server side
|
||||
|
||||
running bool
|
||||
runMtx sync.Mutex
|
||||
tunnelServer bool // different from underlayServer
|
||||
coneLocalPort int
|
||||
coneNatPort int
|
||||
linkModeWeb string // use config.linkmode
|
||||
@@ -40,30 +46,33 @@ type P2PTunnel struct {
|
||||
|
||||
func (t *P2PTunnel) initPort() {
|
||||
t.running = true
|
||||
localPort := int(rand.Uint32()%15000 + 50000) // if the process has bug, will add many upnp port. use specify p2p port by param
|
||||
if t.config.linkMode == LinkModeTCP6 || t.config.linkMode == LinkModeTCP4 || t.config.linkMode == LinkModeIntranet {
|
||||
t.coneLocalPort = gConf.Network.TCPPort
|
||||
t.coneNatPort = gConf.Network.TCPPort // symmetric doesn't need coneNatPort
|
||||
localPort := int(rand.Uint32()%8192 + 1025) // if the process has bug, will add many upnp port. use specify p2p port by param
|
||||
if t.config.linkMode == LinkModeTCP6 || t.config.linkMode == LinkModeTCP4 || t.config.linkMode == LinkModeUDP4 || t.config.linkMode == LinkModeIntranet {
|
||||
t.coneLocalPort = gConf.Network.PublicIPPort
|
||||
t.coneNatPort = gConf.Network.PublicIPPort // symmetric doesn't need coneNatPort
|
||||
}
|
||||
if t.config.linkMode == LinkModeUDPPunch {
|
||||
// prepare one random cone hole manually
|
||||
_, natPort, _ := natTest(gConf.Network.ServerHost, gConf.Network.UDPPort1, localPort)
|
||||
_, natPort, _ := natDetectUDP(gConf.Network.ServerIP, NATDetectPort1, localPort)
|
||||
t.coneLocalPort = localPort
|
||||
t.coneNatPort = natPort
|
||||
}
|
||||
if t.config.linkMode == LinkModeTCPPunch {
|
||||
// prepare one random cone hole by system automatically
|
||||
_, natPort, localPort2 := natTCP(gConf.Network.ServerHost, IfconfigPort1)
|
||||
_, natPort, localPort2, _ := natDetectTCP(gConf.Network.ServerIP, NATDetectPort1, 0)
|
||||
t.coneLocalPort = localPort2
|
||||
t.coneNatPort = natPort
|
||||
}
|
||||
if t.config.linkMode == LinkModeTCP6 && compareVersion(t.config.peerVersion, IPv6PunchVersion) >= 0 {
|
||||
t.coneLocalPort = localPort
|
||||
t.coneNatPort = localPort
|
||||
}
|
||||
t.localHoleAddr = &net.UDPAddr{IP: net.ParseIP(gConf.Network.localIP), Port: t.coneLocalPort}
|
||||
gLog.Printf(LvDEBUG, "prepare punching port %d:%d", t.coneLocalPort, t.coneNatPort)
|
||||
gLog.d("prepare punching port %d:%d", t.coneLocalPort, t.coneNatPort)
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) connect() error {
|
||||
gLog.Printf(LvDEBUG, "start p2pTunnel to %s ", t.config.LogPeerNode())
|
||||
t.tunnelServer = false
|
||||
gLog.d("start p2pTunnel to %s ", t.config.LogPeerNode())
|
||||
appKey := uint64(0)
|
||||
req := PushConnectReq{
|
||||
Token: t.config.peerToken,
|
||||
@@ -91,7 +100,7 @@ func (t *P2PTunnel) connect() error {
|
||||
}
|
||||
rsp := PushConnectRsp{}
|
||||
if err := json.Unmarshal(body, &rsp); err != nil {
|
||||
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(rsp), err)
|
||||
gLog.e("wrong %v:%s", reflect.TypeOf(rsp), err)
|
||||
return err
|
||||
}
|
||||
// gLog.Println(LevelINFO, rsp)
|
||||
@@ -108,7 +117,7 @@ func (t *P2PTunnel) connect() error {
|
||||
t.punchTs = rsp.PunchTs
|
||||
err := t.start()
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "handshake error:", err)
|
||||
gLog.d("handshake error:%s", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -133,7 +142,7 @@ func (t *P2PTunnel) isActive() bool {
|
||||
defer t.hbMtx.Unlock()
|
||||
res := time.Now().Before(t.hbTime.Add(TunnelHeartbeatTime * 2))
|
||||
if !res {
|
||||
gLog.Printf(LvDEBUG, "%d tunnel isActive false", t.id)
|
||||
gLog.d("%d tunnel isActive false", t.id)
|
||||
}
|
||||
return res
|
||||
}
|
||||
@@ -154,7 +163,7 @@ func (t *P2PTunnel) checkActive() bool {
|
||||
t.hbMtx.Unlock()
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
gLog.Printf(LvINFO, "checkActive %t. hbtime=%d", isActive, t.hbTime)
|
||||
gLog.d("checkActive %t. hbtime=%d", isActive, t.hbTime)
|
||||
return isActive
|
||||
}
|
||||
|
||||
@@ -169,7 +178,7 @@ func (t *P2PTunnel) close() {
|
||||
t.conn.Close()
|
||||
}
|
||||
GNetwork.allTunnels.Delete(t.id)
|
||||
gLog.Printf(LvINFO, "%d p2ptunnel close %s ", t.id, t.config.LogPeerNode())
|
||||
gLog.i("%d p2ptunnel close %s ", t.id, t.config.LogPeerNode())
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) start() error {
|
||||
@@ -180,7 +189,7 @@ func (t *P2PTunnel) start() error {
|
||||
}
|
||||
err := t.connectUnderlay()
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, err)
|
||||
gLog.d("connectUnderlay error:%s", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -195,16 +204,16 @@ func (t *P2PTunnel) handshake() error {
|
||||
}
|
||||
}
|
||||
if compareVersion(t.config.peerVersion, SyncServerTimeVersion) < 0 {
|
||||
gLog.Printf(LvDEBUG, "peer version %s less than %s", t.config.peerVersion, SyncServerTimeVersion)
|
||||
gLog.d("peer version %s less than %s", t.config.peerVersion, SyncServerTimeVersion)
|
||||
} else {
|
||||
ts := time.Duration(int64(t.punchTs) + GNetwork.dt + GNetwork.ddtma*int64(time.Since(GNetwork.hbTime)+PunchTsDelay)/int64(NetworkHeartbeatTime) - time.Now().UnixNano())
|
||||
if ts > PunchTsDelay || ts < 0 {
|
||||
ts = PunchTsDelay
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "sleep %d ms", ts/time.Millisecond)
|
||||
gLog.d("sleep %d ms", ts/time.Millisecond)
|
||||
time.Sleep(ts)
|
||||
}
|
||||
gLog.Println(LvDEBUG, "handshake to ", t.config.LogPeerNode())
|
||||
gLog.d("handshake to %s", t.config.LogPeerNode())
|
||||
var err error
|
||||
if gConf.Network.natType == NATCone && t.config.peerNatType == NATCone {
|
||||
err = handshakeC2C(t)
|
||||
@@ -219,19 +228,25 @@ func (t *P2PTunnel) handshake() error {
|
||||
return errors.New("unknown error")
|
||||
}
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "punch handshake error:", err)
|
||||
gLog.d("punch handshake error:%s", err)
|
||||
return err
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "handshake to %s ok", t.config.LogPeerNode())
|
||||
gLog.d("handshake to %s ok", t.config.LogPeerNode())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) connectUnderlay() (err error) {
|
||||
switch t.config.linkMode {
|
||||
case LinkModeTCP6:
|
||||
t.conn, err = t.connectUnderlayTCP6()
|
||||
if compareVersion(t.config.peerVersion, IPv6PunchVersion) >= 0 {
|
||||
t.conn, err = t.connectUnderlayTCP()
|
||||
} else {
|
||||
t.conn, err = t.connectUnderlayTCP6()
|
||||
}
|
||||
case LinkModeTCP4:
|
||||
t.conn, err = t.connectUnderlayTCP()
|
||||
case LinkModeUDP4:
|
||||
t.conn, err = t.connectUnderlayUDP()
|
||||
case LinkModeTCPPunch:
|
||||
if gConf.Network.natType == NATSymmetric || t.config.peerNatType == NATSymmetric {
|
||||
t.conn, err = t.connectUnderlayTCPSymmetric()
|
||||
@@ -257,24 +272,35 @@ func (t *P2PTunnel) connectUnderlay() (err error) {
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) connectUnderlayUDP() (c underlay, err error) {
|
||||
gLog.Printf(LvDEBUG, "connectUnderlayUDP %s start ", t.config.LogPeerNode())
|
||||
defer gLog.Printf(LvDEBUG, "connectUnderlayUDP %s end ", t.config.LogPeerNode())
|
||||
gLog.d("connectUnderlayUDP %s start ", t.config.LogPeerNode())
|
||||
defer gLog.d("connectUnderlayUDP %s end ", t.config.LogPeerNode())
|
||||
var ul underlay
|
||||
underlayProtocol := t.config.UnderlayProtocol
|
||||
if underlayProtocol == "" {
|
||||
underlayProtocol = "quic"
|
||||
}
|
||||
if t.config.isUnderlayServer == 1 {
|
||||
// TODO: move to a func
|
||||
time.Sleep(time.Millisecond * 10) // punching udp port will need some times in some env
|
||||
go GNetwork.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
|
||||
if t.config.UnderlayProtocol == "kcp" {
|
||||
ul, err = listenKCP(t.localHoleAddr.String(), TunnelIdleTimeout)
|
||||
if t.config.linkMode == LinkModeUDP4 {
|
||||
if v4l != nil {
|
||||
ul = v4l.getUnderlay(t.id)
|
||||
}
|
||||
if ul == nil {
|
||||
return nil, fmt.Errorf("listen UDP4 error")
|
||||
}
|
||||
gLog.d("UDP4 connection ok")
|
||||
} else {
|
||||
ul, err = listenQuic(t.localHoleAddr.String(), TunnelIdleTimeout)
|
||||
if t.config.UnderlayProtocol == "kcp" {
|
||||
// ul, err = listenKCP(t.localHoleAddr.String(), TunnelIdleTimeout)
|
||||
} else {
|
||||
ul, err = listenQuic(t.localHoleAddr.String(), TunnelIdleTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
gLog.Printf(LvINFO, "listen %s error:%s", underlayProtocol, err)
|
||||
gLog.i("listen %s error:%s", underlayProtocol, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -284,54 +310,64 @@ func (t *P2PTunnel) connectUnderlayUDP() (c underlay, err error) {
|
||||
return nil, fmt.Errorf("read start msg error:%s", err)
|
||||
}
|
||||
if buff != nil {
|
||||
gLog.Println(LvDEBUG, string(buff))
|
||||
gLog.d("handshake flag:%s", string(buff))
|
||||
}
|
||||
ul.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, []byte("OpenP2P,hello2"))
|
||||
gLog.Printf(LvDEBUG, "%s connection ok", underlayProtocol)
|
||||
gLog.d("%s connection ok", underlayProtocol)
|
||||
return ul, nil
|
||||
}
|
||||
|
||||
//else
|
||||
conn, errL := net.ListenUDP("udp", t.localHoleAddr)
|
||||
//client side
|
||||
listenAddr := t.localHoleAddr
|
||||
if t.config.linkMode == LinkModeUDP4 {
|
||||
listenAddr = &net.UDPAddr{IP: net.ParseIP(gConf.Network.localIP), Port: 0}
|
||||
t.remoteHoleAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", t.config.peerIP, t.config.peerConeNatPort))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
conn, errL := net.ListenUDP("udp", listenAddr)
|
||||
if errL != nil {
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
conn, errL = net.ListenUDP("udp", t.localHoleAddr)
|
||||
conn, errL = net.ListenUDP("udp", listenAddr)
|
||||
if errL != nil {
|
||||
return nil, fmt.Errorf("%s listen error:%s", underlayProtocol, errL)
|
||||
}
|
||||
}
|
||||
GNetwork.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, ReadMsgTimeout)
|
||||
gLog.Printf(LvDEBUG, "%s dial to %s", underlayProtocol, t.remoteHoleAddr.String())
|
||||
gLog.d("%s dial to %s", underlayProtocol, t.remoteHoleAddr.String())
|
||||
if t.config.UnderlayProtocol == "kcp" {
|
||||
ul, errL = dialKCP(conn, t.remoteHoleAddr, TunnelIdleTimeout)
|
||||
// ul, errL = dialKCP(conn, t.remoteHoleAddr, UnderlayConnectTimeout)
|
||||
} else {
|
||||
ul, errL = dialQuic(conn, t.remoteHoleAddr, TunnelIdleTimeout)
|
||||
ul, errL = dialQuic(conn, t.remoteHoleAddr, UnderlayConnectTimeout)
|
||||
}
|
||||
|
||||
if errL != nil {
|
||||
return nil, fmt.Errorf("%s dial to %s error:%s", underlayProtocol, t.remoteHoleAddr.String(), errL)
|
||||
}
|
||||
handshakeBegin := time.Now()
|
||||
ul.WriteBytes(MsgP2P, MsgTunnelHandshake, []byte("OpenP2P,hello"))
|
||||
tidBuff := new(bytes.Buffer)
|
||||
binary.Write(tidBuff, binary.LittleEndian, t.id)
|
||||
ul.WriteBytes(MsgP2P, MsgTunnelHandshake, tidBuff.Bytes())
|
||||
_, buff, err := ul.ReadBuffer() // TODO: kcp need timeout
|
||||
if err != nil {
|
||||
ul.Close()
|
||||
return nil, fmt.Errorf("read MsgTunnelHandshake error:%s", err)
|
||||
}
|
||||
if buff != nil {
|
||||
gLog.Println(LvDEBUG, string(buff))
|
||||
gLog.d("handshake flag:%s", string(buff))
|
||||
}
|
||||
|
||||
gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin))
|
||||
gLog.Printf(LvINFO, "%s connection ok", underlayProtocol)
|
||||
gLog.i("rtt=%dms", time.Since(handshakeBegin)/time.Millisecond)
|
||||
gLog.i("%s connection ok", underlayProtocol)
|
||||
t.linkModeWeb = LinkModeUDPPunch
|
||||
return ul, nil
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) connectUnderlayTCP() (c underlay, err error) {
|
||||
gLog.Printf(LvDEBUG, "connectUnderlayTCP %s start ", t.config.LogPeerNode())
|
||||
defer gLog.Printf(LvDEBUG, "connectUnderlayTCP %s end ", t.config.LogPeerNode())
|
||||
var ul *underlayTCP
|
||||
gLog.d("connectUnderlayTCP %s start ", t.config.LogPeerNode())
|
||||
defer gLog.d("connectUnderlayTCP %s end ", t.config.LogPeerNode())
|
||||
var ul underlay
|
||||
peerIP := t.config.peerIP
|
||||
if t.config.linkMode == LinkModeIntranet {
|
||||
peerIP = t.config.peerLanIP
|
||||
@@ -342,11 +378,15 @@ func (t *P2PTunnel) connectUnderlayTCP() (c underlay, err error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listen TCP error:%s", err)
|
||||
}
|
||||
gLog.Println(LvINFO, "TCP connection ok")
|
||||
|
||||
t.linkModeWeb = LinkModeIPv4
|
||||
if t.config.linkMode == LinkModeIntranet {
|
||||
t.linkModeWeb = LinkModeIntranet
|
||||
}
|
||||
if t.config.linkMode == LinkModeTCP6 {
|
||||
t.linkModeWeb = LinkModeIPv6
|
||||
}
|
||||
gLog.i("%s TCP connection ok", t.linkModeWeb)
|
||||
return ul, nil
|
||||
}
|
||||
|
||||
@@ -355,49 +395,60 @@ func (t *P2PTunnel) connectUnderlayTCP() (c underlay, err error) {
|
||||
GNetwork.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, ReadMsgTimeout)
|
||||
} else { //tcp punch should sleep for punch the same time
|
||||
if compareVersion(t.config.peerVersion, SyncServerTimeVersion) < 0 {
|
||||
gLog.Printf(LvDEBUG, "peer version %s less than %s", t.config.peerVersion, SyncServerTimeVersion)
|
||||
gLog.d("peer version %s less than %s", t.config.peerVersion, SyncServerTimeVersion)
|
||||
} else {
|
||||
ts := time.Duration(int64(t.punchTs) + GNetwork.dt + GNetwork.ddtma*int64(time.Since(GNetwork.hbTime)+PunchTsDelay)/int64(NetworkHeartbeatTime) - time.Now().UnixNano())
|
||||
if ts > PunchTsDelay || ts < 0 {
|
||||
ts = PunchTsDelay
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "sleep %d ms", ts/time.Millisecond)
|
||||
gLog.d("sleep %d ms", ts/time.Millisecond)
|
||||
time.Sleep(ts)
|
||||
}
|
||||
}
|
||||
ul, err = dialTCP(peerIP, t.config.peerConeNatPort, t.coneLocalPort, t.config.linkMode)
|
||||
host := peerIP
|
||||
if t.config.linkMode == LinkModeTCP6 {
|
||||
host = t.config.peerIPv6
|
||||
}
|
||||
ul, err = dialTCP(host, t.config.peerConeNatPort, t.coneLocalPort, t.config.linkMode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("TCP dial to %s:%d error:%s", t.config.peerIP, t.config.peerConeNatPort, err)
|
||||
return nil, fmt.Errorf("TCP dial to %s:%d error:%s", host, t.config.peerConeNatPort, err)
|
||||
}
|
||||
handshakeBegin := time.Now()
|
||||
tidBuff := new(bytes.Buffer)
|
||||
binary.Write(tidBuff, binary.LittleEndian, t.id)
|
||||
// fake_http_hostname := "speedtest.cn"
|
||||
// user_agent := "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||
// ul.WriteMessage(MsgP2P, 100, fmt.Sprintf("GET / HTTP/1.1\r\nHost: %s\r\nUser-Agent: %s\r\nAccept: */*\r\n\r\n",
|
||||
// fake_http_hostname, user_agent))
|
||||
ul.WriteBytes(MsgP2P, MsgTunnelHandshake, tidBuff.Bytes()) // tunnelID
|
||||
_, buff, err := ul.ReadBuffer()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read MsgTunnelHandshake error:%s", err)
|
||||
}
|
||||
if buff != nil {
|
||||
gLog.Println(LvDEBUG, "hello ", string(buff))
|
||||
gLog.d("hello %s", string(buff))
|
||||
}
|
||||
|
||||
gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin))
|
||||
gLog.Println(LvINFO, "TCP connection ok")
|
||||
gLog.i("rtt=%dms", time.Since(handshakeBegin)/time.Millisecond)
|
||||
t.linkModeWeb = LinkModeIPv4
|
||||
if t.config.linkMode == LinkModeIntranet {
|
||||
t.linkModeWeb = LinkModeIntranet
|
||||
}
|
||||
if t.config.linkMode == LinkModeTCP6 {
|
||||
t.linkModeWeb = LinkModeIPv6
|
||||
}
|
||||
gLog.i("%s TCP connection ok", t.linkModeWeb)
|
||||
return ul, nil
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) connectUnderlayTCPSymmetric() (c underlay, err error) {
|
||||
gLog.Printf(LvDEBUG, "connectUnderlayTCPSymmetric %s start ", t.config.LogPeerNode())
|
||||
defer gLog.Printf(LvDEBUG, "connectUnderlayTCPSymmetric %s end ", t.config.LogPeerNode())
|
||||
gLog.d("connectUnderlayTCPSymmetric %s start ", t.config.LogPeerNode())
|
||||
defer gLog.d("connectUnderlayTCPSymmetric %s end ", t.config.LogPeerNode())
|
||||
ts := time.Duration(int64(t.punchTs) + GNetwork.dt + GNetwork.ddtma*int64(time.Since(GNetwork.hbTime)+PunchTsDelay)/int64(NetworkHeartbeatTime) - time.Now().UnixNano())
|
||||
if ts > PunchTsDelay || ts < 0 {
|
||||
ts = PunchTsDelay
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "sleep %d ms", ts/time.Millisecond)
|
||||
gLog.d("sleep %d ms", ts/time.Millisecond)
|
||||
time.Sleep(ts)
|
||||
startTime := time.Now()
|
||||
t.linkModeWeb = LinkModeTCPPunch
|
||||
@@ -424,8 +475,8 @@ func (t *P2PTunnel) connectUnderlayTCPSymmetric() (c underlay, err error) {
|
||||
return
|
||||
}
|
||||
_, buff, err := ul.ReadBuffer()
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "c2s ul.ReadBuffer error:", err)
|
||||
if err != nil || buff == nil {
|
||||
gLog.d("c2s ul.ReadBuffer error:%s", err)
|
||||
return
|
||||
}
|
||||
req := P2PHandshakeReq{}
|
||||
@@ -435,7 +486,7 @@ func (t *P2PTunnel) connectUnderlayTCPSymmetric() (c underlay, err error) {
|
||||
if req.ID != t.id {
|
||||
return
|
||||
}
|
||||
gLog.Printf(LvINFO, "handshakeS2C TCP ok. cost %dms", time.Since(startTime)/time.Millisecond)
|
||||
gLog.i("handshakeS2C TCP ok. cost %dms", time.Since(startTime)/time.Millisecond)
|
||||
|
||||
gotCh <- ul
|
||||
close(gotCh)
|
||||
@@ -453,8 +504,8 @@ func (t *P2PTunnel) connectUnderlayTCPSymmetric() (c underlay, err error) {
|
||||
}
|
||||
|
||||
_, buff, err := ul.ReadBuffer()
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "s2c ul.ReadBuffer error:", err)
|
||||
if err != nil || buff == nil {
|
||||
gLog.d("s2c ul.ReadBuffer error:%s", err)
|
||||
return
|
||||
}
|
||||
req := P2PHandshakeReq{}
|
||||
@@ -485,91 +536,121 @@ func (t *P2PTunnel) connectUnderlayTCPSymmetric() (c underlay, err error) {
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) connectUnderlayTCP6() (c underlay, err error) {
|
||||
gLog.Printf(LvDEBUG, "connectUnderlayTCP6 %s start ", t.config.LogPeerNode())
|
||||
defer gLog.Printf(LvDEBUG, "connectUnderlayTCP6 %s end ", t.config.LogPeerNode())
|
||||
var ul *underlayTCP6
|
||||
gLog.d("connectUnderlayTCP6 %s start ", t.config.LogPeerNode())
|
||||
defer gLog.d("connectUnderlayTCP6 %s end ", t.config.LogPeerNode())
|
||||
tidBuff := new(bytes.Buffer)
|
||||
binary.Write(tidBuff, binary.LittleEndian, t.id)
|
||||
if t.config.isUnderlayServer == 1 {
|
||||
GNetwork.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
|
||||
ul, err = listenTCP6(t.coneNatPort, UnderlayConnectTimeout)
|
||||
if err != nil {
|
||||
// ul, err = listenTCP6(t.coneNatPort, UnderlayConnectTimeout)
|
||||
tid := t.id
|
||||
if compareVersion(t.config.peerVersion, PublicIPVersion) < 0 { // old version
|
||||
ipBytes := net.ParseIP(t.config.peerIP).To4()
|
||||
tid = uint64(binary.BigEndian.Uint32(ipBytes))
|
||||
gLog.d("compatible with old client, use ip as key:%d", tid)
|
||||
}
|
||||
|
||||
if v4l != nil {
|
||||
c = v4l.getUnderlay(tid)
|
||||
}
|
||||
if c == nil {
|
||||
return nil, fmt.Errorf("listen TCP6 error:%s", err)
|
||||
}
|
||||
_, buff, err := ul.ReadBuffer()
|
||||
_, buff, err := c.ReadBuffer()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read start msg error:%s", err)
|
||||
}
|
||||
if buff != nil {
|
||||
gLog.Println(LvDEBUG, string(buff))
|
||||
gLog.d("handshake flag:%s", string(buff))
|
||||
}
|
||||
ul.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, []byte("OpenP2P,hello2"))
|
||||
gLog.Println(LvDEBUG, "TCP6 connection ok")
|
||||
c.WriteBytes(MsgP2P, MsgTunnelHandshake, tidBuff.Bytes()) // tunnelID
|
||||
// ul.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, []byte("OpenP2P,hello2"))
|
||||
gLog.d("TCP6 connection ok")
|
||||
t.linkModeWeb = LinkModeIPv6
|
||||
return ul, nil
|
||||
return c, nil
|
||||
}
|
||||
|
||||
//else
|
||||
GNetwork.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, ReadMsgTimeout)
|
||||
gLog.Println(LvDEBUG, "TCP6 dial to ", t.config.peerIPv6)
|
||||
ul, err = dialTCP6(t.config.peerIPv6, t.config.peerConeNatPort)
|
||||
gLog.d("TCP6 dial to %s", t.config.peerIPv6)
|
||||
ul, err := dialTCP(fmt.Sprintf("[%s]", t.config.peerIPv6), t.config.peerConeNatPort, 0, LinkModeTCP6)
|
||||
if err != nil || ul == nil {
|
||||
return nil, fmt.Errorf("TCP6 dial to %s:%d error:%s", t.config.peerIPv6, t.config.peerConeNatPort, err)
|
||||
}
|
||||
handshakeBegin := time.Now()
|
||||
ul.WriteBytes(MsgP2P, MsgTunnelHandshake, []byte("OpenP2P,hello"))
|
||||
ul.WriteBytes(MsgP2P, MsgTunnelHandshake, tidBuff.Bytes()) // tunnelID
|
||||
// ul.WriteBytes(MsgP2P, MsgTunnelHandshake, []byte("OpenP2P,hello"))
|
||||
_, buff, errR := ul.ReadBuffer()
|
||||
if errR != nil {
|
||||
return nil, fmt.Errorf("read MsgTunnelHandshake error:%s", errR)
|
||||
}
|
||||
if buff != nil {
|
||||
gLog.Println(LvDEBUG, string(buff))
|
||||
gLog.d("handshake flag:%s", string(buff))
|
||||
}
|
||||
|
||||
gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin))
|
||||
gLog.Println(LvINFO, "TCP6 connection ok")
|
||||
gLog.i("rtt=%dms", time.Since(handshakeBegin))
|
||||
gLog.i("TCP6 connection ok")
|
||||
t.linkModeWeb = LinkModeIPv6
|
||||
return ul, nil
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) readLoop() {
|
||||
decryptData := make([]byte, ReadBuffLen+PaddingSize) // 16 bytes for padding
|
||||
gLog.Printf(LvDEBUG, "%d tunnel readloop start", t.id)
|
||||
gLog.d("%d tunnel readloop start", t.id)
|
||||
for t.isRuning() {
|
||||
t.conn.SetReadDeadline(time.Now().Add(TunnelHeartbeatTime * 2))
|
||||
head, body, err := t.conn.ReadBuffer()
|
||||
if err != nil {
|
||||
if err != nil || head == nil {
|
||||
if t.isRuning() {
|
||||
gLog.Printf(LvERROR, "%d tunnel read error:%s", t.id, err)
|
||||
gLog.d("%d tunnel read error:%s", t.id, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
if head.MainType != MsgP2P {
|
||||
gLog.Printf(LvWARN, "%d head.MainType != MsgP2P", t.id)
|
||||
gLog.w("%d head.MainType(%d) != MsgP2P", head.MainType, t.id)
|
||||
continue
|
||||
}
|
||||
// gLog.d("%d tunnel read %d:%d len=%d", t.id, head.MainType, head.SubType, head.DataLen)
|
||||
// TODO: replace some case implement to functions
|
||||
switch head.SubType {
|
||||
case MsgTunnelHeartbeat:
|
||||
t.hbMtx.Lock()
|
||||
t.hbTime = time.Now()
|
||||
t.hbMtx.Unlock()
|
||||
t.conn.WriteBytes(MsgP2P, MsgTunnelHeartbeatAck, nil)
|
||||
gLog.Printf(LvDev, "%d read tunnel heartbeat", t.id)
|
||||
memAppPeerID := new(bytes.Buffer)
|
||||
binary.Write(memAppPeerID, binary.LittleEndian, gConf.Network.nodeID)
|
||||
t.conn.WriteBytes(MsgP2P, MsgTunnelHeartbeatAck, memAppPeerID.Bytes())
|
||||
gLog.dev("%d read tunnel heartbeat", t.id)
|
||||
case MsgTunnelHeartbeatAck:
|
||||
t.hbMtx.Lock()
|
||||
t.hbTime = time.Now()
|
||||
t.hbMtx.Unlock()
|
||||
gLog.Printf(LvDev, "%d read tunnel heartbeat ack", t.id)
|
||||
if head.DataLen >= 8 {
|
||||
memAppPeerID := binary.LittleEndian.Uint64(body[:8])
|
||||
existApp, appok := GNetwork.apps.Load(memAppPeerID)
|
||||
if appok {
|
||||
app := existApp.(*p2pApp)
|
||||
for i := 0; i < app.relayIdxStart; i++ {
|
||||
if app.Tunnel(i) == t {
|
||||
app.rtt[i].Store(int32(time.Since(t.whbTime) / time.Millisecond))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gLog.dev("%d read tunnel heartbeat ack, rtt=%dms", t.id, time.Since(t.whbTime)/time.Millisecond)
|
||||
case MsgOverlayData:
|
||||
if len(body) < overlayHeaderSize {
|
||||
gLog.Printf(LvWARN, "%d len(body) < overlayHeaderSize", t.id)
|
||||
gLog.w("%d len(body) < overlayHeaderSize", t.id)
|
||||
continue
|
||||
}
|
||||
overlayID := binary.LittleEndian.Uint64(body[:8])
|
||||
gLog.Printf(LvDev, "%d tunnel read overlay data %d bodylen=%d", t.id, overlayID, head.DataLen)
|
||||
s, ok := t.overlayConns.Load(overlayID)
|
||||
gLog.dev("%d tunnel read overlay data %d bodylen=%d", t.id, overlayID, head.DataLen)
|
||||
s, ok := overlayConns.Load(overlayID)
|
||||
if !ok {
|
||||
// debug level, when overlay connection closed, always has some packet not found tunnel
|
||||
gLog.Printf(LvDEBUG, "%d tunnel not found overlay connection %d", t.id, overlayID)
|
||||
gLog.d("%d tunnel not found overlay connection %d", t.id, overlayID)
|
||||
continue
|
||||
}
|
||||
overlayConn, ok := s.(*overlayConn)
|
||||
@@ -578,101 +659,86 @@ func (t *P2PTunnel) readLoop() {
|
||||
}
|
||||
payload := body[overlayHeaderSize:]
|
||||
var err error
|
||||
if overlayConn.appKey != 0 {
|
||||
payload, _ = decryptBytes(overlayConn.appKeyBytes, decryptData, body[overlayHeaderSize:], int(head.DataLen-uint32(overlayHeaderSize)))
|
||||
if overlayConn.app.key != 0 {
|
||||
payload, _ = decryptBytes(overlayConn.app.appKeyBytes, decryptData, body[overlayHeaderSize:], int(head.DataLen-uint32(overlayHeaderSize)))
|
||||
}
|
||||
_, err = overlayConn.Write(payload)
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "overlay write error:", err)
|
||||
gLog.e("overlay write error:%s", err)
|
||||
}
|
||||
case MsgNodeData:
|
||||
case MsgNodeDataMP:
|
||||
t.handleNodeDataMP(head, body)
|
||||
case MsgNodeDataMPAck:
|
||||
t.handleNodeDataMPAck(head, body)
|
||||
case MsgNodeData: // unused
|
||||
t.handleNodeData(head, body, false)
|
||||
case MsgRelayNodeData:
|
||||
case MsgRelayNodeData: // unused
|
||||
t.handleNodeData(head, body, true)
|
||||
case MsgRelayData:
|
||||
if len(body) < 8 {
|
||||
continue
|
||||
}
|
||||
tunnelID := binary.LittleEndian.Uint64(body[:8])
|
||||
gLog.Printf(LvDev, "relay data to %d, len=%d", tunnelID, head.DataLen-RelayHeaderSize)
|
||||
gLog.dev("relay data to %d, len=%d", tunnelID, head.DataLen-RelayHeaderSize)
|
||||
if err := GNetwork.relay(tunnelID, body[RelayHeaderSize:]); err != nil {
|
||||
gLog.Printf(LvERROR, "%s:%d relay to %d len=%d error:%s", t.config.LogPeerNode(), t.id, tunnelID, len(body), ErrRelayTunnelNotFound)
|
||||
gLog.d("%s:%d relay to %d len=%d error:%s", t.config.LogPeerNode(), t.id, tunnelID, len(body), ErrRelayTunnelNotFound)
|
||||
}
|
||||
case MsgRelayHeartbeat:
|
||||
case MsgRelayHeartbeat: // only client side will write relay heartbeat, different with tunnel heartbeat
|
||||
req := RelayHeartbeat{}
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
|
||||
gLog.e("wrong %v:%s", reflect.TypeOf(req), err)
|
||||
continue
|
||||
}
|
||||
// TODO: debug relay heartbeat
|
||||
gLog.Printf(LvDEBUG, "read MsgRelayHeartbeat from rtid:%d,appid:%d", req.RelayTunnelID, req.AppID)
|
||||
gLog.dev("read MsgRelayHeartbeat from rtid:%d,appid:%d", req.RelayTunnelID, req.AppID)
|
||||
// update app hbtime
|
||||
GNetwork.updateAppHeartbeat(req.AppID)
|
||||
GNetwork.updateAppHeartbeat(req.AppID, req.RelayTunnelID, true)
|
||||
req.From = gConf.Network.Node
|
||||
t.WriteMessage(req.RelayTunnelID, MsgP2P, MsgRelayHeartbeatAck, &req)
|
||||
case MsgRelayHeartbeatAck:
|
||||
req := RelayHeartbeat{}
|
||||
err := json.Unmarshal(body, &req)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "wrong RelayHeartbeat:%s", err)
|
||||
gLog.e("wrong RelayHeartbeat:%s", err)
|
||||
continue
|
||||
}
|
||||
// TODO: debug relay heartbeat
|
||||
gLog.Printf(LvDEBUG, "read MsgRelayHeartbeatAck to appid:%d", req.AppID)
|
||||
GNetwork.updateAppHeartbeat(req.AppID)
|
||||
case MsgOverlayConnectReq:
|
||||
req := OverlayConnectReq{}
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
|
||||
continue
|
||||
}
|
||||
// app connect only accept token(not relay totp token), avoid someone using the share relay node's token
|
||||
if req.Token != gConf.Network.Token {
|
||||
gLog.Println(LvERROR, "Access Denied:", req.Token)
|
||||
continue
|
||||
}
|
||||
|
||||
overlayID := req.ID
|
||||
gLog.Printf(LvDEBUG, "App:%d overlayID:%d connect %s:%d", req.AppID, overlayID, req.DstIP, req.DstPort)
|
||||
oConn := overlayConn{
|
||||
tunnel: t,
|
||||
id: overlayID,
|
||||
isClient: false,
|
||||
rtid: req.RelayTunnelID,
|
||||
appID: req.AppID,
|
||||
appKey: GetKey(req.AppID),
|
||||
running: true,
|
||||
}
|
||||
if req.Protocol == "udp" {
|
||||
oConn.connUDP, err = net.DialUDP("udp", nil, &net.UDPAddr{IP: net.ParseIP(req.DstIP), Port: req.DstPort})
|
||||
} else {
|
||||
oConn.connTCP, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", req.DstIP, req.DstPort), ReadMsgTimeout)
|
||||
|
||||
}
|
||||
gLog.dev("read MsgRelayHeartbeatAck to appid:%d", req.AppID)
|
||||
GNetwork.updateAppHeartbeat(req.AppID, req.RelayTunnelID, false)
|
||||
req.From = gConf.Network.Node
|
||||
t.WriteMessage(req.RelayTunnelID2, MsgP2P, MsgRelayHeartbeatAck2, &req)
|
||||
case MsgRelayHeartbeatAck2:
|
||||
req := RelayHeartbeat{}
|
||||
err := json.Unmarshal(body, &req)
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, err)
|
||||
gLog.e("wrong RelayHeartbeat:%s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// calc key bytes for encrypt
|
||||
if oConn.appKey != 0 {
|
||||
encryptKey := make([]byte, AESKeySize)
|
||||
binary.LittleEndian.PutUint64(encryptKey, oConn.appKey)
|
||||
binary.LittleEndian.PutUint64(encryptKey[8:], oConn.appKey)
|
||||
oConn.appKeyBytes = encryptKey
|
||||
gLog.dev("read MsgRelayHeartbeatAck2 to appid:%d", req.AppID)
|
||||
GNetwork.updateAppHeartbeat(req.AppID, req.RelayTunnelID, false)
|
||||
case MsgOverlayConnectReq: // TODO: send this msg withAppID, and app handle it
|
||||
// app connect only accept token(not relay totp token), avoid someone using the share relay node's token
|
||||
// targetApp := GNetwork.GetAPPByID(req.AppID)
|
||||
t.handleOverlayConnectReq(body, err)
|
||||
case MsgOverlayConnectRsp:
|
||||
appID := binary.LittleEndian.Uint64(body[:8])
|
||||
i, ok := GNetwork.apps.Load(appID)
|
||||
if !ok {
|
||||
gLog.e("MsgOverlayConnectRsp app not found %d", appID)
|
||||
return
|
||||
}
|
||||
|
||||
t.overlayConns.Store(oConn.id, &oConn)
|
||||
go oConn.run()
|
||||
app := i.(*p2pApp)
|
||||
// ndmp := NodeDataMPHeader{fromNodeID: gConf.Network.nodeID, seq: seq}
|
||||
app.StoreMessage(head, body)
|
||||
case MsgOverlayDisconnectReq:
|
||||
req := OverlayDisconnectReq{}
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
|
||||
gLog.e("wrong %v:%s", reflect.TypeOf(req), err)
|
||||
continue
|
||||
}
|
||||
overlayID := req.ID
|
||||
gLog.Printf(LvDEBUG, "%d disconnect overlay connection %d", t.id, overlayID)
|
||||
i, ok := t.overlayConns.Load(overlayID)
|
||||
gLog.d("%d disconnect overlay connection %d", t.id, overlayID)
|
||||
i, ok := overlayConns.Load(overlayID)
|
||||
if ok {
|
||||
oConn := i.(*overlayConn)
|
||||
oConn.Close()
|
||||
@@ -681,7 +747,55 @@ func (t *P2PTunnel) readLoop() {
|
||||
}
|
||||
}
|
||||
t.close()
|
||||
gLog.Printf(LvDEBUG, "%d tunnel readloop end", t.id)
|
||||
gLog.d("%d tunnel readloop end", t.id)
|
||||
}
|
||||
|
||||
func (*P2PTunnel) handleOverlayConnectReq(body []byte, err error) {
|
||||
req := OverlayConnectReq{}
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
gLog.e("wrong %v:%s", reflect.TypeOf(req), err)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Token != gConf.Network.Token {
|
||||
gLog.e("Access Denied,token=%d", req.Token)
|
||||
return
|
||||
}
|
||||
|
||||
overlayID := req.ID
|
||||
gLog.d("App:%d overlayID:%d connect %s:%d", req.AppID, overlayID, req.DstIP, req.DstPort)
|
||||
|
||||
i, ok := GNetwork.apps.Load(req.AppID)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
targetApp := i.(*p2pApp)
|
||||
oConn := overlayConn{
|
||||
app: targetApp,
|
||||
id: overlayID,
|
||||
isClient: false,
|
||||
running: true,
|
||||
}
|
||||
// connect local service should use sys dns
|
||||
sysResolver := &net.Resolver{}
|
||||
ips, err := sysResolver.LookupIP(context.Background(), "ip4", req.DstIP)
|
||||
if err != nil {
|
||||
gLog.e("handleOverlayConnectReq dial error:%s", err)
|
||||
return
|
||||
}
|
||||
if req.Protocol == "udp" {
|
||||
oConn.connUDP, err = net.DialUDP("udp", nil, &net.UDPAddr{IP: ips[0], Port: req.DstPort})
|
||||
} else {
|
||||
oConn.connTCP, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ips[0].String(), req.DstPort), ReadMsgTimeout)
|
||||
|
||||
}
|
||||
if err != nil {
|
||||
gLog.e("handleOverlayConnectReq dial error:%s", err)
|
||||
return
|
||||
}
|
||||
overlayConns.Store(oConn.id, &oConn)
|
||||
go oConn.run()
|
||||
targetApp.WriteMessageWithAppID(MsgP2P, MsgOverlayConnectRsp, nil)
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) writeLoop() {
|
||||
@@ -690,29 +804,39 @@ func (t *P2PTunnel) writeLoop() {
|
||||
t.hbMtx.Unlock()
|
||||
tc := time.NewTicker(TunnelHeartbeatTime)
|
||||
defer tc.Stop()
|
||||
gLog.Printf(LvDEBUG, "%s:%d tunnel writeLoop start", t.config.LogPeerNode(), t.id)
|
||||
defer gLog.Printf(LvDEBUG, "%s:%d tunnel writeLoop end", t.config.LogPeerNode(), t.id)
|
||||
gLog.d("%s:%d tunnel writeLoop start", t.config.LogPeerNode(), t.id)
|
||||
defer gLog.d("%s:%d tunnel writeLoop end", t.config.LogPeerNode(), t.id)
|
||||
writeHb := func() {
|
||||
// tunnel send
|
||||
t.whbTime = time.Now()
|
||||
err := t.conn.WriteBytes(MsgP2P, MsgTunnelHeartbeat, nil)
|
||||
if err != nil {
|
||||
gLog.d("%d write tunnel heartbeat error %s", t.id, err)
|
||||
t.close()
|
||||
return
|
||||
}
|
||||
gLog.dev("%d write tunnel heartbeat ok", t.id)
|
||||
}
|
||||
writeHb()
|
||||
for t.isRuning() {
|
||||
select {
|
||||
case buff := <-t.writeDataSmall:
|
||||
t.conn.WriteBuffer(buff)
|
||||
// gLog.Printf(LvDEBUG, "write icmp %d", time.Now().Unix())
|
||||
// gLog.d("write icmp %d", time.Now().Unix())
|
||||
default:
|
||||
select {
|
||||
case buff := <-t.writeDataSmall:
|
||||
t.conn.WriteBuffer(buff)
|
||||
// gLog.Printf(LvDEBUG, "write icmp %d", time.Now().Unix())
|
||||
// gLog.d("write icmp %d", time.Now().Unix())
|
||||
case buff := <-t.writeData:
|
||||
t.conn.WriteBuffer(buff)
|
||||
case <-tc.C:
|
||||
// tunnel send
|
||||
err := t.conn.WriteBytes(MsgP2P, MsgTunnelHeartbeat, nil)
|
||||
err := t.conn.WriteBuffer(buff)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "%d write tunnel heartbeat error %s", t.id, err)
|
||||
gLog.e("%d write tunnel error %s", t.id, err)
|
||||
t.close()
|
||||
return
|
||||
}
|
||||
gLog.Printf(LvDev, "%d write tunnel heartbeat ok", t.id)
|
||||
case <-tc.C:
|
||||
writeHb()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -742,54 +866,74 @@ func (t *P2PTunnel) listen() error {
|
||||
}
|
||||
|
||||
GNetwork.push(t.config.PeerNode, MsgPushConnectRsp, rsp)
|
||||
gLog.Printf(LvDEBUG, "p2ptunnel wait for connecting")
|
||||
t.tunnelServer = true
|
||||
gLog.d("p2ptunnel wait for connecting")
|
||||
return t.start()
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) closeOverlayConns(appID uint64) {
|
||||
t.overlayConns.Range(func(_, i interface{}) bool {
|
||||
oConn := i.(*overlayConn)
|
||||
if oConn.appID == appID {
|
||||
oConn.Close()
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) handleNodeData(head *openP2PHeader, body []byte, isRelay bool) {
|
||||
gLog.Printf(LvDev, "%d tunnel read node data bodylen=%d, relay=%t", t.id, head.DataLen, isRelay)
|
||||
gLog.dev("%d tunnel read node data bodylen=%d, relay=%t", t.id, head.DataLen, isRelay)
|
||||
ch := GNetwork.nodeData
|
||||
// if body[9] == 1 { // TODO: deal relay
|
||||
// ch = GNetwork.nodeDataSmall
|
||||
// gLog.Printf(LvDEBUG, "read icmp %d", time.Now().Unix())
|
||||
// gLog.d("read icmp %d", time.Now().Unix())
|
||||
// }
|
||||
if isRelay {
|
||||
fromPeerID := binary.LittleEndian.Uint64(body[:8])
|
||||
ch <- &NodeData{fromPeerID, body[8:]} // TODO: cache peerNodeID; encrypt/decrypt
|
||||
// fromPeerID := binary.LittleEndian.Uint64(body[:8]) // unused
|
||||
ch <- body[8:] // TODO: cache peerNodeID; encrypt/decrypt
|
||||
} else {
|
||||
ch <- &NodeData{NodeNameToID(t.config.PeerNode), body} // TODO: cache peerNodeID; encrypt/decrypt
|
||||
ch <- body // TODO: cache peerNodeID; encrypt/decrypt
|
||||
}
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) asyncWriteNodeData(mainType, subType uint16, data []byte) {
|
||||
writeBytes := append(encodeHeader(mainType, subType, uint32(len(data))), data...)
|
||||
// if len(data) < 192 {
|
||||
if data[9] == 1 { // icmp
|
||||
select {
|
||||
case t.writeDataSmall <- writeBytes:
|
||||
// gLog.Printf(LvWARN, "%s:%d t.writeDataSmall write %d", t.config.PeerNode, t.id, len(t.writeDataSmall))
|
||||
default:
|
||||
gLog.Printf(LvWARN, "%s:%d t.writeDataSmall is full, drop it", t.config.LogPeerNode(), t.id)
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case t.writeData <- writeBytes:
|
||||
default:
|
||||
gLog.Printf(LvWARN, "%s:%d t.writeData is full, drop it", t.config.LogPeerNode(), t.id)
|
||||
}
|
||||
func (t *P2PTunnel) handleNodeDataMP(head *openP2PHeader, body []byte) {
|
||||
gLog.dev("%s tid:%d tunnel read node data mp bodylen=%d", t.config.LogPeerNode(), t.id, head.DataLen) // Debug
|
||||
if head.DataLen < 16 {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: reorder write tun
|
||||
fromNodeID := binary.LittleEndian.Uint64(body[:8])
|
||||
seq := binary.LittleEndian.Uint64(body[8:16])
|
||||
i, ok := GNetwork.apps.Load(fromNodeID)
|
||||
if !ok {
|
||||
gLog.e("handleNodeDataMP peer not found,from=%s nodeID=%d, seq=%d", t.config.LogPeerNode(), fromNodeID, seq)
|
||||
return
|
||||
}
|
||||
app := i.(*p2pApp)
|
||||
// ndmp := NodeDataMPHeader{fromNodeID: gConf.Network.nodeID, seq: seq}
|
||||
app.handleNodeDataMP(seq, body[16:], t)
|
||||
|
||||
}
|
||||
func (t *P2PTunnel) handleNodeDataMPAck(head *openP2PHeader, body []byte) {
|
||||
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) asyncWriteNodeData(id uint64, seq uint64, IPPacket []byte, relayHead []byte) {
|
||||
all := new(bytes.Buffer)
|
||||
if relayHead != nil {
|
||||
all.Write(encodeHeader(MsgP2P, MsgRelayData, uint32(openP2PHeaderSize+len(relayHead)+16+len(IPPacket))))
|
||||
all.Write(relayHead)
|
||||
}
|
||||
all.Write(encodeHeader(MsgP2P, MsgNodeDataMP, 16+uint32(len(IPPacket)))) // id+seq=16 bytes
|
||||
binary.Write(all, binary.LittleEndian, id)
|
||||
binary.Write(all, binary.LittleEndian, seq)
|
||||
all.Write(IPPacket)
|
||||
// if len(data) < 192 {
|
||||
if IPPacket[9] == 1 { // icmp
|
||||
select {
|
||||
case t.writeDataSmall <- all.Bytes():
|
||||
// gLog.w("%s:%d t.writeDataSmall write %d", t.config.PeerNode, t.id, len(t.writeDataSmall))
|
||||
default:
|
||||
gLog.w("%s:%d t.writeDataSmall is full, drop it", t.config.LogPeerNode(), t.id)
|
||||
}
|
||||
} else {
|
||||
t.writeData <- all.Bytes()
|
||||
// select {
|
||||
// case t.writeData <- writeBytes:
|
||||
// default:
|
||||
// gLog.w("%s:%d t.writeData is full, drop it", t.config.LogPeerNode(), t.id)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) WriteMessage(rtid uint64, mainType uint16, subType uint16, req interface{}) error {
|
||||
@@ -803,3 +947,41 @@ func (t *P2PTunnel) WriteMessage(rtid uint64, mainType uint16, subType uint16, r
|
||||
return t.conn.WriteBytes(mainType, MsgRelayData, msgWithHead)
|
||||
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) WriteMessageWithAppID(appID uint64, rtid uint64, mainType uint16, subType uint16, req interface{}) error {
|
||||
data, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
head := new(bytes.Buffer)
|
||||
binary.Write(head, binary.LittleEndian, appID)
|
||||
msgWithAppID := append(head.Bytes(), data...)
|
||||
if rtid == 0 {
|
||||
return t.conn.WriteBytes(mainType, subType, msgWithAppID)
|
||||
}
|
||||
relayHead := new(bytes.Buffer)
|
||||
binary.Write(relayHead, binary.LittleEndian, rtid)
|
||||
msg, _ := newMessageWithBuff(mainType, subType, msgWithAppID)
|
||||
msgWithHead := append(relayHead.Bytes(), msg...)
|
||||
return t.conn.WriteBytes(mainType, MsgRelayData, msgWithHead)
|
||||
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) WriteBytes(rtid uint64, mainType uint16, subType uint16, data []byte) error {
|
||||
if rtid == 0 {
|
||||
return t.conn.WriteBytes(mainType, subType, data)
|
||||
}
|
||||
all := new(bytes.Buffer)
|
||||
binary.Write(all, binary.LittleEndian, rtid)
|
||||
all.Write(encodeHeader(mainType, subType, uint32(len(data))))
|
||||
all.Write(data)
|
||||
return t.conn.WriteBytes(mainType, MsgRelayData, all.Bytes())
|
||||
|
||||
}
|
||||
|
||||
// func (t *P2PTunnel) RTT() int {
|
||||
// if t.isWriting.Load() && t.rtt.Load() < int32(time.Now().Add(time.Duration(-t.writingTs.Load())).Unix()/int64(time.Millisecond)) {
|
||||
// return int(time.Now().Add(time.Duration(-t.writingTs.Load())).Unix() / int64(time.Millisecond))
|
||||
// }
|
||||
// return int(t.rtt.Load())
|
||||
// }
|
||||
|
||||
211
core/protocol.go
@@ -10,7 +10,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const OpenP2PVersion = "3.21.12"
|
||||
const OpenP2PVersion = "3.25.8"
|
||||
const ProductName string = "openp2p"
|
||||
const LeastSupportVersion = "3.0.0"
|
||||
const SyncServerTimeVersion = "3.9.0"
|
||||
@@ -18,13 +18,16 @@ const SymmetricSimultaneouslySendVersion = "3.10.7"
|
||||
const PublicIPVersion = "3.11.2"
|
||||
const SupportIntranetVersion = "3.14.5"
|
||||
const SupportDualTunnelVersion = "3.15.5"
|
||||
|
||||
const IPv6PunchVersion = "3.24.9"
|
||||
const SupportUDP4DirectVersion = "3.24.16"
|
||||
const SupportMultiDirectVersion = "3.25.1"
|
||||
const (
|
||||
IfconfigPort1 = 27180
|
||||
IfconfigPort2 = 27181
|
||||
WsPort = 27183
|
||||
UDPPort1 = 27182
|
||||
UDPPort2 = 27183
|
||||
NATDetectPort1 = 27180
|
||||
NATDetectPort2 = 27181
|
||||
WsPort = 27183
|
||||
WsPort2 = 465
|
||||
UDPPort1 = 27182
|
||||
UDPPort2 = 27183
|
||||
)
|
||||
|
||||
type openP2PHeader struct {
|
||||
@@ -48,6 +51,12 @@ type overlayHeader struct {
|
||||
id uint64
|
||||
}
|
||||
|
||||
type NodeDataMPAck struct {
|
||||
FromNodeID uint64
|
||||
Seq uint64
|
||||
Delay uint32 // delay write mergeack ms
|
||||
}
|
||||
|
||||
var overlayHeaderSize = binary.Size(overlayHeader{})
|
||||
|
||||
func decodeHeader(data []byte) (*openP2PHeader, error) {
|
||||
@@ -74,7 +83,7 @@ func encodeHeader(mainType uint16, subType uint16, len uint32) []byte {
|
||||
return headBuf.Bytes()
|
||||
}
|
||||
|
||||
// Message type
|
||||
// Message main type
|
||||
const (
|
||||
MsgLogin = 0
|
||||
MsgHeartbeat = 1
|
||||
@@ -109,42 +118,50 @@ const (
|
||||
MsgPushReportMemApps = 17
|
||||
MsgPushServerSideSaveMemApp = 18
|
||||
MsgPushCheckRemoteService = 19
|
||||
MsgPushSpecTunnel = 20
|
||||
MsgPushReportHeap = 21
|
||||
MsgPushSDWanRefresh = 22
|
||||
MsgPushNat4Detect = 23
|
||||
)
|
||||
|
||||
// MsgP2P sub type message
|
||||
const (
|
||||
MsgPunchHandshake = iota
|
||||
MsgPunchHandshakeAck
|
||||
MsgTunnelHandshake
|
||||
MsgTunnelHandshakeAck
|
||||
MsgTunnelHeartbeat
|
||||
MsgTunnelHeartbeatAck
|
||||
MsgOverlayConnectReq
|
||||
MsgOverlayConnectRsp
|
||||
MsgOverlayDisconnectReq
|
||||
MsgOverlayData
|
||||
MsgRelayData
|
||||
MsgRelayHeartbeat
|
||||
MsgRelayHeartbeatAck
|
||||
MsgNodeData
|
||||
MsgRelayNodeData
|
||||
MsgPunchHandshake = 0
|
||||
MsgPunchHandshakeAck = 1
|
||||
MsgTunnelHandshake = 2
|
||||
MsgTunnelHandshakeAck = 3
|
||||
MsgTunnelHeartbeat = 4
|
||||
MsgTunnelHeartbeatAck = 5
|
||||
MsgOverlayConnectReq = 6
|
||||
MsgOverlayConnectRsp = 7
|
||||
MsgOverlayDisconnectReq = 8
|
||||
MsgOverlayData = 9
|
||||
MsgRelayData = 10
|
||||
MsgRelayHeartbeat = 11
|
||||
MsgRelayHeartbeatAck = 12
|
||||
MsgNodeData = 13
|
||||
MsgRelayNodeData = 14
|
||||
MsgNodeDataMP = 15
|
||||
MsgNodeDataMPAck = 16
|
||||
MsgRelayHeartbeatAck2 = 17
|
||||
)
|
||||
|
||||
// MsgRelay sub type message
|
||||
const (
|
||||
MsgRelayNodeReq = iota
|
||||
MsgRelayNodeRsp
|
||||
MsgRelayNodeReq = 0
|
||||
MsgRelayNodeRsp = 1
|
||||
)
|
||||
|
||||
// MsgReport sub type message
|
||||
const (
|
||||
MsgReportBasic = iota
|
||||
MsgReportQuery
|
||||
MsgReportConnect
|
||||
MsgReportApps
|
||||
MsgReportLog
|
||||
MsgReportMemApps
|
||||
MsgReportResponse
|
||||
MsgReportBasic = 0
|
||||
MsgReportQuery = 1
|
||||
MsgReportConnect = 2
|
||||
MsgReportApps = 3
|
||||
MsgReportLog = 4
|
||||
MsgReportMemApps = 5
|
||||
MsgReportResponse = 6
|
||||
MsgReportBasicRsp = 7
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -167,16 +184,12 @@ const (
|
||||
MaxRetry = 10
|
||||
Cone2ConeTCPPunchMaxRetry = 1
|
||||
Cone2ConeUDPPunchMaxRetry = 1
|
||||
PublicIPEchoTimeout = time.Second * 3
|
||||
NatTestTimeout = time.Second * 5
|
||||
PublicIPEchoTimeout = time.Second * 5
|
||||
NatDetectTimeout = time.Second * 5
|
||||
UDPReadTimeout = time.Second * 5
|
||||
ClientAPITimeout = time.Second * 10
|
||||
UnderlayConnectTimeout = time.Second * 10
|
||||
MaxDirectTry = 3
|
||||
|
||||
// sdwan
|
||||
ReadTunBuffSize = 1600
|
||||
ReadTunBuffNum = 10
|
||||
)
|
||||
|
||||
// NATNone has public ip
|
||||
@@ -208,19 +221,19 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
MsgQueryPeerInfoReq = iota
|
||||
MsgQueryPeerInfoRsp
|
||||
MsgQueryPeerInfoReq = 0
|
||||
MsgQueryPeerInfoRsp = 1
|
||||
)
|
||||
|
||||
const (
|
||||
MsgSDWANInfoReq = iota
|
||||
MsgSDWANInfoRsp
|
||||
MsgSDWANInfoReq = 0
|
||||
MsgSDWANInfoRsp = 1
|
||||
)
|
||||
|
||||
// MsgNATDetect
|
||||
const (
|
||||
MsgNAT = iota
|
||||
MsgPublicIP
|
||||
MsgNAT = 0
|
||||
MsgPublicIP = 1
|
||||
)
|
||||
|
||||
func newMessage(mainType uint16, subType uint16, packet interface{}) ([]byte, error) {
|
||||
@@ -243,6 +256,21 @@ func newMessage(mainType uint16, subType uint16, packet interface{}) ([]byte, er
|
||||
return writeBytes, nil
|
||||
}
|
||||
|
||||
func newMessageWithBuff(mainType uint16, subType uint16, data []byte) ([]byte, error) {
|
||||
head := openP2PHeader{
|
||||
uint32(len(data)),
|
||||
mainType,
|
||||
subType,
|
||||
}
|
||||
headBuf := new(bytes.Buffer)
|
||||
err := binary.Write(headBuf, binary.LittleEndian, head)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writeBytes := append(headBuf.Bytes(), data...)
|
||||
return writeBytes, nil
|
||||
}
|
||||
|
||||
func NodeNameToID(name string) uint64 {
|
||||
return crc64.Checksum([]byte(name), crc64.MakeTable(crc64.ISO))
|
||||
}
|
||||
@@ -295,6 +323,8 @@ type LoginRsp struct {
|
||||
Token uint64 `json:"token,omitempty"`
|
||||
Ts int64 `json:"ts,omitempty"`
|
||||
LoginMaxDelay int `json:"loginMaxDelay,omitempty"` // seconds
|
||||
Forcev6 int `json:"forcev6,omitempty"`
|
||||
PublicIPPort int `json:"publicIPPort,omitempty"`
|
||||
}
|
||||
|
||||
type NatDetectReq struct {
|
||||
@@ -329,7 +359,8 @@ type TunnelMsg struct {
|
||||
}
|
||||
|
||||
type RelayNodeReq struct {
|
||||
PeerNode string `json:"peerNode,omitempty"`
|
||||
PeerNode string `json:"peerNode,omitempty"`
|
||||
ExcludeNodes string `json:"excludeNodes,omitempty"` //TODO: add exclude ip
|
||||
}
|
||||
|
||||
type RelayNodeRsp struct {
|
||||
@@ -339,13 +370,15 @@ type RelayNodeRsp struct {
|
||||
}
|
||||
|
||||
type AddRelayTunnelReq struct {
|
||||
From string `json:"from,omitempty"`
|
||||
RelayName string `json:"relayName,omitempty"`
|
||||
RelayTunnelID uint64 `json:"relayTunnelID,omitempty"`
|
||||
RelayToken uint64 `json:"relayToken,omitempty"`
|
||||
RelayMode string `json:"relayMode,omitempty"`
|
||||
AppID uint64 `json:"appID,omitempty"` // deprecated
|
||||
AppKey uint64 `json:"appKey,omitempty"` // deprecated
|
||||
From string `json:"from,omitempty"`
|
||||
RelayName string `json:"relayName,omitempty"`
|
||||
RelayTunnelID uint64 `json:"relayTunnelID,omitempty"`
|
||||
RelayToken uint64 `json:"relayToken,omitempty"`
|
||||
RelayMode string `json:"relayMode,omitempty"`
|
||||
AppID uint64 `json:"appID,omitempty"` // deprecated
|
||||
AppKey uint64 `json:"appKey,omitempty"` // deprecated
|
||||
UnderlayProtocol string `json:"underlayProtocol,omitempty"` // quic or kcp, default quic
|
||||
PunchPriority int `json:"punchPriority,omitempty"`
|
||||
}
|
||||
|
||||
type APPKeySync struct {
|
||||
@@ -354,9 +387,10 @@ type APPKeySync struct {
|
||||
}
|
||||
|
||||
type RelayHeartbeat struct {
|
||||
From string `json:"from,omitempty"`
|
||||
RelayTunnelID uint64 `json:"relayTunnelID,omitempty"`
|
||||
AppID uint64 `json:"appID,omitempty"`
|
||||
From string `json:"from,omitempty"`
|
||||
RelayTunnelID uint64 `json:"relayTunnelID,omitempty"`
|
||||
RelayTunnelID2 uint64 `json:"relayTunnelID2,omitempty"`
|
||||
AppID uint64 `json:"appID,omitempty"`
|
||||
}
|
||||
|
||||
type ReportBasic struct {
|
||||
@@ -365,6 +399,7 @@ type ReportBasic struct {
|
||||
LanIP string `json:"lanIP,omitempty"`
|
||||
HasIPv4 int `json:"hasIPv4,omitempty"`
|
||||
IPv6 string `json:"IPv6,omitempty"`
|
||||
PublicIPPort int `json:"publicIPPort,omitempty"`
|
||||
HasUPNPorNATPMP int `json:"hasUPNPorNATPMP,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
NetInfo NetInfo `json:"netInfo,omitempty"`
|
||||
@@ -415,13 +450,16 @@ type AppInfo struct {
|
||||
}
|
||||
|
||||
type ReportApps struct {
|
||||
Apps []AppInfo
|
||||
Apps []AppInfo
|
||||
TunError string `json:"tunError,omitempty"`
|
||||
}
|
||||
|
||||
type ReportLogReq struct {
|
||||
FileName string `json:"fileName,omitempty"`
|
||||
Offset int64 `json:"offset,omitempty"`
|
||||
Len int64 `json:"len,omitempty"`
|
||||
FileName string `json:"fileName,omitempty"`
|
||||
Offset int64 `json:"offset,omitempty"`
|
||||
Len int64 `json:"len,omitempty"`
|
||||
IsSetLogLevel int64 `json:"isSetLogLevel,omitempty"`
|
||||
LogLevel int64 `json:"loglevel,omitempty"`
|
||||
}
|
||||
type ReportLogRsp struct {
|
||||
FileName string `json:"fileName,omitempty"`
|
||||
@@ -434,6 +472,7 @@ type UpdateInfo struct {
|
||||
Error int `json:"error,omitempty"`
|
||||
ErrorDetail string `json:"errorDetail,omitempty"`
|
||||
Url string `json:"url,omitempty"`
|
||||
Url2 string `json:"url2,omitempty"`
|
||||
}
|
||||
|
||||
type NetInfo struct {
|
||||
@@ -465,8 +504,10 @@ type ProfileInfo struct {
|
||||
}
|
||||
|
||||
type EditNode struct {
|
||||
NewName string `json:"newName,omitempty"`
|
||||
Bandwidth int `json:"bandwidth,omitempty"`
|
||||
NewName string `json:"newName,omitempty"`
|
||||
Bandwidth int `json:"bandwidth,omitempty"`
|
||||
Forcev6 int `json:"forcev6,omitempty"`
|
||||
PublicIPPort int `json:"publicIPPort,omitempty"`
|
||||
}
|
||||
|
||||
type QueryPeerInfoReq struct {
|
||||
@@ -501,6 +542,8 @@ type SDWANInfo struct {
|
||||
ForceRelay int32 `json:"forceRelay,omitempty"`
|
||||
PunchPriority int32 `json:"punchPriority,omitempty"`
|
||||
Enable int32 `json:"enable,omitempty"`
|
||||
TunnelNum int32 `json:"tunnelNum,omitempty"`
|
||||
Mtu int32 `json:"mtu,omitempty"`
|
||||
Nodes []*SDWANNode
|
||||
}
|
||||
|
||||
@@ -516,6 +559,10 @@ type ServerSideSaveMemApp struct {
|
||||
RelayTunnelID uint64 `json:"relayTunnelID,omitempty"` // rtid, if not 0 relay
|
||||
RelayMode string `json:"relayMode,omitempty"`
|
||||
AppID uint64 `json:"appID,omitempty"`
|
||||
AppKey uint64 `json:"appKey,omitempty"`
|
||||
RelayIndex uint32 `json:"relayIndex,omitempty"`
|
||||
TunnelNum uint32 `json:"tunnelNum,omitempty"`
|
||||
SrcPort uint32 `json:"srcPort,omitempty"`
|
||||
}
|
||||
|
||||
type CheckRemoteService struct {
|
||||
@@ -523,6 +570,23 @@ type CheckRemoteService struct {
|
||||
Port uint32 `json:"port,omitempty"`
|
||||
}
|
||||
|
||||
type SpecTunnel struct {
|
||||
TunnelIndex uint32 `json:"tunnelIndex,omitempty"`
|
||||
}
|
||||
|
||||
type Nat4Detect struct {
|
||||
CustomData []*Nat4DetectItem
|
||||
Num int32 `json:"num,omitempty"`
|
||||
*Nat4DetectItem
|
||||
}
|
||||
|
||||
type Nat4DetectItem struct {
|
||||
Protocol string `json:"protocol,omitempty"`
|
||||
Server string `json:"server,omitempty"`
|
||||
ServerPort int32 `json:"serverPort,omitempty"`
|
||||
LocalPort int32 `json:"localPort,omitempty"`
|
||||
}
|
||||
|
||||
const rootCA = `-----BEGIN CERTIFICATE-----
|
||||
MIIDhTCCAm0CFHm0cd8dnGCbUW/OcS56jf0gvRk7MA0GCSqGSIb3DQEBCwUAMH4x
|
||||
CzAJBgNVBAYTAkNOMQswCQYDVQQIDAJHRDETMBEGA1UECgwKb3BlbnAycC5jbjET
|
||||
@@ -545,6 +609,31 @@ RVtXS+DplMClQ5QSlv3StwcWOsjyiAimNfLEU5xoEfq17yOJUTU1OTL4YOt16QUc
|
||||
C1tnzFr3k/ioqFR7cnyzNrbjlfPOmO9l2WReEbMP3bvaSHm6EcpJKS8=
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
const rootEdgeCA = `-----BEGIN CERTIFICATE-----
|
||||
MIID/zCCAuegAwIBAgIUI53UqyuJSa74NFIKherg5WTjtl4wDQYJKoZIhvcNAQEL
|
||||
BQAwgYYxCzAJBgNVBAYTAkNOMQswCQYDVQQIDAJHRDETMBEGA1UECgwKb3BlbnAy
|
||||
cC5jbjETMBEGA1UECwwKb3BlbnAycC5jbjEbMBkGA1UEAwwSb3BlbnAycC5jbiBS
|
||||
b290IENBMSMwIQYJKoZIhvcNAQkBFhRvcGVucDJwLmNuQGdtYWlsLmNvbTAeFw0y
|
||||
NTA5MDMwNTExMTBaFw0zNTA5MDEwNTExMTBaMIGGMQswCQYDVQQGEwJDTjELMAkG
|
||||
A1UECAwCR0QxEzARBgNVBAoMCm9wZW5wMnAuY24xEzARBgNVBAsMCm9wZW5wMnAu
|
||||
Y24xGzAZBgNVBAMMEm9wZW5wMnAuY24gUm9vdCBDQTEjMCEGCSqGSIb3DQEJARYU
|
||||
b3BlbnAycC5jbkBnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
|
||||
AoIBAQC/aHC0opWx1MFkXYI+Mm0CkMi7nB5XaD3K/DGGtA/kadhayFSWb6Y2+UWW
|
||||
s6OYBy7NmQRJgTedS4siQA6JEG4H3FBbz8URLt4TH/EP9+6QB0Z+P0arvUXNkl4k
|
||||
7cALmblaiqjq2M199+FWKhDWH2vMr1htY9Y3ldivLRMeH76diKgf8NvsX+wGR8bZ
|
||||
4MlJMFln0UeUYKIbekK7DmA5/9f2A/2Nrmi84PKGHU+0ZjB7gik/slW5zH0k7e+S
|
||||
wNtTuf8+6+t/LcJK9dWsS6f5+DOWmLcIWs6s/VMP9ODEzlY/hKMFk53+H+AjAZY/
|
||||
J/qhOxLXMNlNjdjwSEFPBY/vwVEnAgMBAAGjYzBhMB0GA1UdDgQWBBTXSSeIvz/R
|
||||
6A1pz0H4xBlV1Vu9kTAfBgNVHSMEGDAWgBTXSSeIvz/R6A1pz0H4xBlV1Vu9kTAP
|
||||
BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC
|
||||
AQEABqvvKwM+k2NfIFf9tzo1EsD4rQunyn6K5Zhf/kspb9++2Onw/lDlOErxSLLz
|
||||
C5aXn+B48honQeYEL/cYhH4duVQb0Zk71iF/PKDxYvF79Xbx9k7Kzg6RryaH8ZfQ
|
||||
pyEao+Uc6O895F+SLBog5aHIbz8gFNCRVaSAv3xpUIyQ/haxyHHapaLqt/ueNFVP
|
||||
qEG+9R41q55rEYb2ltINhumS3gb4qOcKI5pHuAw42pF8SShqaBIfFXSZ4u9ib7/k
|
||||
CvHN0kDYavV6NRiCSRF6wMxmaF70WpfqQhGdw0WyIzJfMOtSdvctjfNCoaWy2V2s
|
||||
nLaJXgiPehxIVGNC9dk/ZZzI2g==
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
const ISRGRootX1 = `-----BEGIN CERTIFICATE-----
|
||||
MIIEJjCCAw6gAwIBAgISAztStWq026ej0RCsk3ErbUdPMA0GCSqGSIb3DQEBCwUA
|
||||
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
|
||||
|
||||
158
core/sdwan.go
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -43,8 +44,8 @@ type sdwanNode struct {
|
||||
}
|
||||
|
||||
type p2pSDWAN struct {
|
||||
nodeName string
|
||||
tun *optun
|
||||
tunErr string
|
||||
sysRoute sync.Map // ip:sdwanNode
|
||||
subnet *net.IPNet
|
||||
gateway net.IP
|
||||
@@ -53,39 +54,54 @@ type p2pSDWAN struct {
|
||||
}
|
||||
|
||||
func (s *p2pSDWAN) reset() {
|
||||
gLog.Println(LvINFO, "reset sdwan when network disconnected")
|
||||
gLog.i("reset sdwan when network disconnected")
|
||||
// clear sysroute
|
||||
delRoutesByGateway(s.gateway.String())
|
||||
s.sysRoute.Range(func(key, value interface{}) bool {
|
||||
s.sysRoute.Delete(key)
|
||||
return true
|
||||
})
|
||||
// clear internel route
|
||||
s.internalRoute = NewIPTree("")
|
||||
// clear p2papp
|
||||
for _, node := range gConf.getAddNodes() {
|
||||
for _, node := range gConf.getSDWAN().Nodes {
|
||||
gConf.delete(AppConfig{SrcPort: 0, PeerNode: node.Name})
|
||||
GNetwork.DeleteApp(AppConfig{SrcPort: 0, PeerNode: node.Name})
|
||||
}
|
||||
|
||||
gConf.resetSDWAN()
|
||||
}
|
||||
func (s *p2pSDWAN) init(name string) error {
|
||||
|
||||
func (s *p2pSDWAN) init() error {
|
||||
gConf.Network.previousIP = gConf.Network.publicIP
|
||||
if gConf.getSDWAN().Gateway == "" {
|
||||
gLog.Println(LvDEBUG, "sdwan init: not in sdwan clear all ")
|
||||
gLog.d("sdwan init: not in sdwan clear all ")
|
||||
}
|
||||
if s.internalRoute == nil {
|
||||
s.internalRoute = NewIPTree("")
|
||||
}
|
||||
|
||||
s.nodeName = name
|
||||
if gw, sn, err := net.ParseCIDR(gConf.getSDWAN().Gateway); err == nil { // preserve old gateway
|
||||
s.gateway = gw
|
||||
s.subnet = sn
|
||||
}
|
||||
|
||||
for _, node := range gConf.getDelNodes() {
|
||||
gLog.Println(LvDEBUG, "sdwan init: deal deleted node: ", node.Name)
|
||||
gLog.Printf(LvDEBUG, "sdwan init: delRoute: %s, %s ", node.IP, s.gateway.String())
|
||||
delRoute(node.IP, s.gateway.String())
|
||||
gLog.d("sdwan init: deal deleted node: %s", node.Name)
|
||||
gLog.d("sdwan init: delRoute: %s, %s ", node.IP, s.gateway.String())
|
||||
// delRoute(node.IP, s.gateway.String()) // TODO: seems no need delelte each node
|
||||
s.internalRoute.Del(node.IP, node.IP)
|
||||
ipNum, _ := inetAtoN(node.IP)
|
||||
s.sysRoute.Delete(ipNum)
|
||||
// if node.Name == gConf.Network.Node {
|
||||
// // this is local node, need rm all client-side apps
|
||||
// GNetwork.apps.Range(func(id, i interface{}) bool {
|
||||
// app := i.(*p2pApp)
|
||||
// if app.config.is
|
||||
// return true
|
||||
// })
|
||||
// continue
|
||||
// }
|
||||
gConf.delete(AppConfig{SrcPort: 0, PeerNode: node.Name})
|
||||
GNetwork.DeleteApp(AppConfig{SrcPort: 0, PeerNode: node.Name})
|
||||
arr := strings.Split(node.Resource, ",")
|
||||
@@ -106,26 +122,26 @@ func (s *p2pSDWAN) init(name string) error {
|
||||
}
|
||||
s.internalRoute.Del(minIP.String(), maxIP.String())
|
||||
delRoute(ipnet.String(), s.gateway.String())
|
||||
gLog.Printf(LvDEBUG, "sdwan init: resource delRoute: %s, %s ", ipnet.String(), s.gateway.String())
|
||||
gLog.d("sdwan init: resource delRoute: %s, %s ", ipnet.String(), s.gateway.String())
|
||||
}
|
||||
}
|
||||
for _, node := range gConf.getAddNodes() {
|
||||
gLog.Println(LvDEBUG, "sdwan init: deal add node: ", node.Name)
|
||||
gLog.d("sdwan init: deal add node: %s", node.Name)
|
||||
ipNet := &net.IPNet{
|
||||
IP: net.ParseIP(node.IP),
|
||||
Mask: s.subnet.Mask,
|
||||
}
|
||||
if node.Name == s.nodeName {
|
||||
if node.Name == gConf.Network.Node {
|
||||
s.virtualIP = ipNet
|
||||
gLog.Println(LvINFO, "sdwan init: start tun ", ipNet.String())
|
||||
gLog.i("sdwan init: start tun %s", ipNet.String())
|
||||
err := s.StartTun()
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "sdwan init: start tun error:", err)
|
||||
gLog.e("sdwan init: start tun error:%s", err)
|
||||
return err
|
||||
}
|
||||
gLog.Println(LvINFO, "sdwan init: start tun ok")
|
||||
gLog.i("sdwan init: start tun ok")
|
||||
allowTunForward()
|
||||
gLog.Printf(LvDEBUG, "sdwan init: addRoute %s %s %s", s.subnet.String(), s.gateway.String(), s.tun.tunName)
|
||||
gLog.d("sdwan init: addRoute %s %s %s", s.subnet.String(), s.gateway.String(), s.tun.tunName)
|
||||
addRoute(s.subnet.String(), s.gateway.String(), s.tun.tunName)
|
||||
// addRoute("255.255.255.255/32", s.gateway.String(), s.tun.tunName) // for broadcast
|
||||
// addRoute("224.0.0.0/4", s.gateway.String(), s.tun.tunName) // for multicast
|
||||
@@ -140,11 +156,11 @@ func (s *p2pSDWAN) init(name string) error {
|
||||
s.internalRoute.AddIntIP(ip, ip, &sdwanNode{name: node.Name, id: NodeNameToID(node.Name)})
|
||||
}
|
||||
for _, node := range gConf.getAddNodes() {
|
||||
if node.Name == s.nodeName { // not deal resource itself
|
||||
if node.Name == gConf.Network.Node { // not deal resource itself
|
||||
continue
|
||||
}
|
||||
if len(node.Resource) > 0 {
|
||||
gLog.Printf(LvINFO, "sdwan init: deal add node: %s resource: %s", node.Name, node.Resource)
|
||||
gLog.i("sdwan init: deal add node: %s resource: %s", node.Name, node.Resource)
|
||||
arr := strings.Split(node.Resource, ",")
|
||||
for _, r := range arr {
|
||||
// add internal route
|
||||
@@ -154,17 +170,17 @@ func (s *p2pSDWAN) init(name string) error {
|
||||
continue
|
||||
}
|
||||
if ipnet.Contains(net.ParseIP(gConf.Network.localIP)) { // local ip and resource in the same lan
|
||||
gLog.Printf(LvDEBUG, "sdwan init: local ip %s in this resource %s, ignore", gConf.Network.localIP, ipnet.IP.String())
|
||||
gLog.d("sdwan init: local ip %s in this resource %s, ignore", gConf.Network.localIP, ipnet.IP.String())
|
||||
continue
|
||||
}
|
||||
// local net could access this single ip
|
||||
if ipnet.Mask[0] == 255 && ipnet.Mask[1] == 255 && ipnet.Mask[2] == 255 && ipnet.Mask[3] == 255 {
|
||||
gLog.Printf(LvDEBUG, "sdwan init: ping %s start", ipnet.IP.String())
|
||||
gLog.d("sdwan init: ping %s start", ipnet.IP.String())
|
||||
if _, err := Ping(ipnet.IP.String()); err == nil {
|
||||
gLog.Printf(LvDEBUG, "sdwan init: ping %s ok, ignore this resource", ipnet.IP.String())
|
||||
gLog.d("sdwan init: ping %s ok, ignore this resource", ipnet.IP.String())
|
||||
continue
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "sdwan init: ping %s failed", ipnet.IP.String())
|
||||
gLog.d("sdwan init: ping %s failed", ipnet.IP.String())
|
||||
}
|
||||
minIP := ipnet.IP
|
||||
maxIP := make(net.IP, len(minIP))
|
||||
@@ -174,13 +190,13 @@ func (s *p2pSDWAN) init(name string) error {
|
||||
}
|
||||
s.internalRoute.Add(minIP.String(), maxIP.String(), &sdwanNode{name: node.Name, id: NodeNameToID(node.Name)})
|
||||
// add sys route
|
||||
gLog.Printf(LvDEBUG, "sdwan init: addRoute %s %s %s", ipnet.String(), s.gateway.String(), s.tun.tunName)
|
||||
gLog.d("sdwan init: addRoute %s %s %s", ipnet.String(), s.gateway.String(), s.tun.tunName)
|
||||
addRoute(ipnet.String(), s.gateway.String(), s.tun.tunName)
|
||||
}
|
||||
}
|
||||
}
|
||||
gConf.retryAllMemApp()
|
||||
gLog.Printf(LvINFO, "sdwan init ok")
|
||||
gLog.i("sdwan init ok")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -193,28 +209,28 @@ func (s *p2pSDWAN) run() {
|
||||
}
|
||||
|
||||
func (s *p2pSDWAN) readNodeLoop() {
|
||||
gLog.Printf(LvDEBUG, "sdwan readNodeLoop start")
|
||||
defer gLog.Printf(LvDEBUG, "sdwan readNodeLoop end")
|
||||
gLog.d("sdwan readNodeLoop start")
|
||||
defer gLog.d("sdwan readNodeLoop end")
|
||||
writeBuff := make([][]byte, 1)
|
||||
for {
|
||||
nd := GNetwork.ReadNode(time.Second * 10) // TODO: read multi packet
|
||||
if nd == nil {
|
||||
gLog.Printf(LvDev, "waiting for node data")
|
||||
gLog.dev("waiting for node data")
|
||||
continue
|
||||
}
|
||||
head := PacketHeader{}
|
||||
parseHeader(nd.Data, &head)
|
||||
gLog.Printf(LvDev, "write tun dst ip=%s,len=%d", net.IP{byte(head.dst >> 24), byte(head.dst >> 16), byte(head.dst >> 8), byte(head.dst)}.String(), len(nd.Data))
|
||||
parseHeader(nd, &head)
|
||||
gLog.dev("write tun dst ip=%s,len=%d", net.IP{byte(head.dst >> 24), byte(head.dst >> 16), byte(head.dst >> 8), byte(head.dst)}.String(), len(nd))
|
||||
if PIHeaderSize == 0 {
|
||||
writeBuff[0] = nd.Data
|
||||
writeBuff[0] = nd
|
||||
} else {
|
||||
writeBuff[0] = make([]byte, PIHeaderSize+len(nd.Data))
|
||||
copy(writeBuff[0][PIHeaderSize:], nd.Data)
|
||||
writeBuff[0] = make([]byte, PIHeaderSize+len(nd))
|
||||
copy(writeBuff[0][PIHeaderSize:], nd)
|
||||
}
|
||||
|
||||
len, err := s.tun.Write(writeBuff, PIHeaderSize)
|
||||
if err != nil {
|
||||
gLog.Printf(LvDEBUG, "write tun dst ip=%s,len=%d,error:%s", net.IP{byte(head.dst >> 24), byte(head.dst >> 16), byte(head.dst >> 8), byte(head.dst)}.String(), len, err)
|
||||
gLog.d("write tun dst ip=%s,len=%d,error:%s", net.IP{byte(head.dst >> 24), byte(head.dst >> 16), byte(head.dst >> 8), byte(head.dst)}.String(), len, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,9 +246,11 @@ func (s *p2pSDWAN) routeTunPacket(p []byte, head *PacketHeader) {
|
||||
v, ok := s.internalRoute.Load(head.dst)
|
||||
if !ok || v == nil {
|
||||
if isBroadcastOrMulticast(head.dst, s.subnet) {
|
||||
gLog.Printf(LvDev, "multicast ip=%s", net.IP{byte(head.dst >> 24), byte(head.dst >> 16), byte(head.dst >> 8), byte(head.dst)}.String())
|
||||
gLog.dev("multicast ip=%s", net.IP{byte(head.dst >> 24), byte(head.dst >> 16), byte(head.dst >> 8), byte(head.dst)}.String())
|
||||
GNetwork.WriteBroadcast(p)
|
||||
return
|
||||
}
|
||||
gLog.dev("internalRoute not found ip:%s", net.IP{byte(head.dst >> 24), byte(head.dst >> 16), byte(head.dst >> 8), byte(head.dst)}.String())
|
||||
return
|
||||
} else {
|
||||
node = v.(*sdwanNode)
|
||||
@@ -240,13 +258,13 @@ func (s *p2pSDWAN) routeTunPacket(p []byte, head *PacketHeader) {
|
||||
|
||||
err := GNetwork.WriteNode(node.id, p)
|
||||
if err != nil {
|
||||
gLog.Printf(LvDev, "write packet to %s fail: %s", node.name, err)
|
||||
gLog.dev("write packet to %s fail: %s", node.name, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *p2pSDWAN) readTunLoop() {
|
||||
gLog.Printf(LvDEBUG, "sdwan readTunLoop start")
|
||||
defer gLog.Printf(LvDEBUG, "sdwan readTunLoop end")
|
||||
gLog.d("sdwan readTunLoop start")
|
||||
defer gLog.d("sdwan readTunLoop end")
|
||||
readBuff := make([][]byte, ReadTunBuffNum)
|
||||
for i := 0; i < ReadTunBuffNum; i++ {
|
||||
readBuff[i] = make([]byte, ReadTunBuffSize+PIHeaderSize)
|
||||
@@ -256,16 +274,16 @@ func (s *p2pSDWAN) readTunLoop() {
|
||||
for {
|
||||
n, err := s.tun.Read(readBuff, readBuffSize, PIHeaderSize)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "read tun fail: ", err)
|
||||
gLog.e("read tun fail: %s", err)
|
||||
return
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
if readBuffSize[i] > ReadTunBuffSize {
|
||||
gLog.Printf(LvERROR, "read tun overflow: len=", readBuffSize[i])
|
||||
gLog.e("read tun overflow: len=%d", readBuffSize[i])
|
||||
continue
|
||||
}
|
||||
parseHeader(readBuff[i][PIHeaderSize:readBuffSize[i]+PIHeaderSize], &ih)
|
||||
gLog.Printf(LvDev, "read tun dst ip=%s,len=%d", net.IP{byte(ih.dst >> 24), byte(ih.dst >> 16), byte(ih.dst >> 8), byte(ih.dst)}.String(), readBuffSize[0])
|
||||
gLog.dev("read tun dst ip=%s,len=%d", net.IP{byte(ih.dst >> 24), byte(ih.dst >> 16), byte(ih.dst >> 8), byte(ih.dst)}.String(), readBuffSize[0])
|
||||
s.routeTunPacket(readBuff[i][PIHeaderSize:readBuffSize[i]+PIHeaderSize], &ih)
|
||||
}
|
||||
}
|
||||
@@ -277,23 +295,25 @@ func (s *p2pSDWAN) StartTun() error {
|
||||
tun := &optun{}
|
||||
err := tun.Start(s.virtualIP.String(), &sdwan)
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "open tun fail:", err)
|
||||
gLog.e("open tun fail:%v", err)
|
||||
s.tunErr = err.Error()
|
||||
return err
|
||||
}
|
||||
s.tun = tun
|
||||
s.tunErr = ""
|
||||
go s.readTunLoop()
|
||||
go s.readNodeLoop() // multi-thread read will cause packets out of order, resulting in slower speeds
|
||||
}
|
||||
err := setTunAddr(s.tun.tunName, s.virtualIP.String(), sdwan.Gateway, s.tun.dev)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "setTunAddr error:%s,%s,%s,%s", err, s.tun.tunName, s.virtualIP.String(), sdwan.Gateway)
|
||||
gLog.e("setTunAddr error:%s,%s,%s,%s", err, s.tun.tunName, s.virtualIP.String(), sdwan.Gateway)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleSDWAN(subType uint16, msg []byte) error {
|
||||
gLog.Printf(LvDEBUG, "handle sdwan msg type:%d", subType)
|
||||
gLog.d("handle sdwan msg type:%d", subType)
|
||||
var err error
|
||||
switch subType {
|
||||
case MsgSDWANInfoRsp:
|
||||
@@ -301,15 +321,25 @@ func handleSDWAN(subType uint16, msg []byte) error {
|
||||
if err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp); err != nil {
|
||||
return ErrMsgFormat
|
||||
}
|
||||
gLog.Println(LvINFO, "sdwan init:", prettyJson(rsp))
|
||||
if runtime.GOOS == "android" {
|
||||
AndroidSDWANConfig <- msg[openP2PHeaderSize:]
|
||||
}
|
||||
gLog.i("sdwan init:%s", prettyJson(rsp))
|
||||
// GNetwork.sdwan.detail = &rsp
|
||||
if gConf.Network.previousIP != gConf.Network.publicIP || gConf.getSDWAN().CentralNode != rsp.CentralNode || gConf.getSDWAN().Gateway != rsp.Gateway {
|
||||
GNetwork.sdwan.reset()
|
||||
preAndroidSDWANConfig = "" // let androind app reset vpnservice
|
||||
}
|
||||
gConf.setSDWAN(rsp)
|
||||
err = GNetwork.sdwan.init(gConf.Network.Node)
|
||||
if runtime.GOOS == "android" {
|
||||
if !compareResources(preAndroidSDWANConfig, string(msg[openP2PHeaderSize:])) { // when config change, notify android app
|
||||
select {
|
||||
case AndroidSDWANConfig <- msg[openP2PHeaderSize:]:
|
||||
default:
|
||||
}
|
||||
preAndroidSDWANConfig = string(msg[openP2PHeaderSize:])
|
||||
}
|
||||
}
|
||||
err = GNetwork.sdwan.init()
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "sdwan init fail: ", err)
|
||||
gLog.e("sdwan init fail: %s", err)
|
||||
if GNetwork.sdwan.tun != nil {
|
||||
GNetwork.sdwan.tun.Stop()
|
||||
GNetwork.sdwan.tun = nil
|
||||
@@ -321,3 +351,33 @@ func handleSDWAN(subType uint16, msg []byte) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// for android vpnservice
|
||||
func compareResources(json1, json2 string) bool {
|
||||
var net1, net2 SDWANInfo
|
||||
if err := json.Unmarshal([]byte(json1), &net1); err != nil {
|
||||
fmt.Println("Error parsing json1:", err)
|
||||
fmt.Println("Error parsing json1:", string(json1))
|
||||
return false
|
||||
}
|
||||
if err := json.Unmarshal([]byte(json2), &net2); err != nil {
|
||||
fmt.Println("Error parsing json2:", err)
|
||||
fmt.Println("Error parsing json1:", string(json2))
|
||||
return false
|
||||
}
|
||||
|
||||
// 获取所有资源并比较
|
||||
resources1 := getResources(net1)
|
||||
resources2 := getResources(net2)
|
||||
return reflect.DeepEqual(resources1, resources2)
|
||||
}
|
||||
|
||||
func getResources(network SDWANInfo) []string {
|
||||
var resources []string
|
||||
for _, node := range network.Nodes {
|
||||
if node.Resource != "" {
|
||||
resources = append(resources, node.Resource)
|
||||
}
|
||||
}
|
||||
return resources
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package openp2p
|
||||
|
||||
import (
|
||||
"log"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -15,7 +16,7 @@ func TestBandwidth(t *testing.T) {
|
||||
for i := 0; i < writeNum; i++ {
|
||||
speedl.Add(oneBuffSize, true)
|
||||
}
|
||||
t.Logf("cost %ds, expect %ds", time.Since(startTs)/time.Second, expectTime)
|
||||
log.Printf("cost %ds, expect %ds", time.Since(startTs)/time.Second, expectTime)
|
||||
if time.Since(startTs) > time.Duration(expectTime+1)*time.Second || time.Since(startTs) < time.Duration(expectTime-1)*time.Second {
|
||||
t.Error("error")
|
||||
}
|
||||
@@ -27,12 +28,12 @@ func TestSymmetric(t *testing.T) {
|
||||
oneBuffSize := 300
|
||||
writeNum := 70
|
||||
expectTime := (oneBuffSize*writeNum - 20000) / speed
|
||||
t.Logf("expect %ds", expectTime)
|
||||
log.Printf("expect %ds", expectTime)
|
||||
startTs := time.Now()
|
||||
for i := 0; i < writeNum; i++ {
|
||||
speedl.Add(oneBuffSize, true)
|
||||
}
|
||||
t.Logf("cost %ds, expect %ds", time.Since(startTs)/time.Second, expectTime)
|
||||
log.Printf("cost %ds, expect %ds", time.Since(startTs)/time.Second, expectTime)
|
||||
if time.Since(startTs) > time.Duration(expectTime+1)*time.Second || time.Since(startTs) < time.Duration(expectTime-1)*time.Second {
|
||||
t.Error("error")
|
||||
}
|
||||
@@ -44,6 +45,7 @@ func TestSymmetric2(t *testing.T) {
|
||||
oneBuffSize := 800
|
||||
writeNum := 40
|
||||
expectTime := (oneBuffSize*writeNum - 30000) / speed
|
||||
log.Printf("expect %ds", expectTime)
|
||||
startTs := time.Now()
|
||||
for i := 0; i < writeNum; {
|
||||
if speedl.Add(oneBuffSize, true) {
|
||||
@@ -52,7 +54,7 @@ func TestSymmetric2(t *testing.T) {
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
}
|
||||
t.Logf("cost %ds, expect %ds", time.Since(startTs)/time.Second, expectTime)
|
||||
log.Printf("cost %ds, expect %ds", time.Since(startTs)/time.Second, expectTime)
|
||||
if time.Since(startTs) > time.Duration(expectTime+1)*time.Second || time.Since(startTs) < time.Duration(expectTime-1)*time.Second {
|
||||
t.Error("error")
|
||||
}
|
||||
|
||||
10
core/udp.go
@@ -22,7 +22,7 @@ func UDPRead(conn *net.UDPConn, timeout time.Duration) (ra net.Addr, head *openP
|
||||
if timeout > 0 {
|
||||
err = conn.SetReadDeadline(time.Now().Add(timeout))
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "SetReadDeadline error")
|
||||
gLog.e("SetReadDeadline error")
|
||||
return nil, nil, nil, 0, err
|
||||
}
|
||||
}
|
||||
@@ -35,9 +35,13 @@ func UDPRead(conn *net.UDPConn, timeout time.Duration) (ra net.Addr, head *openP
|
||||
}
|
||||
head = &openP2PHeader{}
|
||||
err = binary.Read(bytes.NewReader(buff[:openP2PHeaderSize]), binary.LittleEndian, head)
|
||||
if err != nil || head.DataLen > uint32(len(buff)-openP2PHeaderSize) {
|
||||
gLog.Println(LvERROR, "parse p2pheader error:", err)
|
||||
if err != nil {
|
||||
gLog.e("parse p2pheader error:%s", err)
|
||||
return nil, nil, nil, 0, err
|
||||
}
|
||||
if head.DataLen > uint32(len(buff)-openP2PHeaderSize) {
|
||||
gLog.e("parse p2pheader error:%d", ErrHeaderDataLen)
|
||||
return nil, nil, nil, 0, ErrHeaderDataLen
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package openp2p
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -18,6 +19,7 @@ type underlay interface {
|
||||
SetReadDeadline(t time.Time) error
|
||||
SetWriteDeadline(t time.Time) error
|
||||
Protocol() string
|
||||
RemoteAddr() net.Addr
|
||||
}
|
||||
|
||||
func DefaultReadBuffer(ul underlay) (*openP2PHeader, []byte, error) {
|
||||
@@ -28,6 +30,7 @@ func DefaultReadBuffer(ul underlay) (*openP2PHeader, []byte, error) {
|
||||
}
|
||||
head, err := decodeHeader(headBuf)
|
||||
if err != nil || head.MainType > 16 {
|
||||
gLog.d("DefaultReadBuffer error:%v, %d", err, head.MainType)
|
||||
return nil, nil, err
|
||||
}
|
||||
dataBuf := make([]byte, head.DataLen)
|
||||
@@ -39,7 +42,7 @@ func DefaultWriteBytes(ul underlay, mainType, subType uint16, data []byte) error
|
||||
writeBytes := append(encodeHeader(mainType, subType, uint32(len(data))), data...)
|
||||
ul.SetWriteDeadline(time.Now().Add(TunnelHeartbeatTime / 2))
|
||||
ul.WLock()
|
||||
_, err := ul.Write(writeBytes)
|
||||
err := writeFull(ul, writeBytes)
|
||||
ul.WUnlock()
|
||||
return err
|
||||
}
|
||||
@@ -47,7 +50,7 @@ func DefaultWriteBytes(ul underlay, mainType, subType uint16, data []byte) error
|
||||
func DefaultWriteBuffer(ul underlay, data []byte) error {
|
||||
ul.SetWriteDeadline(time.Now().Add(TunnelHeartbeatTime / 2))
|
||||
ul.WLock()
|
||||
_, err := ul.Write(data)
|
||||
err := writeFull(ul, data)
|
||||
ul.WUnlock()
|
||||
return err
|
||||
}
|
||||
@@ -59,7 +62,7 @@ func DefaultWriteMessage(ul underlay, mainType uint16, subType uint16, packet in
|
||||
}
|
||||
ul.SetWriteDeadline(time.Now().Add(TunnelHeartbeatTime / 2))
|
||||
ul.WLock()
|
||||
_, err = ul.Write(writeBytes)
|
||||
err = writeFull(ul, writeBytes)
|
||||
ul.WUnlock()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ func (conn *underlayKCP) Accept() error {
|
||||
}
|
||||
|
||||
func listenKCP(addr string, idleTimeout time.Duration) (*underlayKCP, error) {
|
||||
gLog.Println(LvDEBUG, "kcp listen on ", addr)
|
||||
gLog.d("kcp listen on %s", addr)
|
||||
listener, err := kcp.ListenWithOptions(addr, nil, 0, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("quic.ListenAddr error:%s", err)
|
||||
@@ -81,6 +81,7 @@ func listenKCP(addr string, idleTimeout time.Duration) (*underlayKCP, error) {
|
||||
}
|
||||
|
||||
func dialKCP(conn *net.UDPConn, remoteAddr *net.UDPAddr, idleTimeout time.Duration) (*underlayKCP, error) {
|
||||
conn.SetDeadline(time.Now().Add(idleTimeout))
|
||||
kConn, err := kcp.NewConn(remoteAddr.String(), nil, 0, 0, conn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("quic.DialContext error:%s", err)
|
||||
@@ -81,7 +81,7 @@ func (conn *underlayQUIC) Accept() error {
|
||||
}
|
||||
|
||||
func listenQuic(addr string, idleTimeout time.Duration) (*underlayQUIC, error) {
|
||||
gLog.Println(LvDEBUG, "quic listen on ", addr)
|
||||
gLog.d("quic listen on %s", addr)
|
||||
listener, err := quic.ListenAddr(addr, generateTLSConfig(),
|
||||
&quic.Config{Versions: quicVersion, MaxIdleTimeout: idleTimeout, DisablePathMTUDiscovery: true})
|
||||
if err != nil {
|
||||
@@ -96,13 +96,15 @@ func listenQuic(addr string, idleTimeout time.Duration) (*underlayQUIC, error) {
|
||||
return ul, nil
|
||||
}
|
||||
|
||||
func dialQuic(conn *net.UDPConn, remoteAddr *net.UDPAddr, idleTimeout time.Duration) (*underlayQUIC, error) {
|
||||
func dialQuic(conn *net.UDPConn, remoteAddr *net.UDPAddr, timeout time.Duration) (*underlayQUIC, error) {
|
||||
tlsConf := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
NextProtos: []string{"openp2pv1"},
|
||||
}
|
||||
Connection, err := quic.DialContext(context.Background(), conn, remoteAddr, conn.LocalAddr().String(), tlsConf,
|
||||
&quic.Config{Versions: quicVersion, MaxIdleTimeout: idleTimeout, DisablePathMTUDiscovery: true})
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
Connection, err := quic.DialContext(ctx, conn, remoteAddr, conn.LocalAddr().String(), tlsConf,
|
||||
&quic.Config{Versions: quicVersion, MaxIdleTimeout: TunnelIdleTimeout, DisablePathMTUDiscovery: true})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("quic.DialContext error:%s", err)
|
||||
}
|
||||
|
||||
@@ -46,19 +46,25 @@ func (conn *underlayTCP) WUnlock() {
|
||||
conn.writeMtx.Unlock()
|
||||
}
|
||||
|
||||
func listenTCP(host string, port int, localPort int, mode string, t *P2PTunnel) (*underlayTCP, error) {
|
||||
if mode == LinkModeTCPPunch {
|
||||
func listenTCP(host string, port int, localPort int, mode string, t *P2PTunnel) (underlay, error) {
|
||||
if mode == LinkModeTCPPunch || mode == LinkModeTCP6 {
|
||||
if compareVersion(t.config.peerVersion, SyncServerTimeVersion) < 0 {
|
||||
gLog.Printf(LvDEBUG, "peer version %s less than %s", t.config.peerVersion, SyncServerTimeVersion)
|
||||
gLog.d("peer version %s less than %s", t.config.peerVersion, SyncServerTimeVersion)
|
||||
} else {
|
||||
ts := time.Duration(int64(t.punchTs) + GNetwork.dt - time.Now().UnixNano())
|
||||
gLog.Printf(LvDEBUG, "sleep %d ms", ts/time.Millisecond)
|
||||
gLog.d("sleep %d ms", ts/time.Millisecond)
|
||||
time.Sleep(ts)
|
||||
}
|
||||
gLog.Println(LvDEBUG, " send tcp punch: ", fmt.Sprintf("0.0.0.0:%d", localPort), "-->", fmt.Sprintf("%s:%d", host, port))
|
||||
c, err := reuse.DialTimeout("tcp", fmt.Sprintf("0.0.0.0:%d", localPort), fmt.Sprintf("%s:%d", host, port), CheckActiveTimeout)
|
||||
// gLog.d(" send tcp punch: ", fmt.Sprintf("0.0.0.0:%d", localPort), "-->", fmt.Sprintf("%s:%d", host, port))
|
||||
var c net.Conn
|
||||
var err error
|
||||
if mode == LinkModeTCPPunch {
|
||||
c, err = reuse.DialTimeout("tcp", fmt.Sprintf("0.0.0.0:%d", localPort), fmt.Sprintf("%s:%d", host, port), CheckActiveTimeout)
|
||||
} else {
|
||||
c, err = reuse.DialTimeout("tcp6", fmt.Sprintf("[::]:%d", localPort), fmt.Sprintf("[%s]:%d", t.config.peerIPv6, port), CheckActiveTimeout)
|
||||
}
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "send tcp punch: ", err)
|
||||
// gLog.d("send tcp punch: ", err)
|
||||
return nil, err
|
||||
}
|
||||
utcp := &underlayTCP{writeMtx: &sync.Mutex{}, Conn: c}
|
||||
@@ -67,7 +73,7 @@ func listenTCP(host string, port int, localPort int, mode string, t *P2PTunnel)
|
||||
return nil, fmt.Errorf("read start msg error:%s", err)
|
||||
}
|
||||
if buff != nil {
|
||||
gLog.Println(LvDEBUG, string(buff))
|
||||
gLog.d("handshake flag:%s", string(buff))
|
||||
}
|
||||
utcp.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, buff)
|
||||
return utcp, nil
|
||||
@@ -77,59 +83,46 @@ func listenTCP(host string, port int, localPort int, mode string, t *P2PTunnel)
|
||||
if compareVersion(t.config.peerVersion, PublicIPVersion) < 0 { // old version
|
||||
ipBytes := net.ParseIP(t.config.peerIP).To4()
|
||||
tid = uint64(binary.BigEndian.Uint32(ipBytes))
|
||||
gLog.Println(LvDEBUG, "compatible with old client, use ip as key:", tid)
|
||||
gLog.d("compatible with old client, use ip as key:%d", tid)
|
||||
}
|
||||
var utcp *underlayTCP
|
||||
if mode == LinkModeIntranet && gConf.Network.hasIPv4 == 0 && gConf.Network.hasUPNPorNATPMP == 0 {
|
||||
addr, _ := net.ResolveTCPAddr("tcp4", fmt.Sprintf("0.0.0.0:%d", localPort))
|
||||
l, err := net.ListenTCP("tcp4", addr)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "listen %d error:", localPort, err)
|
||||
return nil, err
|
||||
}
|
||||
defer l.Close()
|
||||
err = l.SetDeadline(time.Now().Add(UnderlayTCPConnectTimeout))
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "set listen timeout:", err)
|
||||
return nil, err
|
||||
}
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
utcp = &underlayTCP{writeMtx: &sync.Mutex{}, Conn: c}
|
||||
} else {
|
||||
if v4l != nil {
|
||||
utcp = v4l.getUnderlayTCP(tid)
|
||||
}
|
||||
var ul underlay
|
||||
if v4l != nil {
|
||||
ul = v4l.getUnderlay(tid)
|
||||
}
|
||||
|
||||
if utcp == nil {
|
||||
if ul == nil {
|
||||
return nil, ErrConnectPublicV4
|
||||
}
|
||||
return utcp, nil
|
||||
return ul, nil
|
||||
}
|
||||
|
||||
func dialTCP(host string, port int, localPort int, mode string) (*underlayTCP, error) {
|
||||
var c net.Conn
|
||||
var err error
|
||||
if mode == LinkModeTCPPunch {
|
||||
gLog.Println(LvDev, " send tcp punch: ", fmt.Sprintf("0.0.0.0:%d", localPort), "-->", fmt.Sprintf("%s:%d", host, port))
|
||||
if c, err = reuse.DialTimeout("tcp", fmt.Sprintf("0.0.0.0:%d", localPort), fmt.Sprintf("%s:%d", host, port), CheckActiveTimeout); err != nil {
|
||||
gLog.Println(LvDev, "send tcp punch: ", err)
|
||||
}
|
||||
network := "tcp"
|
||||
localAddr := fmt.Sprintf("0.0.0.0:%d", localPort)
|
||||
remoteAddr := fmt.Sprintf("%s:%d", host, port)
|
||||
if mode == LinkModeTCP6 { // address need [ip]
|
||||
network = "tcp6"
|
||||
localAddr = fmt.Sprintf("[::]:%d", localPort)
|
||||
remoteAddr = fmt.Sprintf("[%s]:%d", host, port)
|
||||
}
|
||||
if mode == LinkModeTCP4 || mode == LinkModeIntranet { // random port
|
||||
localAddr = fmt.Sprintf("0.0.0.0:%d", 0)
|
||||
}
|
||||
gLog.dev("send tcp punch: %s --> %s", localAddr, remoteAddr)
|
||||
|
||||
} else {
|
||||
c, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), CheckActiveTimeout)
|
||||
c, err = reuse.DialTimeout(network, localAddr, remoteAddr, CheckActiveTimeout)
|
||||
if err != nil {
|
||||
gLog.dev("send tcp punch: %v", err)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
gLog.Printf(LvDev, "Dial %s:%d error:%s", host, port, err)
|
||||
gLog.dev("Dial %s:%d error:%s", host, port, err)
|
||||
return nil, err
|
||||
}
|
||||
tc := c.(*net.TCPConn)
|
||||
tc.SetKeepAlive(true)
|
||||
tc.SetKeepAlivePeriod(UnderlayTCPKeepalive)
|
||||
gLog.Printf(LvDEBUG, "Dial %s:%d OK", host, port)
|
||||
gLog.d("Dial %s:%d OK", host, port)
|
||||
return &underlayTCP{writeMtx: &sync.Mutex{}, Conn: c}, nil
|
||||
}
|
||||
|
||||
17
core/underlay_tcp_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package openp2p
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDialTCP(t *testing.T) {
|
||||
InitForUnitTest(LvDEBUG)
|
||||
// ul, err := dialTCP("[240e:3b1:6f6:d14:1c0b:9605:554d:351c]", 3389, 0, LinkModeTCP6)
|
||||
// if err != nil || ul == nil {
|
||||
// t.Error("dialTCP error:", err)
|
||||
// }
|
||||
ul, err := dialTCP("192.168.3.9", 3389, 0, LinkModeTCP6)
|
||||
if err != nil || ul == nil {
|
||||
t.Error("dialTCP error:", err)
|
||||
}
|
||||
}
|
||||
107
core/update.go
@@ -9,7 +9,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -19,21 +18,21 @@ import (
|
||||
)
|
||||
|
||||
func update(host string, port int) error {
|
||||
gLog.Println(LvINFO, "update start")
|
||||
defer gLog.Println(LvINFO, "update end")
|
||||
gLog.i("update start")
|
||||
defer gLog.i("update end")
|
||||
caCertPool, err := x509.SystemCertPool()
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "Failed to load system root CAs:", err)
|
||||
} else {
|
||||
gLog.e("Failed to load system root CAs:%s", err)
|
||||
caCertPool = x509.NewCertPool()
|
||||
}
|
||||
caCertPool.AppendCertsFromPEM([]byte(rootCA))
|
||||
caCertPool.AppendCertsFromPEM([]byte(rootEdgeCA))
|
||||
caCertPool.AppendCertsFromPEM([]byte(ISRGRootX1))
|
||||
|
||||
c := http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{RootCAs: caCertPool,
|
||||
InsecureSkipVerify: false},
|
||||
InsecureSkipVerify: gConf.TLSInsecureSkipVerify},
|
||||
},
|
||||
Timeout: time.Second * 30,
|
||||
}
|
||||
@@ -41,32 +40,36 @@ func update(host string, port int) error {
|
||||
goarch := runtime.GOARCH
|
||||
rsp, err := c.Get(fmt.Sprintf("https://%s:%d/api/v1/update?fromver=%s&os=%s&arch=%s&user=%s&node=%s", host, port, OpenP2PVersion, goos, goarch, url.QueryEscape(gConf.Network.User), url.QueryEscape(gConf.Network.Node)))
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "update:query update list failed:", err)
|
||||
gLog.e("update:query update list failed:%s", err)
|
||||
return err
|
||||
}
|
||||
defer rsp.Body.Close()
|
||||
if rsp.StatusCode != http.StatusOK {
|
||||
gLog.Println(LvERROR, "get update info error:", rsp.Status)
|
||||
gLog.e("get update info error:%s", rsp.Status)
|
||||
return err
|
||||
}
|
||||
rspBuf, err := ioutil.ReadAll(rsp.Body)
|
||||
rspBuf, err := io.ReadAll(rsp.Body)
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "update:read update list failed:", err)
|
||||
gLog.e("update:read update list failed:%s", err)
|
||||
return err
|
||||
}
|
||||
updateInfo := UpdateInfo{}
|
||||
if err = json.Unmarshal(rspBuf, &updateInfo); err != nil {
|
||||
gLog.Println(LvERROR, rspBuf, " update info decode error:", err)
|
||||
gLog.e("%s update info decode error:%s", string(rspBuf), err)
|
||||
return err
|
||||
}
|
||||
if updateInfo.Error != 0 {
|
||||
gLog.Println(LvERROR, "update error:", updateInfo.Error, updateInfo.ErrorDetail)
|
||||
gLog.e("update error:%d,%s", updateInfo.Error, updateInfo.ErrorDetail)
|
||||
return err
|
||||
}
|
||||
err = updateFile(updateInfo.Url, "", "openp2p")
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "update: download failed:", err)
|
||||
return err
|
||||
gLog.e("update: download failed:%s, retry...", err)
|
||||
err = updateFile(updateInfo.Url2, "", "openp2p")
|
||||
if err != nil {
|
||||
gLog.e("update: download failed:%s", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -74,66 +77,80 @@ func update(host string, port int) error {
|
||||
func downloadFile(url string, checksum string, dstFile string) error {
|
||||
output, err := os.OpenFile(dstFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0776)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "OpenFile %s error:%s", dstFile, err)
|
||||
gLog.e("OpenFile %s error:%s", dstFile, err)
|
||||
return err
|
||||
}
|
||||
caCertPool, err := x509.SystemCertPool()
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "Failed to load system root CAs:", err)
|
||||
} else {
|
||||
gLog.e("Failed to load system root CAs:%s", err)
|
||||
caCertPool = x509.NewCertPool()
|
||||
}
|
||||
caCertPool.AppendCertsFromPEM([]byte(rootCA))
|
||||
caCertPool.AppendCertsFromPEM([]byte(rootEdgeCA))
|
||||
caCertPool.AppendCertsFromPEM([]byte(ISRGRootX1))
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: caCertPool,
|
||||
InsecureSkipVerify: false},
|
||||
InsecureSkipVerify: gConf.TLSInsecureSkipVerify},
|
||||
}
|
||||
client := &http.Client{Transport: tr}
|
||||
client := &http.Client{Transport: tr,
|
||||
Timeout: 60 * time.Second}
|
||||
response, err := client.Get(url)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "download url %s error:%s", url, err)
|
||||
gLog.e("download url %s error:%s", url, err)
|
||||
output.Close()
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
n, err := io.Copy(output, response.Body)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "io.Copy error:%s", err)
|
||||
gLog.e("io.Copy error:%s", err)
|
||||
output.Close()
|
||||
return err
|
||||
}
|
||||
output.Sync()
|
||||
output.Close()
|
||||
gLog.Println(LvINFO, "download ", url, " ok")
|
||||
gLog.Printf(LvINFO, "size: %d bytes", n)
|
||||
gLog.i("download %s ok", url)
|
||||
gLog.i("size: %d bytes", n)
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateFile(url string, checksum string, dst string) error {
|
||||
gLog.Println(LvINFO, "download ", url)
|
||||
tmpFile := filepath.Dir(os.Args[0]) + "/openp2p.tmp"
|
||||
gLog.i("download %s", url)
|
||||
tempDir := os.TempDir()
|
||||
tmpFile := filepath.Join(tempDir, "openp2p.tmp")
|
||||
err := downloadFile(url, checksum, tmpFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
backupFile := os.Args[0] + "0"
|
||||
err = os.Rename(os.Args[0], backupFile) // the old daemon process was using the 0 file, so it will prevent override it
|
||||
backupBase := filepath.Base(os.Args[0])
|
||||
var backupFile string
|
||||
if runtime.GOOS == "windows" {
|
||||
backupFile = filepath.Join(tempDir, backupBase+"0")
|
||||
} else {
|
||||
backupFile = os.Args[0] + "0" // linux can not mv running executable to /tmp, because they are different volumns
|
||||
}
|
||||
gLog.i("backup file %s --> %s", os.Args[0], backupFile)
|
||||
err = moveFile(os.Args[0], backupFile)
|
||||
if err != nil {
|
||||
gLog.Printf(LvINFO, " rename %s error:%s, retry 1", os.Args[0], err)
|
||||
backupFile = os.Args[0] + "1"
|
||||
err = os.Rename(os.Args[0], backupFile)
|
||||
if runtime.GOOS == "windows" {
|
||||
backupFile = filepath.Join(tempDir, backupBase+"1")
|
||||
} else {
|
||||
backupFile = os.Args[0] + "1" // 1st update will mv deamon process to 0, 2nd update mv to 0 will failed, mv to 1
|
||||
}
|
||||
gLog.i("backup file %s --> %s", os.Args[0], backupFile)
|
||||
err = moveFile(os.Args[0], backupFile)
|
||||
if err != nil {
|
||||
gLog.Printf(LvINFO, " rename %s error:%s", os.Args[0], err)
|
||||
gLog.e(" rename %s error:%s", os.Args[0], err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
// extract
|
||||
gLog.Println(LvINFO, "extract files")
|
||||
gLog.i("extract files")
|
||||
err = extract(filepath.Dir(os.Args[0]), tmpFile)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "extract error:%s. revert rename", err)
|
||||
os.Rename(backupFile, os.Args[0])
|
||||
gLog.e("extract error:%s. revert rename", err)
|
||||
moveFile(backupFile, os.Args[0])
|
||||
return err
|
||||
}
|
||||
os.Remove(tmpFile)
|
||||
@@ -224,16 +241,20 @@ func extractTgz(dst, src string) error {
|
||||
}
|
||||
|
||||
func cleanTempFiles() {
|
||||
tmpFile := os.Args[0] + "0"
|
||||
if _, err := os.Stat(tmpFile); err == nil {
|
||||
if err := os.Remove(tmpFile); err != nil {
|
||||
gLog.Printf(LvDEBUG, " remove %s error:%s", tmpFile, err)
|
||||
tempDir := os.TempDir()
|
||||
backupBase := filepath.Base(os.Args[0])
|
||||
for i := 0; i < 2; i++ {
|
||||
tmpFile := fmt.Sprintf("%s%d", os.Args[0], i)
|
||||
if _, err := os.Stat(tmpFile); err == nil {
|
||||
if err := os.Remove(tmpFile); err != nil {
|
||||
gLog.d(" remove %s error:%s", tmpFile, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
tmpFile = os.Args[0] + "1"
|
||||
if _, err := os.Stat(tmpFile); err == nil {
|
||||
if err := os.Remove(tmpFile); err != nil {
|
||||
gLog.Printf(LvDEBUG, " remove %s error:%s", tmpFile, err)
|
||||
tmpFile = fmt.Sprintf("%s%s%d", tempDir, backupBase, i)
|
||||
if _, err := os.Stat(tmpFile); err == nil {
|
||||
if err := os.Remove(tmpFile); err != nil {
|
||||
gLog.d(" remove %s error:%s", tmpFile, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
core/upnp.go
@@ -63,13 +63,15 @@ func Discover() (nat NAT, err error) {
|
||||
return
|
||||
}
|
||||
var n int
|
||||
socket.SetDeadline(time.Now().Add(3 * time.Second))
|
||||
_, _, err = socket.ReadFromUDP(answerBytes)
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "UPNP discover error:", err)
|
||||
gLog.d("UPNP discover error:%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
socket.SetDeadline(time.Now().Add(3 * time.Second))
|
||||
n, _, err = socket.ReadFromUDP(answerBytes)
|
||||
if err != nil {
|
||||
break
|
||||
@@ -266,7 +268,11 @@ func soapRequest(url, function, message, domain string) (r *http.Response, err e
|
||||
|
||||
// log.Stderr("soapRequest ", req)
|
||||
|
||||
r, err = http.DefaultClient.Do(req)
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
r, err = client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -5,9 +5,12 @@ import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
var (
|
||||
defaultInstallPath = "/usr/local/openp2p"
|
||||
defaultBinName = "openp2p"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultBinName = "openp2p"
|
||||
)
|
||||
|
||||
func getOsName() (osName string) {
|
||||
|
||||
@@ -5,14 +5,16 @@ import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
var (
|
||||
defaultInstallPath = "/usr/local/openp2p"
|
||||
defaultBinName = "openp2p"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultBinName = "openp2p"
|
||||
)
|
||||
|
||||
func getOsName() (osName string) {
|
||||
|
||||
@@ -10,9 +10,12 @@ import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
var (
|
||||
defaultInstallPath = "/usr/local/openp2p"
|
||||
defaultBinName = "openp2p"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultBinName = "openp2p"
|
||||
)
|
||||
|
||||
func getOsName() (osName string) {
|
||||
|
||||
@@ -11,9 +11,12 @@ import (
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
const (
|
||||
var (
|
||||
defaultInstallPath = "C:\\Program Files\\OpenP2P"
|
||||
defaultBinName = "openp2p.exe"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultBinName = "openp2p.exe"
|
||||
)
|
||||
|
||||
func getOsName() (osName string) {
|
||||
@@ -47,7 +50,7 @@ func setRLimit() error {
|
||||
func setFirewall() {
|
||||
fullPath, err := filepath.Abs(os.Args[0])
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "add firewall error:", err)
|
||||
gLog.e("add firewall error:%s", err)
|
||||
return
|
||||
}
|
||||
isXP := false
|
||||
|
||||
@@ -1,91 +1,156 @@
|
||||
package openp2p
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/quic-go/quic-go"
|
||||
)
|
||||
|
||||
type v4Listener struct {
|
||||
conns sync.Map
|
||||
port int
|
||||
acceptCh chan bool
|
||||
conns sync.Map
|
||||
port int
|
||||
acceptCh chan bool
|
||||
running bool
|
||||
tcpListener *net.TCPListener
|
||||
udpListener quic.Listener
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
func (vl *v4Listener) start() error {
|
||||
func (vl *v4Listener) start() {
|
||||
vl.running = true
|
||||
v4l.acceptCh = make(chan bool, 500)
|
||||
for {
|
||||
vl.listen()
|
||||
time.Sleep(UnderlayTCPConnectTimeout)
|
||||
}
|
||||
vl.wg.Add(1)
|
||||
go func() {
|
||||
defer vl.wg.Done()
|
||||
for vl.running {
|
||||
vl.listenTCP()
|
||||
time.Sleep(UnderlayTCPConnectTimeout)
|
||||
}
|
||||
}()
|
||||
vl.wg.Add(1)
|
||||
go func() {
|
||||
defer vl.wg.Done()
|
||||
for vl.running {
|
||||
vl.listenUDP()
|
||||
time.Sleep(UnderlayTCPConnectTimeout)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (vl *v4Listener) listen() error {
|
||||
gLog.Printf(LvINFO, "v4Listener listen %d start", vl.port)
|
||||
defer gLog.Printf(LvINFO, "v4Listener listen %d end", vl.port)
|
||||
addr, _ := net.ResolveTCPAddr("tcp4", fmt.Sprintf("0.0.0.0:%d", vl.port))
|
||||
l, err := net.ListenTCP("tcp4", addr)
|
||||
func (vl *v4Listener) stop() {
|
||||
vl.running = false
|
||||
if vl.tcpListener != nil {
|
||||
vl.tcpListener.Close()
|
||||
}
|
||||
if vl.udpListener != nil {
|
||||
vl.udpListener.Close()
|
||||
}
|
||||
vl.wg.Wait()
|
||||
}
|
||||
|
||||
func (vl *v4Listener) listenTCP() error {
|
||||
gLog.d("v4Listener listenTCP %d start", vl.port)
|
||||
defer gLog.d("v4Listener listenTCP %d end", vl.port)
|
||||
addr, _ := net.ResolveTCPAddr("tcp", fmt.Sprintf("0.0.0.0:%d", vl.port)) // system will auto listen both v4 and v6
|
||||
var err error
|
||||
vl.tcpListener, err = net.ListenTCP("tcp", addr)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "v4Listener listen %d error:", vl.port, err)
|
||||
gLog.e("v4Listener listen %d error:", vl.port, err)
|
||||
return err
|
||||
}
|
||||
defer l.Close()
|
||||
defer vl.tcpListener.Close()
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
c, err := vl.tcpListener.Accept()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
go vl.handleConnection(c)
|
||||
utcp := &underlayTCP{writeMtx: &sync.Mutex{}, Conn: c, connectTime: time.Now()}
|
||||
go vl.handleConnection(utcp)
|
||||
}
|
||||
vl.tcpListener = nil
|
||||
return nil
|
||||
}
|
||||
func (vl *v4Listener) handleConnection(c net.Conn) {
|
||||
gLog.Println(LvDEBUG, "v4Listener accept connection: ", c.RemoteAddr().String())
|
||||
utcp := &underlayTCP{writeMtx: &sync.Mutex{}, Conn: c, connectTime: time.Now()}
|
||||
utcp.SetReadDeadline(time.Now().Add(UnderlayTCPConnectTimeout))
|
||||
_, buff, err := utcp.ReadBuffer()
|
||||
|
||||
func (vl *v4Listener) listenUDP() error {
|
||||
gLog.d("v4Listener listenUDP %d start", vl.port)
|
||||
defer gLog.d("v4Listener listenUDP %d end", vl.port)
|
||||
var err error
|
||||
vl.udpListener, err = quic.ListenAddr(fmt.Sprintf("0.0.0.0:%d", vl.port), generateTLSConfig(),
|
||||
&quic.Config{Versions: quicVersion, MaxIdleTimeout: TunnelIdleTimeout, DisablePathMTUDiscovery: true})
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "utcp.ReadBuffer error:", err)
|
||||
return err
|
||||
}
|
||||
utcp.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, buff)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), UnderlayConnectTimeout)
|
||||
defer cancel()
|
||||
defer vl.udpListener.Close()
|
||||
for {
|
||||
sess, err := vl.udpListener.Accept(context.Background())
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
stream, err := sess.AcceptStream(ctx)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
ul := &underlayQUIC{writeMtx: &sync.Mutex{}, Stream: stream, Connection: sess}
|
||||
go vl.handleConnection(ul)
|
||||
}
|
||||
vl.udpListener = nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (vl *v4Listener) handleConnection(ul underlay) {
|
||||
gLog.d("v4Listener accept connection: %s", ul.RemoteAddr().String())
|
||||
ul.SetReadDeadline(time.Now().Add(UnderlayTCPConnectTimeout))
|
||||
_, buff, err := ul.ReadBuffer()
|
||||
if err != nil || buff == nil {
|
||||
gLog.e("v4Listener read MsgTunnelHandshake error:%s", err)
|
||||
}
|
||||
ul.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, buff)
|
||||
var tid uint64
|
||||
if string(buff) == "OpenP2P,hello" { // old client
|
||||
// save remoteIP as key
|
||||
remoteAddr := c.RemoteAddr().(*net.TCPAddr).IP
|
||||
remoteAddr := ul.RemoteAddr().(*net.TCPAddr).IP
|
||||
ipBytes := remoteAddr.To4()
|
||||
tid = uint64(binary.BigEndian.Uint32(ipBytes)) // bytes not enough for uint64
|
||||
gLog.Println(LvDEBUG, "hello ", string(buff))
|
||||
gLog.d("hello %s", string(buff))
|
||||
} else {
|
||||
if len(buff) < 8 {
|
||||
return
|
||||
}
|
||||
tid = binary.LittleEndian.Uint64(buff[:8])
|
||||
gLog.Println(LvDEBUG, "hello ", tid)
|
||||
gLog.d("hello %d", tid)
|
||||
}
|
||||
// clear timeout connection
|
||||
// clear timeout connections
|
||||
vl.conns.Range(func(idx, i interface{}) bool {
|
||||
ut := i.(*underlayTCP)
|
||||
if ut.connectTime.Before(time.Now().Add(-UnderlayTCPConnectTimeout)) {
|
||||
vl.conns.Delete(idx)
|
||||
if ut, ok := i.(*underlayTCP); ok {
|
||||
if ut.connectTime.Before(time.Now().Add(-UnderlayTCPConnectTimeout)) {
|
||||
vl.conns.Delete(idx)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
vl.conns.Store(tid, utcp)
|
||||
if len(vl.acceptCh) == 0 {
|
||||
vl.acceptCh <- true
|
||||
vl.conns.Store(tid, ul)
|
||||
select {
|
||||
case vl.acceptCh <- true:
|
||||
default:
|
||||
gLog.e("msgQueue full, drop it")
|
||||
}
|
||||
}
|
||||
|
||||
func (vl *v4Listener) getUnderlayTCP(tid uint64) *underlayTCP {
|
||||
func (vl *v4Listener) getUnderlay(tid uint64) underlay {
|
||||
for i := 0; i < 100; i++ {
|
||||
select {
|
||||
case <-time.After(time.Millisecond * 50):
|
||||
case <-vl.acceptCh:
|
||||
}
|
||||
if u, ok := vl.conns.LoadAndDelete(tid); ok {
|
||||
return u.(*underlayTCP)
|
||||
return u.(underlay)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 361 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 340 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 201 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 297 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 327 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 247 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 273 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 23 KiB |
@@ -6,7 +6,9 @@ RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||||
rm -rf /tmp/* /var/tmp/* /var/cache/apk/* /var/cache/distfiles/*
|
||||
|
||||
COPY get-client.sh /
|
||||
ARG DOCKER_VER="latest"
|
||||
ARG VERSION
|
||||
LABEL version=${VERSION}
|
||||
# ARG DOCKER_VER="latest"
|
||||
RUN echo $TARGETPLATFORM && chmod +x /get-client.sh && ./get-client.sh
|
||||
|
||||
ENTRYPOINT ["/openp2p"]
|
||||
ENTRYPOINT ["/usr/local/openp2p/openp2p"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
|
||||
echo "Building version:${DOCKER_VER}"
|
||||
echo "Building version:${VERSION}"
|
||||
echo "Running on platform: $TARGETPLATFORM"
|
||||
# TARGETPLATFORM=$(echo $TARGETPLATFORM | tr ',' '/')
|
||||
echo "Running on platform: $TARGETPLATFORM"
|
||||
@@ -25,7 +25,7 @@ sysType="linux-amd64"
|
||||
sysType="linux-mipsbe"
|
||||
fi
|
||||
fi
|
||||
url="https://openp2p.cn/download/v1/${DOCKER_VER}/openp2p-latest.$sysType.tar.gz"
|
||||
url="https://console.openpxp.com/download/v1/${VERSION}/openp2p-${VERSION}.$sysType.tar.gz"
|
||||
echo "download $url start"
|
||||
|
||||
if [ -f /usr/bin/curl ]; then
|
||||
@@ -38,8 +38,9 @@ if [ $? -ne 0 ]; then
|
||||
exit 9
|
||||
fi
|
||||
echo "download ok"
|
||||
tar -xzvf openp2p.tar.gz
|
||||
chmod +x openp2p
|
||||
mkdir -p /usr/local/openp2p/
|
||||
tar -xzvf openp2p.tar.gz -C /usr/local/openp2p/
|
||||
chmod +x /usr/local/openp2p/openp2p
|
||||
pwd
|
||||
ls -l
|
||||
exit 0
|
||||
|
||||
@@ -2,12 +2,12 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
op "openp2p/core"
|
||||
op2p "openp2p/core"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
op.Run()
|
||||
op2p.Run()
|
||||
for i := 0; i < 10; i++ {
|
||||
go echoClient("5800-debug")
|
||||
}
|
||||
@@ -15,28 +15,28 @@ func main() {
|
||||
}
|
||||
|
||||
func echoClient(peerNode string) {
|
||||
sendDatalen := op.ReadBuffLen
|
||||
sendDatalen := op2p.ReadBuffLen
|
||||
sendBuff := make([]byte, sendDatalen)
|
||||
for i := 0; i < len(sendBuff); i++ {
|
||||
sendBuff[i] = byte('A' + i/100)
|
||||
}
|
||||
// peerNode = "YOUR-PEER-NODE-NAME"
|
||||
if err := op.GNetwork.ConnectNode(peerNode); err != nil {
|
||||
if err := op2p.GNetwork.ConnectNode(peerNode); err != nil {
|
||||
fmt.Println("connect error:", err)
|
||||
return
|
||||
}
|
||||
for i := 0; ; i++ {
|
||||
sendBuff[1] = 'A' + byte(i%26)
|
||||
if err := op.GNetwork.WriteNode(op.NodeNameToID(peerNode), sendBuff[:sendDatalen]); err != nil {
|
||||
if err := op2p.GNetwork.WriteNode(op2p.NodeNameToID(peerNode), sendBuff[:sendDatalen]); err != nil {
|
||||
fmt.Println("write error:", err)
|
||||
break
|
||||
}
|
||||
nd := op.GNetwork.ReadNode(time.Second * 10)
|
||||
nd := op2p.GNetwork.ReadNode(time.Second * 10)
|
||||
if nd == nil {
|
||||
fmt.Printf("waiting for node data\n")
|
||||
time.Sleep(time.Second * 10)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("read %d len=%d data=%s\n", nd.NodeID, len(nd.Data), nd.Data[:16]) // only print 16 bytes
|
||||
fmt.Printf("read len=%d data=%s\n", len(nd), nd[:16]) // only print 16 bytes
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
op "openp2p/core"
|
||||
op2p "openp2p/core"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
op.Run()
|
||||
op2p.Run()
|
||||
echoServer()
|
||||
forever := make(chan bool)
|
||||
<-forever
|
||||
@@ -16,15 +16,15 @@ func main() {
|
||||
func echoServer() {
|
||||
// peerID := fmt.Sprintf("%d", core.NodeNameToID(peerNode))
|
||||
for {
|
||||
nd := op.GNetwork.ReadNode(time.Second * 10)
|
||||
nd := op2p.GNetwork.ReadNode(time.Second * 10)
|
||||
if nd == nil {
|
||||
fmt.Printf("waiting for node data\n")
|
||||
// time.Sleep(time.Second * 10)
|
||||
continue
|
||||
}
|
||||
// fmt.Printf("read %s len=%d data=%s\n", nd.Node, len(nd.Data), nd.Data[:16])
|
||||
nd.Data[0] = 'R' // echo server mark as replied
|
||||
if err := op.GNetwork.WriteNode(nd.NodeID, nd.Data); err != nil {
|
||||
nd[0] = 'R' // echo server mark as replied
|
||||
if err := op2p.GNetwork.WriteNode(0, nd); err != nil {
|
||||
fmt.Println("write error:", err)
|
||||
break
|
||||
}
|
||||
|
||||
17
go.mod
@@ -4,13 +4,16 @@ go 1.20
|
||||
|
||||
require (
|
||||
github.com/emirpasic/gods v1.18.1
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/huin/goupnp v1.3.0
|
||||
github.com/jackpal/go-nat-pmp v1.0.2
|
||||
github.com/openp2p-cn/go-reuseport v0.3.2
|
||||
github.com/openp2p-cn/service v1.0.0
|
||||
github.com/openp2p-cn/totp v0.0.0-20230421034602-0f3320ffb25e
|
||||
github.com/openp2p-cn/wireguard-go v0.0.20241020
|
||||
github.com/quic-go/quic-go v0.34.0
|
||||
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54
|
||||
github.com/xtaci/kcp-go/v5 v5.5.17
|
||||
golang.org/x/net v0.30.0
|
||||
golang.org/x/sys v0.26.0
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3
|
||||
)
|
||||
@@ -20,22 +23,16 @@ require (
|
||||
github.com/golang/mock v1.7.0-rc.1 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
github.com/kardianos/service v1.2.2 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/klauspost/reedsolomon v1.11.8 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
|
||||
github.com/templexxx/cpu v0.1.0 // indirect
|
||||
github.com/templexxx/xorsimd v0.4.2 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
|
||||
golang.org/x/crypto v0.28.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/tools v0.26.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20241128011400-745828301c93 // indirect
|
||||
)
|
||||
|
||||
109
go.sum
Normal file
@@ -0,0 +1,109 @@
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=
|
||||
github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
|
||||
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||
github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60=
|
||||
github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
|
||||
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
|
||||
github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
|
||||
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
|
||||
github.com/openp2p-cn/go-reuseport v0.3.2 h1:TO78WsyJ1F6g7rLp3hpTKOBxtZTU5Lz+Y4Mj+fVUfZc=
|
||||
github.com/openp2p-cn/go-reuseport v0.3.2/go.mod h1:+EwCusXz50jaYkPNZcCrK4cLoA9tr2jEiJC+bjzpWc8=
|
||||
github.com/openp2p-cn/service v1.0.0 h1:1++FroLvW4Mc/PStFIAF0mzudVW6E8EAeqWyIESTGZA=
|
||||
github.com/openp2p-cn/service v1.0.0/go.mod h1:U4VHekhSJldZ332W6bLviB1fipDrS4omY4dHVc/kgts=
|
||||
github.com/openp2p-cn/totp v0.0.0-20230421034602-0f3320ffb25e h1:QqP3Va/nPj45wq0C8OmGiyZ4HhbTcV6yGuhcYCMgbjg=
|
||||
github.com/openp2p-cn/totp v0.0.0-20230421034602-0f3320ffb25e/go.mod h1:RYVP3CTIvHD9IwQe2M3zy5iLKNjusRVDz/4gQuKcc/o=
|
||||
github.com/openp2p-cn/wireguard-go v0.0.20241020 h1:cNgG8o2ctYT9YanqalfMQo+jVju7MrdJFI6WLZZRr7M=
|
||||
github.com/openp2p-cn/wireguard-go v0.0.20241020/go.mod h1:ka26SCScyLEd+uFrnq6w4n65Sxq1W/xIJfXEXLLvJEc=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U=
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||
github.com/quic-go/quic-go v0.34.0 h1:OvOJ9LFjTySgwOTYUZmNoq0FzVicP8YujpV0kB7m2lU=
|
||||
github.com/quic-go/quic-go v0.34.0/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54 h1:8mhqcHPqTMhSPoslhGYihEgSfc77+7La1P6kiB6+9So=
|
||||
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
|
||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
|
||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gvisor.dev/gvisor v0.0.0-20241128011400-745828301c93 h1:QyA/pFgC67EZ5+0oRfiNFhfEGd3NqZM1A2HQEuPKC3c=
|
||||
gvisor.dev/gvisor v0.0.0-20241128011400-745828301c93/go.mod h1:5DMfjtclAbTIjbXqO1qCe2K5GKKxWz2JHvCChuTcJEM=
|
||||
239
pkg/upnp/nat.go
Normal file
@@ -0,0 +1,239 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package nat provides access to common network port mapping protocols.
|
||||
package openp2p
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
natpmp "github.com/jackpal/go-nat-pmp"
|
||||
)
|
||||
|
||||
// Interface An implementation of nat.Interface can map local ports to ports
|
||||
// accessible from the Internet.
|
||||
type Interface interface {
|
||||
// These methods manage a mapping between a port on the local
|
||||
// machine to a port that can be connected to from the internet.
|
||||
//
|
||||
// protocol is "UDP" or "TCP". Some implementations allow setting
|
||||
// a display name for the mapping. The mapping may be removed by
|
||||
// the gateway when its lifetime ends.
|
||||
AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) (uint16, error)
|
||||
DeleteMapping(protocol string, extport, intport int) error
|
||||
|
||||
// ExternalIP should return the external (Internet-facing)
|
||||
// address of the gateway device.
|
||||
ExternalIP() (net.IP, error)
|
||||
|
||||
// String should return name of the method. This is used for logging.
|
||||
String() string
|
||||
}
|
||||
|
||||
// Parse parses a NAT interface description.
|
||||
// The following formats are currently accepted.
|
||||
// Note that mechanism names are not case-sensitive.
|
||||
//
|
||||
// "" or "none" return nil
|
||||
// "extip:77.12.33.4" will assume the local machine is reachable on the given IP
|
||||
// "any" uses the first auto-detected mechanism
|
||||
// "upnp" uses the Universal Plug and Play protocol
|
||||
// "pmp" uses NAT-PMP with an auto-detected gateway address
|
||||
// "pmp:192.168.0.1" uses NAT-PMP with the given gateway address
|
||||
func Parse(spec string) (Interface, error) {
|
||||
var (
|
||||
before, after, found = strings.Cut(spec, ":")
|
||||
mech = strings.ToLower(before)
|
||||
ip net.IP
|
||||
)
|
||||
if found {
|
||||
ip = net.ParseIP(after)
|
||||
if ip == nil {
|
||||
return nil, errors.New("invalid IP address")
|
||||
}
|
||||
}
|
||||
switch mech {
|
||||
case "", "none", "off":
|
||||
return nil, nil
|
||||
case "any", "auto", "on":
|
||||
return Any(), nil
|
||||
case "extip", "ip":
|
||||
if ip == nil {
|
||||
return nil, errors.New("missing IP address")
|
||||
}
|
||||
return ExtIP(ip), nil
|
||||
case "upnp":
|
||||
return UPnP(), nil
|
||||
case "pmp", "natpmp", "nat-pmp":
|
||||
return PMP(ip), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown mechanism %q", before)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
DefaultMapTimeout = 10 * time.Minute
|
||||
)
|
||||
|
||||
// Map adds a port mapping on m and keeps it alive until c is closed.
|
||||
// This function is typically invoked in its own goroutine.
|
||||
//
|
||||
// Note that Map does not handle the situation where the NAT interface assigns a different
|
||||
// external port than the requested one.
|
||||
func Map(m Interface, c <-chan struct{}, protocol string, extport, intport int, name string) {
|
||||
// log := log.New("proto", protocol, "extport", extport, "intport", intport, "interface", m)
|
||||
refresh := time.NewTimer(DefaultMapTimeout)
|
||||
defer func() {
|
||||
refresh.Stop()
|
||||
// log.Debug("Deleting port mapping")
|
||||
m.DeleteMapping(protocol, extport, intport)
|
||||
}()
|
||||
if _, err := m.AddMapping(protocol, extport, intport, name, DefaultMapTimeout); err != nil {
|
||||
// log.Debug("Couldn't add port mapping", "err", err)
|
||||
} else {
|
||||
// log.Info("Mapped network port")
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case _, ok := <-c:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
case <-refresh.C:
|
||||
// log.Trace("Refreshing port mapping")
|
||||
if _, err := m.AddMapping(protocol, extport, intport, name, DefaultMapTimeout); err != nil {
|
||||
// log.Debug("Couldn't add port mapping", "err", err)
|
||||
}
|
||||
refresh.Reset(DefaultMapTimeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ExtIP assumes that the local machine is reachable on the given
|
||||
// external IP address, and that any required ports were mapped manually.
|
||||
// Mapping operations will not return an error but won't actually do anything.
|
||||
type ExtIP net.IP
|
||||
|
||||
func (n ExtIP) ExternalIP() (net.IP, error) { return net.IP(n), nil }
|
||||
func (n ExtIP) String() string { return fmt.Sprintf("ExtIP(%v)", net.IP(n)) }
|
||||
|
||||
// These do nothing.
|
||||
|
||||
func (ExtIP) AddMapping(string, int, int, string, time.Duration) (uint16, error) { return 0, nil }
|
||||
func (ExtIP) DeleteMapping(string, int, int) error { return nil }
|
||||
|
||||
// Any returns a port mapper that tries to discover any supported
|
||||
// mechanism on the local network.
|
||||
func Any() Interface {
|
||||
// TODO: attempt to discover whether the local machine has an
|
||||
// Internet-class address. Return ExtIP in this case.
|
||||
return startautodisc("UPnP or NAT-PMP", func() Interface {
|
||||
found := make(chan Interface, 2)
|
||||
go func() { found <- discoverUPnP() }()
|
||||
go func() { found <- discoverPMP() }()
|
||||
for i := 0; i < cap(found); i++ {
|
||||
if c := <-found; c != nil {
|
||||
return c
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// UPnP returns a port mapper that uses UPnP. It will attempt to
|
||||
// discover the address of your router using UDP broadcasts.
|
||||
func UPnP() Interface {
|
||||
return startautodisc("UPnP", discoverUPnP)
|
||||
}
|
||||
|
||||
// PMP returns a port mapper that uses NAT-PMP. The provided gateway
|
||||
// address should be the IP of your router. If the given gateway
|
||||
// address is nil, PMP will attempt to auto-discover the router.
|
||||
func PMP(gateway net.IP) Interface {
|
||||
if gateway != nil {
|
||||
return &pmp{gw: gateway, c: natpmp.NewClient(gateway)}
|
||||
}
|
||||
return startautodisc("NAT-PMP", discoverPMP)
|
||||
}
|
||||
|
||||
// autodisc represents a port mapping mechanism that is still being
|
||||
// auto-discovered. Calls to the Interface methods on this type will
|
||||
// wait until the discovery is done and then call the method on the
|
||||
// discovered mechanism.
|
||||
//
|
||||
// This type is useful because discovery can take a while but we
|
||||
// want return an Interface value from UPnP, PMP and Auto immediately.
|
||||
type autodisc struct {
|
||||
what string // type of interface being autodiscovered
|
||||
once sync.Once
|
||||
doit func() Interface
|
||||
|
||||
mu sync.Mutex
|
||||
found Interface
|
||||
}
|
||||
|
||||
func startautodisc(what string, doit func() Interface) Interface {
|
||||
// TODO: monitor network configuration and rerun doit when it changes.
|
||||
return &autodisc{what: what, doit: doit}
|
||||
}
|
||||
|
||||
func (n *autodisc) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) (uint16, error) {
|
||||
if err := n.wait(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return n.found.AddMapping(protocol, extport, intport, name, lifetime)
|
||||
}
|
||||
|
||||
func (n *autodisc) DeleteMapping(protocol string, extport, intport int) error {
|
||||
if err := n.wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
return n.found.DeleteMapping(protocol, extport, intport)
|
||||
}
|
||||
|
||||
func (n *autodisc) ExternalIP() (net.IP, error) {
|
||||
if err := n.wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return n.found.ExternalIP()
|
||||
}
|
||||
|
||||
func (n *autodisc) String() string {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
if n.found == nil {
|
||||
return n.what
|
||||
}
|
||||
return n.found.String()
|
||||
}
|
||||
|
||||
// wait blocks until auto-discovery has been performed.
|
||||
func (n *autodisc) wait() error {
|
||||
n.once.Do(func() {
|
||||
n.mu.Lock()
|
||||
n.found = n.doit()
|
||||
n.mu.Unlock()
|
||||
})
|
||||
if n.found == nil {
|
||||
return fmt.Errorf("no %s router discovered", n.what)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
130
pkg/upnp/natpmp.go
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package openp2p
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
natpmp "github.com/jackpal/go-nat-pmp"
|
||||
)
|
||||
|
||||
// natPMPClient adapts the NAT-PMP protocol implementation so it conforms to
|
||||
// the common interface.
|
||||
type pmp struct {
|
||||
gw net.IP
|
||||
c *natpmp.Client
|
||||
}
|
||||
|
||||
func (n *pmp) String() string {
|
||||
return fmt.Sprintf("NAT-PMP(%v)", n.gw)
|
||||
}
|
||||
|
||||
func (n *pmp) ExternalIP() (net.IP, error) {
|
||||
response, err := n.c.GetExternalAddress()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.ExternalIPAddress[:], nil
|
||||
}
|
||||
|
||||
func (n *pmp) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) (uint16, error) {
|
||||
if lifetime <= 0 {
|
||||
return 0, fmt.Errorf("lifetime must not be <= 0")
|
||||
}
|
||||
// Note order of port arguments is switched between our
|
||||
// AddMapping and the client's AddPortMapping.
|
||||
res, err := n.c.AddPortMapping(strings.ToLower(protocol), intport, extport, int(lifetime/time.Second))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// NAT-PMP maps an alternative available port number if the requested port
|
||||
// is already mapped to another address and returns success. Handling of
|
||||
// alternate port numbers is done by the caller.
|
||||
return res.MappedExternalPort, nil
|
||||
}
|
||||
|
||||
func (n *pmp) DeleteMapping(protocol string, extport, intport int) (err error) {
|
||||
// To destroy a mapping, send an add-port with an internalPort of
|
||||
// the internal port to destroy, an external port of zero and a
|
||||
// time of zero.
|
||||
_, err = n.c.AddPortMapping(strings.ToLower(protocol), intport, 0, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
func discoverPMP() Interface {
|
||||
// run external address lookups on all potential gateways
|
||||
gws := potentialGateways()
|
||||
found := make(chan *pmp, len(gws))
|
||||
for i := range gws {
|
||||
gw := gws[i]
|
||||
go func() {
|
||||
c := natpmp.NewClient(gw)
|
||||
if _, err := c.GetExternalAddress(); err != nil {
|
||||
found <- nil
|
||||
} else {
|
||||
found <- &pmp{gw, c}
|
||||
}
|
||||
}()
|
||||
}
|
||||
// return the one that responds first.
|
||||
// discovery needs to be quick, so we stop caring about
|
||||
// any responses after a very short timeout.
|
||||
timeout := time.NewTimer(1 * time.Second)
|
||||
defer timeout.Stop()
|
||||
for range gws {
|
||||
select {
|
||||
case c := <-found:
|
||||
if c != nil {
|
||||
return c
|
||||
}
|
||||
case <-timeout.C:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: improve this. We currently assume that (on most networks)
|
||||
// the router is X.X.X.1 in a local LAN range.
|
||||
func potentialGateways() (gws []net.IP) {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for _, iface := range ifaces {
|
||||
ifaddrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
return gws
|
||||
}
|
||||
for _, addr := range ifaddrs {
|
||||
if x, ok := addr.(*net.IPNet); ok {
|
||||
if x.IP.IsPrivate() {
|
||||
ip := x.IP.Mask(x.Mask).To4()
|
||||
if ip != nil {
|
||||
ip[3] = ip[3] | 0x01
|
||||
gws = append(gws, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return gws
|
||||
}
|
||||
247
pkg/upnp/natupnp.go
Normal file
@@ -0,0 +1,247 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package openp2p
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/huin/goupnp"
|
||||
"github.com/huin/goupnp/dcps/internetgateway1"
|
||||
"github.com/huin/goupnp/dcps/internetgateway2"
|
||||
)
|
||||
|
||||
const (
|
||||
soapRequestTimeout = 3 * time.Second
|
||||
rateLimit = 200 * time.Millisecond
|
||||
)
|
||||
|
||||
type upnp struct {
|
||||
dev *goupnp.RootDevice
|
||||
service string
|
||||
client upnpClient
|
||||
mu sync.Mutex
|
||||
lastReqTime time.Time
|
||||
rand *rand.Rand
|
||||
}
|
||||
|
||||
type upnpClient interface {
|
||||
GetExternalIPAddress() (string, error)
|
||||
AddPortMapping(string, uint16, string, uint16, string, bool, string, uint32) error
|
||||
DeletePortMapping(string, uint16, string) error
|
||||
GetNATRSIPStatus() (sip bool, nat bool, err error)
|
||||
}
|
||||
|
||||
func (n *upnp) natEnabled() bool {
|
||||
var ok bool
|
||||
var err error
|
||||
n.withRateLimit(func() error {
|
||||
_, ok, err = n.client.GetNATRSIPStatus()
|
||||
return err
|
||||
})
|
||||
return err == nil && ok
|
||||
}
|
||||
|
||||
func (n *upnp) ExternalIP() (addr net.IP, err error) {
|
||||
var ipString string
|
||||
n.withRateLimit(func() error {
|
||||
ipString, err = n.client.GetExternalIPAddress()
|
||||
return err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ip := net.ParseIP(ipString)
|
||||
if ip == nil {
|
||||
return nil, errors.New("bad IP in response")
|
||||
}
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, lifetime time.Duration) (uint16, error) {
|
||||
ip, err := n.internalAddress()
|
||||
if err != nil {
|
||||
return 0, nil // TODO: Shouldn't we return the error?
|
||||
}
|
||||
protocol = strings.ToUpper(protocol)
|
||||
lifetimeS := uint32(lifetime / time.Second)
|
||||
n.DeleteMapping(protocol, extport, intport)
|
||||
|
||||
err = n.withRateLimit(func() error {
|
||||
return n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS)
|
||||
})
|
||||
if err == nil {
|
||||
return uint16(extport), nil
|
||||
}
|
||||
|
||||
return uint16(extport), n.withRateLimit(func() error {
|
||||
p, err := n.addAnyPortMapping(protocol, extport, intport, ip, desc, lifetimeS)
|
||||
if err == nil {
|
||||
extport = int(p)
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (n *upnp) addAnyPortMapping(protocol string, extport, intport int, ip net.IP, desc string, lifetimeS uint32) (uint16, error) {
|
||||
if client, ok := n.client.(*internetgateway2.WANIPConnection2); ok {
|
||||
return client.AddAnyPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS)
|
||||
}
|
||||
// It will retry with a random port number if the client does
|
||||
// not support AddAnyPortMapping.
|
||||
extport = n.randomPort()
|
||||
err := n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint16(extport), nil
|
||||
}
|
||||
|
||||
func (n *upnp) randomPort() int {
|
||||
if n.rand == nil {
|
||||
n.rand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
}
|
||||
return n.rand.Intn(math.MaxUint16-10000) + 10000
|
||||
}
|
||||
|
||||
func (n *upnp) internalAddress() (net.IP, error) {
|
||||
devaddr, err := net.ResolveUDPAddr("udp4", n.dev.URLBase.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, iface := range ifaces {
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
if x, ok := addr.(*net.IPNet); ok && x.Contains(devaddr.IP) {
|
||||
return x.IP, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("could not find local address in same net as %v", devaddr)
|
||||
}
|
||||
|
||||
func (n *upnp) DeleteMapping(protocol string, extport, intport int) error {
|
||||
return n.withRateLimit(func() error {
|
||||
return n.client.DeletePortMapping("", uint16(extport), strings.ToUpper(protocol))
|
||||
})
|
||||
}
|
||||
|
||||
func (n *upnp) String() string {
|
||||
return "UPNP " + n.service
|
||||
}
|
||||
|
||||
func (n *upnp) withRateLimit(fn func() error) error {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
|
||||
lastreq := time.Since(n.lastReqTime)
|
||||
if lastreq < rateLimit {
|
||||
time.Sleep(rateLimit - lastreq)
|
||||
}
|
||||
err := fn()
|
||||
n.lastReqTime = time.Now()
|
||||
return err
|
||||
}
|
||||
|
||||
// discoverUPnP searches for Internet Gateway Devices
|
||||
// and returns the first one it can find on the local network.
|
||||
func discoverUPnP() Interface {
|
||||
found := make(chan *upnp, 2)
|
||||
// IGDv1
|
||||
go discover(found, internetgateway1.URN_WANConnectionDevice_1, func(sc goupnp.ServiceClient) *upnp {
|
||||
switch sc.Service.ServiceType {
|
||||
case internetgateway1.URN_WANIPConnection_1:
|
||||
return &upnp{service: "IGDv1-IP1", client: &internetgateway1.WANIPConnection1{ServiceClient: sc}}
|
||||
case internetgateway1.URN_WANPPPConnection_1:
|
||||
return &upnp{service: "IGDv1-PPP1", client: &internetgateway1.WANPPPConnection1{ServiceClient: sc}}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
// IGDv2
|
||||
go discover(found, internetgateway2.URN_WANConnectionDevice_2, func(sc goupnp.ServiceClient) *upnp {
|
||||
switch sc.Service.ServiceType {
|
||||
case internetgateway2.URN_WANIPConnection_1:
|
||||
return &upnp{service: "IGDv2-IP1", client: &internetgateway2.WANIPConnection1{ServiceClient: sc}}
|
||||
case internetgateway2.URN_WANIPConnection_2:
|
||||
return &upnp{service: "IGDv2-IP2", client: &internetgateway2.WANIPConnection2{ServiceClient: sc}}
|
||||
case internetgateway2.URN_WANPPPConnection_1:
|
||||
return &upnp{service: "IGDv2-PPP1", client: &internetgateway2.WANPPPConnection1{ServiceClient: sc}}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
for i := 0; i < cap(found); i++ {
|
||||
if c := <-found; c != nil {
|
||||
return c
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// finds devices matching the given target and calls matcher for all
|
||||
// advertised services of each device. The first non-nil service found
|
||||
// is sent into out. If no service matched, nil is sent.
|
||||
func discover(out chan<- *upnp, target string, matcher func(goupnp.ServiceClient) *upnp) {
|
||||
devs, err := goupnp.DiscoverDevices(target)
|
||||
if err != nil {
|
||||
out <- nil
|
||||
return
|
||||
}
|
||||
found := false
|
||||
for i := 0; i < len(devs) && !found; i++ {
|
||||
if devs[i].Root == nil {
|
||||
continue
|
||||
}
|
||||
devs[i].Root.Device.VisitServices(func(service *goupnp.Service) {
|
||||
if found {
|
||||
return
|
||||
}
|
||||
// check for a matching IGD service
|
||||
sc := goupnp.ServiceClient{
|
||||
SOAPClient: service.NewSOAPClient(),
|
||||
RootDevice: devs[i].Root,
|
||||
Location: devs[i].Location,
|
||||
Service: service,
|
||||
}
|
||||
sc.SOAPClient.HTTPClient.Timeout = soapRequestTimeout
|
||||
upnp := matcher(sc)
|
||||
if upnp == nil {
|
||||
return
|
||||
}
|
||||
upnp.dev = devs[i].Root
|
||||
|
||||
out <- upnp
|
||||
found = true
|
||||
})
|
||||
}
|
||||
if !found {
|
||||
out <- nil
|
||||
}
|
||||
}
|
||||