Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12393f00c5 | ||
|
|
ef4bc1e1e3 | ||
|
|
a1621bcfdd | ||
|
|
47220fe38b | ||
|
|
d3e8ee2a32 | ||
|
|
8e303e93f8 | ||
|
|
471aa5e6ea | ||
|
|
a4c6668760 | ||
|
|
dfaff2c327 | ||
|
|
57fe6986b0 | ||
|
|
6639f40d70 | ||
|
|
d827fd108d | ||
|
|
4daeeaab1a |
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. 共享节点调度模型优化,对不同的运营商优化
|
||||
|
||||
22
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.
|
||||
|
||||
@@ -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 |
@@ -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
|
||||
}
|
||||
}
|
||||