Compare commits

...
9 Commits
Author SHA1 Message Date
TenderIronh 080e6af779 fix public ip detect bug 2024-12-02 21:10:15 +08:00
TenderIronh 77bfa45172 portmap loss & android ipv6 failed & public ip detect 2024-11-21 10:31:07 +08:00
TenderIronh 3616768682 rename 2024-11-21 10:29:06 +08:00
TenderIronh f015b828fc specified gomobile version 2024-10-20 21:33:20 +08:00
TenderIronh df1e16e708 3.21.8 2024-10-20 11:33:07 +08:00
W192547975 c68094cc12 CertPool Fix (#96)
Remove caCertPool errCert “else” in p2pnetwork.go
2024-08-02 14:23:47 +08:00
CAESIUS_TIM a0df0b1e95 [doc ]no bare urls (#80) 2024-08-02 14:22:31 +08:00
TenderIronh 9c3d557f5d LF 2024-07-26 22:27:24 +08:00
TenderIronh 2dea3a718d check remote service 2024-07-26 22:07:48 +08:00
35 changed files with 4300 additions and 4100 deletions
+2 -1
View File
@@ -20,4 +20,5 @@ wintun.dll
.vscode/ .vscode/
app/.idea/ app/.idea/
*_debug_bin* *_debug_bin*
cmd/openp2p cmd/openp2p
vendor/
+2 -2
View File
@@ -31,7 +31,7 @@ P2P直连可以让你的设备跑满带宽。不论你的设备在任何网络
## 快速入门 ## 快速入门
仅需简单4步就能用起来。 仅需简单4步就能用起来。
下面是一个远程办公例子:在家里连入办公室Windows电脑。 下面是一个远程办公例子:在家里连入办公室Windows电脑。
(另外一个快速入门视频 https://www.bilibili.com/video/BV1Et4y1P7bF/ (另外一个快速入门视频 <https://www.bilibili.com/video/BV1Et4y1P7bF/>
### 1.注册 ### 1.注册
前往<https://console.openp2p.cn> 注册新用户,暂无需任何认证 前往<https://console.openp2p.cn> 注册新用户,暂无需任何认证
@@ -96,7 +96,7 @@ Windows默认会阻止没有花钱买它家证书签名过的程序,选择“
服务端有个调度模型,根据带宽、ping值、稳定性、服务时长,尽可能地使共享节点均匀地提供服务。连接共享节点使用TOTP密码,hmac-sha256算法校验,它是一次性密码,和我们平时使用的手机验证码或银行密码器一样的原理。 服务端有个调度模型,根据带宽、ping值、稳定性、服务时长,尽可能地使共享节点均匀地提供服务。连接共享节点使用TOTP密码,hmac-sha256算法校验,它是一次性密码,和我们平时使用的手机验证码或银行密码器一样的原理。
## 编译 ## 编译
go version go1.18.1+ go version 1.20 only (支持win7)
cd到代码根目录,执行 cd到代码根目录,执行
``` ```
make make
+1 -1
View File
@@ -103,7 +103,7 @@ That's right, the relay node is naturally an man-in-middle, so AES encryption is
The server side has a scheduling model, which calculate bandwith, ping value,stability and service duration to provide a well-proportioned service to every share node. It uses TOTP(Time-based One-time Password) with hmac-sha256 algorithem, its theory as same as the cellphone validation code or bank cipher coder. The server side has a scheduling model, which calculate bandwith, ping value,stability and service duration to provide a well-proportioned service to every share node. It uses TOTP(Time-based One-time Password) with hmac-sha256 algorithem, its theory as same as the cellphone validation code or bank cipher coder.
## Build ## Build
go version go1.18.1+ go version 1.20 only (support win7)
cd root directory of the socure code and execute cd root directory of the socure code and execute
``` ```
make make
+108 -108
View File
@@ -1,108 +1,108 @@
# 手动运行说明 # 手动运行说明
大部分情况通过<https://console.openp2p.cn> 操作即可。有些情况需要手动运行 大部分情况通过<https://console.openp2p.cn> 操作即可。有些情况需要手动运行
> :warning: 本文所有命令, Windows环境使用"openp2p.exe", Linux环境使用"./openp2p" > :warning: 本文所有命令, Windows环境使用"openp2p.exe", Linux环境使用"./openp2p"
## 安装和监听 ## 安装和监听
``` ```
./openp2p install -node OFFICEPC1 -token TOKEN ./openp2p install -node OFFICEPC1 -token TOKEN
./openp2p -d -node OFFICEPC1 -token TOKEN ./openp2p -d -node OFFICEPC1 -token TOKEN
# 注意Windows系统把“./openp2p” 换成“openp2p.exe” # 注意Windows系统把“./openp2p” 换成“openp2p.exe”
``` ```
>* install: 安装模式【推荐】,会安装成系统服务,这样它就能随系统自动启动 >* install: 安装模式【推荐】,会安装成系统服务,这样它就能随系统自动启动
>* -d: daemon模式。发现worker进程意外退出就会自动启动新的worker进程 >* -d: daemon模式。发现worker进程意外退出就会自动启动新的worker进程
>* -node: 独一无二的节点名字,唯一标识 >* -node: 独一无二的节点名字,唯一标识
>* -token: 在<console.openp2p.cn>“我的”里面找到 >* -token: 在<console.openp2p.cn>“我的”里面找到
>* -sharebandwidth: 作为共享节点时提供带宽,默认10mbps. 如果是光纤大带宽,设置越大效果越好. 0表示不共享,该节点只在私有的P2P网络使用。不加入共享的P2P网络,这样也意味着无法使用别人的共享节点 >* -sharebandwidth: 作为共享节点时提供带宽,默认10mbps. 如果是光纤大带宽,设置越大效果越好. 0表示不共享,该节点只在私有的P2P网络使用。不加入共享的P2P网络,这样也意味着无法使用别人的共享节点
>* -loglevel: 需要查看更多调试日志,设置0;默认是1 >* -loglevel: 需要查看更多调试日志,设置0;默认是1
### 在docker容器里运行openp2p ### 在docker容器里运行openp2p
我们暂时还没提供官方docker镜像,你可以在随便一个容器里运行 我们暂时还没提供官方docker镜像,你可以在随便一个容器里运行
``` ```
nohup ./openp2p -d -node OFFICEPC1 -token TOKEN & nohup ./openp2p -d -node OFFICEPC1 -token TOKEN &
#这里由于一般的镜像都精简过,install系统服务会失败,所以使用直接daemon模式后台运行 #这里由于一般的镜像都精简过,install系统服务会失败,所以使用直接daemon模式后台运行
``` ```
## 连接 ## 连接
``` ```
./openp2p -d -node HOMEPC123 -token TOKEN -appname OfficeWindowsRemote -peernode OFFICEPC1 -dstip 127.0.0.1 -dstport 3389 -srcport 23389 ./openp2p -d -node HOMEPC123 -token TOKEN -appname OfficeWindowsRemote -peernode OFFICEPC1 -dstip 127.0.0.1 -dstport 3389 -srcport 23389
使用配置文件,建立多个P2PApp 使用配置文件,建立多个P2PApp
./openp2p -d ./openp2p -d
``` ```
>* -appname: 这个P2P应用名字 >* -appname: 这个P2P应用名字
>* -peernode: 目标节点名字 >* -peernode: 目标节点名字
>* -dstip: 目标服务地址,默认本机127.0.0.1 >* -dstip: 目标服务地址,默认本机127.0.0.1
>* -dstport: 目标服务端口,常见的如windows远程桌面3389Linux ssh 22 >* -dstport: 目标服务端口,常见的如windows远程桌面3389Linux ssh 22
>* -protocol: 目标服务协议 tcp、udp >* -protocol: 目标服务协议 tcp、udp
## 配置文件 ## 配置文件
一般保存在当前目录,安装模式下会保存到 `C:\Program Files\OpenP2P\config.json``/usr/local/openp2p/config.json` 一般保存在当前目录,安装模式下会保存到 `C:\Program Files\OpenP2P\config.json``/usr/local/openp2p/config.json`
希望修改参数,或者配置多个P2PApp可手动修改配置文件 希望修改参数,或者配置多个P2PApp可手动修改配置文件
配置实例 配置实例
``` ```
{ {
"network": { "network": {
"Node": "YOUR-NODE-NAME", "Node": "YOUR-NODE-NAME",
"Token": "TOKEN", "Token": "TOKEN",
"ShareBandwidth": 0, "ShareBandwidth": 0,
"ServerHost": "api.openp2p.cn", "ServerHost": "api.openp2p.cn",
"ServerPort": 27183, "ServerPort": 27183,
"UDPPort1": 27182, "UDPPort1": 27182,
"UDPPort2": 27183 "UDPPort2": 27183
}, },
"apps": [ "apps": [
{ {
"AppName": "OfficeWindowsPC", "AppName": "OfficeWindowsPC",
"Protocol": "tcp", "Protocol": "tcp",
"SrcPort": 23389, "SrcPort": 23389,
"PeerNode": "OFFICEPC1", "PeerNode": "OFFICEPC1",
"DstPort": 3389, "DstPort": 3389,
"DstHost": "localhost", "DstHost": "localhost",
}, },
{ {
"AppName": "OfficeServerSSH", "AppName": "OfficeServerSSH",
"Protocol": "tcp", "Protocol": "tcp",
"SrcPort": 22, "SrcPort": 22,
"PeerNode": "OFFICEPC1", "PeerNode": "OFFICEPC1",
"DstPort": 22, "DstPort": 22,
"DstHost": "192.168.1.5", "DstHost": "192.168.1.5",
} }
] ]
} }
``` ```
## 升级客户端 ## 升级客户端
``` ```
# update local client # update local client
./openp2p update ./openp2p update
# update remote client # update remote client
curl --insecure 'https://api.openp2p.cn:27183/api/v1/device/YOUR-NODE-NAME/update?user=&password=' curl --insecure 'https://api.openp2p.cn:27183/api/v1/device/YOUR-NODE-NAME/update?user=&password='
``` ```
Windows系统需要设置防火墙放行本程序,程序会自动设置,如果设置失败会影响连接功能。 Windows系统需要设置防火墙放行本程序,程序会自动设置,如果设置失败会影响连接功能。
Linux系统(Ubuntu和CentOS7)的防火墙默认配置均不会有影响,如果不行可尝试关闭防火墙 Linux系统(Ubuntu和CentOS7)的防火墙默认配置均不会有影响,如果不行可尝试关闭防火墙
``` ```
systemctl stop firewalld.service systemctl stop firewalld.service
systemctl start firewalld.service systemctl start firewalld.service
firewall-cmd --state firewall-cmd --state
``` ```
## 停止 ## 停止
TODO: windows linux macos TODO: windows linux macos
## 卸载 ## 卸载
``` ```
./openp2p uninstall ./openp2p uninstall
# 已安装时 # 已安装时
# windows # windows
C:\Program Files\OpenP2P\openp2p.exe uninstall C:\Program Files\OpenP2P\openp2p.exe uninstall
# linux,macos # linux,macos
sudo /usr/local/openp2p/openp2p uninstall sudo /usr/local/openp2p/openp2p uninstall
``` ```
## Docker运行 ## Docker运行
``` ```
# 把YOUR-TOKEN和YOUR-NODE-NAME替换成自己的 # 把YOUR-TOKEN和YOUR-NODE-NAME替换成自己的
docker run -d --restart=always --net host --name openp2p-client -e OPENP2P_TOKEN=YOUR-TOKEN -e OPENP2P_NODE=YOUR-NODE-NAME openp2pcn/openp2p-client:latest docker run -d --restart=always --net host --name openp2p-client -e OPENP2P_TOKEN=YOUR-TOKEN -e OPENP2P_NODE=YOUR-NODE-NAME openp2pcn/openp2p-client:latest
OR OR
docker run -d --restart=always --net host --name openp2p-client openp2pcn/openp2p-client:latest -token YOUR-TOKEN -node YOUR-NODE-NAME docker run -d --restart=always --net host --name openp2p-client openp2pcn/openp2p-client:latest -token YOUR-TOKEN -node YOUR-NODE-NAME
``` ```
+108 -108
View File
@@ -1,109 +1,109 @@
# Parameters details # Parameters details
In most cases, you can operate it through <https://console.openp2p.cn>. In some cases it is necessary to run manually In most cases, you can operate it through <https://console.openp2p.cn>. In some cases it is necessary to run manually
> :warning: all commands in this doc, Windows env uses "openp2p.exe", Linux env uses "./openp2p" > :warning: all commands in this doc, Windows env uses "openp2p.exe", Linux env uses "./openp2p"
## Install and Listen ## Install and Listen
``` ```
./openp2p install -node OFFICEPC1 -token TOKEN ./openp2p install -node OFFICEPC1 -token TOKEN
Or Or
./openp2p -d -node OFFICEPC1 -token TOKEN ./openp2p -d -node OFFICEPC1 -token TOKEN
``` ```
>* install: [recommand] will install as system service. So it will autorun when system booting. >* install: [recommand] will install as system service. So it will autorun when system booting.
>* -d: daemon mode run once. When the worker process is found to exit unexpectedly, a new worker process will be automatically started >* -d: daemon mode run once. When the worker process is found to exit unexpectedly, a new worker process will be automatically started
>* -node: Unique node name, unique identification >* -node: Unique node name, unique identification
>* -token: See <console.openp2p.cn> "Profile" >* -token: See <console.openp2p.cn> "Profile"
>* -sharebandwidth: Provides bandwidth when used as a shared node, the default is 10mbps. If it is a large bandwidth of optical fiber, the larger the setting, the better the effect. 0 means not shared, the node is only used in a private P2P network. Do not join the shared P2P network, which also means that you CAN NOT use other peoples shared nodes >* -sharebandwidth: Provides bandwidth when used as a shared node, the default is 10mbps. If it is a large bandwidth of optical fiber, the larger the setting, the better the effect. 0 means not shared, the node is only used in a private P2P network. Do not join the shared P2P network, which also means that you CAN NOT use other peoples shared nodes
>* -loglevel: Need to view more debug logs, set 0; the default is 1 >* -loglevel: Need to view more debug logs, set 0; the default is 1
### Run in Docker container ### Run in Docker container
We don't provide official docker image yet, you can run it in any container We don't provide official docker image yet, you can run it in any container
``` ```
nohup ./openp2p -d -node OFFICEPC1 -token TOKEN & nohup ./openp2p -d -node OFFICEPC1 -token TOKEN &
# Since many docker images have been simplified, the install system service will fail, so the daemon mode is used to run in the background # Since many docker images have been simplified, the install system service will fail, so the daemon mode is used to run in the background
``` ```
## Connect ## Connect
``` ```
./openp2p -d -node HOMEPC123 -token TOKEN -appname OfficeWindowsRemote -peernode OFFICEPC1 -dstip 127.0.0.1 -dstport 3389 -srcport 23389 ./openp2p -d -node HOMEPC123 -token TOKEN -appname OfficeWindowsRemote -peernode OFFICEPC1 -dstip 127.0.0.1 -dstport 3389 -srcport 23389
Create multiple P2PApp by config file Create multiple P2PApp by config file
./openp2p -d ./openp2p -d
``` ```
>* -appname: This P2PApp name >* -appname: This P2PApp name
>* -peernode: Target node name >* -peernode: Target node name
>* -dstip: Target service address, default local 127.0.0.1 >* -dstip: Target service address, default local 127.0.0.1
>* -dstport: Target service port, such as windows remote desktop 3389, Linux ssh 22 >* -dstport: Target service port, such as windows remote desktop 3389, Linux ssh 22
>* -protocol: Target service protocol tcp, udp >* -protocol: Target service protocol tcp, udp
## Config file ## Config file
Generally saved in the current directory, in installation mode it will be saved to `C:\Program Files\OpenP2P\config.json` or `/usr/local/openp2p/config.json` Generally saved in the current directory, in installation mode it will be saved to `C:\Program Files\OpenP2P\config.json` or `/usr/local/openp2p/config.json`
If you want to modify the parameters, or configure multiple P2PApps, you can manually modify the configuration file If you want to modify the parameters, or configure multiple P2PApps, you can manually modify the configuration file
Configuration example Configuration example
``` ```
{ {
"network": { "network": {
"Node": "YOUR-NODE-NAME", "Node": "YOUR-NODE-NAME",
"Token": "TOKEN", "Token": "TOKEN",
"ShareBandwidth": 0, "ShareBandwidth": 0,
"ServerHost": "api.openp2p.cn", "ServerHost": "api.openp2p.cn",
"ServerPort": 27183, "ServerPort": 27183,
"UDPPort1": 27182, "UDPPort1": 27182,
"UDPPort2": 27183 "UDPPort2": 27183
}, },
"apps": [ "apps": [
{ {
"AppName": "OfficeWindowsPC", "AppName": "OfficeWindowsPC",
"Protocol": "tcp", "Protocol": "tcp",
"SrcPort": 23389, "SrcPort": 23389,
"PeerNode": "OFFICEPC1", "PeerNode": "OFFICEPC1",
"DstPort": 3389, "DstPort": 3389,
"DstHost": "localhost", "DstHost": "localhost",
}, },
{ {
"AppName": "OfficeServerSSH", "AppName": "OfficeServerSSH",
"Protocol": "tcp", "Protocol": "tcp",
"SrcPort": 22, "SrcPort": 22,
"PeerNode": "OFFICEPC1", "PeerNode": "OFFICEPC1",
"DstPort": 22, "DstPort": 22,
"DstHost": "192.168.1.5", "DstHost": "192.168.1.5",
} }
] ]
} }
``` ```
## Client update ## Client update
``` ```
# update local client # update local client
./openp2p update ./openp2p update
# update remote client # update remote client
curl --insecure 'https://api.openp2p.cn:27183/api/v1/device/YOUR-NODE-NAME/update?user=&password=' curl --insecure 'https://api.openp2p.cn:27183/api/v1/device/YOUR-NODE-NAME/update?user=&password='
``` ```
Windows system needs to set up firewall for this program, the program will automatically set the firewall, if the setting fails, the UDP punching will be affected. Windows system needs to set up firewall for this program, the program will automatically set the firewall, if the setting fails, the UDP punching will be affected.
The default firewall configuration of Linux system (Ubuntu and CentOS7) will not have any effect, if not, you can try to turn off the firewall The default firewall configuration of Linux system (Ubuntu and CentOS7) will not have any effect, if not, you can try to turn off the firewall
``` ```
systemctl stop firewalld.service systemctl stop firewalld.service
systemctl start firewalld.service systemctl start firewalld.service
firewall-cmd --state firewall-cmd --state
``` ```
## Uninstall ## Uninstall
``` ```
./openp2p uninstall ./openp2p uninstall
# when already installed # when already installed
# windows # windows
C:\Program Files\OpenP2P\openp2p.exe uninstall C:\Program Files\OpenP2P\openp2p.exe uninstall
# linux,macos # linux,macos
sudo /usr/local/openp2p/openp2p uninstall sudo /usr/local/openp2p/openp2p uninstall
``` ```
## Run with Docker ## Run with Docker
``` ```
# Replace YOUR-TOKEN and YOUR-NODE-NAME with yours # Replace YOUR-TOKEN and YOUR-NODE-NAME with yours
docker run -d --net host --name openp2p-client -e OPENP2P_TOKEN=YOUR-TOKEN -e OPENP2P_NODE=YOUR-NODE-NAME openp2pcn/openp2p-client:latest docker run -d --net host --name openp2p-client -e OPENP2P_TOKEN=YOUR-TOKEN -e OPENP2P_NODE=YOUR-NODE-NAME openp2pcn/openp2p-client:latest
OR OR
docker run -d --net host --name openp2p-client openp2pcn/openp2p-client:latest -token YOUR-TOKEN -node YOUR-NODE-NAME docker run -d --net host --name openp2p-client openp2pcn/openp2p-client:latest -token YOUR-TOKEN -node YOUR-NODE-NAME
``` ```
+3 -2
View File
@@ -2,9 +2,10 @@
depends on openjdk 11, gradle 8.1.3, ndk 21 depends on openjdk 11, gradle 8.1.3, ndk 21
``` ```
go install golang.org/x/mobile/cmd/gomobile@latest # latest version not support go1.20
go install golang.org/x/mobile/cmd/gomobile@7c4916698cc93475ebfea76748ee0faba2deb2a5
gomobile init gomobile init
go get -v golang.org/x/mobile/bind go get -v golang.org/x/mobile/bind@7c4916698cc93475ebfea76748ee0faba2deb2a5
cd core cd core
gomobile bind -target android -v gomobile bind -target android -v
if [[ $? -ne 0 ]]; then if [[ $? -ne 0 ]]; then
@@ -203,7 +203,8 @@ class OpenP2PService : VpnService() {
val network = Network(id, name, gateway, nodeList) val network = Network(id, name, gateway, nodeList)
println(network) println(network)
Log.i(OpenP2PService.LOG_TAG, "onBind"); Log.i(OpenP2PService.LOG_TAG, "onBind");
builder.addDnsServer("8.8.8.8") builder.addDnsServer("223.5.5.5")
builder.addDnsServer("2400:3200::1") // alicloud dns v6 & v4
builder.addRoute("10.2.3.0", 24) builder.addRoute("10.2.3.0", 24)
// builder.addRoute("0.0.0.0", 0); // builder.addRoute("0.0.0.0", 0);
builder.setSession(LOG_TAG!!) builder.setSession(LOG_TAG!!)
+9 -9
View File
@@ -1,9 +1,9 @@
package main package main
import ( import (
op "openp2p/core" op "openp2p/core"
) )
func main() { func main() {
op.Run() op.Run()
} }
+13
View File
@@ -1,6 +1,7 @@
package openp2p package openp2p
import ( import (
"fmt"
"log" "log"
"testing" "testing"
) )
@@ -114,3 +115,15 @@ func TestIsIPv6(t *testing.T) {
} }
} }
} }
func TestNodeID(t *testing.T) {
node1 := "n1-stable"
node2 := "tony-stable"
nodeID1 := NodeNameToID(node1)
nodeID2 := NodeNameToID(node2)
if nodeID1 < nodeID2 {
fmt.Printf("%s < %s\n", node1, node2)
} else {
fmt.Printf("%s >= %s\n", node1, node2)
}
}
+39 -22
View File
@@ -3,6 +3,7 @@ package openp2p
import ( import (
"encoding/json" "encoding/json"
"flag" "flag"
"fmt"
"os" "os"
"strconv" "strconv"
"strings" "strings"
@@ -28,6 +29,7 @@ type AppConfig struct {
ForceRelay int // default:0 disable;1 enable ForceRelay int // default:0 disable;1 enable
Enabled int // default:1 Enabled int // default:1
// runtime info // runtime info
relayMode string // private|public
peerVersion string peerVersion string
peerToken uint64 peerToken uint64
peerNatType int peerNatType int
@@ -64,17 +66,25 @@ func (c *AppConfig) ID() uint64 {
return uint64(c.SrcPort)*10 + 1 return uint64(c.SrcPort)*10 + 1
} }
func (c *AppConfig) LogPeerNode() string {
if c.relayMode == "public" { // memapp
return fmt.Sprintf("%d", NodeNameToID(c.PeerNode))
}
return c.PeerNode
}
type Config struct { type Config struct {
Network NetworkConfig `json:"network"` Network NetworkConfig `json:"network"`
Apps []*AppConfig `json:"apps"` Apps []*AppConfig `json:"apps"`
LogLevel int LogLevel int
MaxLogSize int
daemonMode bool daemonMode bool
mtx sync.Mutex mtx sync.Mutex
sdwanMtx sync.Mutex sdwanMtx sync.Mutex
sdwan SDWANInfo sdwan SDWANInfo
delNodes []SDWANNode delNodes []*SDWANNode
addNodes []SDWANNode addNodes []*SDWANNode
} }
func (c *Config) getSDWAN() SDWANInfo { func (c *Config) getSDWAN() SDWANInfo {
@@ -83,23 +93,30 @@ func (c *Config) getSDWAN() SDWANInfo {
return c.sdwan return c.sdwan
} }
func (c *Config) getDelNodes() []SDWANNode { func (c *Config) getDelNodes() []*SDWANNode {
c.sdwanMtx.Lock() c.sdwanMtx.Lock()
defer c.sdwanMtx.Unlock() defer c.sdwanMtx.Unlock()
return c.delNodes return c.delNodes
} }
func (c *Config) getAddNodes() []SDWANNode { func (c *Config) getAddNodes() []*SDWANNode {
c.sdwanMtx.Lock() c.sdwanMtx.Lock()
defer c.sdwanMtx.Unlock() defer c.sdwanMtx.Unlock()
return c.addNodes return c.addNodes
} }
func (c *Config) resetSDWAN() {
c.sdwanMtx.Lock()
defer c.sdwanMtx.Unlock()
c.delNodes = []*SDWANNode{}
c.addNodes = []*SDWANNode{}
c.sdwan = SDWANInfo{}
}
func (c *Config) setSDWAN(s SDWANInfo) { func (c *Config) setSDWAN(s SDWANInfo) {
c.sdwanMtx.Lock() c.sdwanMtx.Lock()
defer c.sdwanMtx.Unlock() defer c.sdwanMtx.Unlock()
// get old-new // get old-new
c.delNodes = []SDWANNode{} c.delNodes = []*SDWANNode{}
for _, oldNode := range c.sdwan.Nodes { for _, oldNode := range c.sdwan.Nodes {
isDeleted := true isDeleted := true
for _, newNode := range s.Nodes { for _, newNode := range s.Nodes {
@@ -113,7 +130,7 @@ func (c *Config) setSDWAN(s SDWANInfo) {
} }
} }
// get new-old // get new-old
c.addNodes = []SDWANNode{} c.addNodes = []*SDWANNode{}
for _, newNode := range s.Nodes { for _, newNode := range s.Nodes {
isNew := true isNew := true
for _, oldNode := range c.sdwan.Nodes { for _, oldNode := range c.sdwan.Nodes {
@@ -147,7 +164,7 @@ func (c *Config) retryApp(peerNode string) {
GNetwork.apps.Range(func(id, i interface{}) bool { GNetwork.apps.Range(func(id, i interface{}) bool {
app := i.(*p2pApp) app := i.(*p2pApp)
if app.config.PeerNode == peerNode { if app.config.PeerNode == peerNode {
gLog.Println(LvDEBUG, "retry app ", peerNode) gLog.Println(LvDEBUG, "retry app ", app.config.LogPeerNode())
app.config.retryNum = 0 app.config.retryNum = 0
app.config.nextRetryTime = time.Now() app.config.nextRetryTime = time.Now()
app.retryRelayNum = 0 app.retryRelayNum = 0
@@ -157,7 +174,7 @@ func (c *Config) retryApp(peerNode string) {
app.hbMtx.Unlock() app.hbMtx.Unlock()
} }
if app.config.RelayNode == peerNode { if app.config.RelayNode == peerNode {
gLog.Println(LvDEBUG, "retry app ", peerNode) gLog.Println(LvDEBUG, "retry app ", app.config.LogPeerNode())
app.retryRelayNum = 0 app.retryRelayNum = 0
app.nextRetryRelayTime = time.Now() app.nextRetryRelayTime = time.Now()
app.hbMtx.Lock() app.hbMtx.Lock()
@@ -171,7 +188,7 @@ func (c *Config) retryApp(peerNode string) {
func (c *Config) retryAllApp() { func (c *Config) retryAllApp() {
GNetwork.apps.Range(func(id, i interface{}) bool { GNetwork.apps.Range(func(id, i interface{}) bool {
app := i.(*p2pApp) app := i.(*p2pApp)
gLog.Println(LvDEBUG, "retry app ", app.config.PeerNode) gLog.Println(LvDEBUG, "retry app ", app.config.LogPeerNode())
app.config.retryNum = 0 app.config.retryNum = 0
app.config.nextRetryTime = time.Now() app.config.nextRetryTime = time.Now()
app.retryRelayNum = 0 app.retryRelayNum = 0
@@ -189,7 +206,7 @@ func (c *Config) retryAllMemApp() {
if app.config.SrcPort != 0 { if app.config.SrcPort != 0 {
return true return true
} }
gLog.Println(LvDEBUG, "retry app ", app.config.PeerNode) gLog.Println(LvDEBUG, "retry app ", app.config.LogPeerNode())
app.config.retryNum = 0 app.config.retryNum = 0
app.config.nextRetryTime = time.Now() app.config.nextRetryTime = time.Now()
app.retryRelayNum = 0 app.retryRelayNum = 0
@@ -221,17 +238,8 @@ func (c *Config) delete(app AppConfig) {
defer c.mtx.Unlock() defer c.mtx.Unlock()
defer c.save() defer c.save()
for i := 0; i < len(c.Apps); i++ { for i := 0; i < len(c.Apps); i++ {
got := false if (app.SrcPort != 0 && c.Apps[i].Protocol == app.Protocol && c.Apps[i].SrcPort == app.SrcPort) || // normal app
if app.SrcPort != 0 { // normal p2papp (app.SrcPort == 0 && c.Apps[i].SrcPort == 0 && c.Apps[i].PeerNode == app.PeerNode) { // memapp
if c.Apps[i].Protocol == app.Protocol && c.Apps[i].SrcPort == app.SrcPort {
got = true
}
} else { // memapp
if c.Apps[i].PeerNode == app.PeerNode {
got = true
}
}
if got {
if i == len(c.Apps)-1 { if i == len(c.Apps)-1 {
c.Apps = c.Apps[:i] c.Apps = c.Apps[:i]
} else { } else {
@@ -240,12 +248,14 @@ func (c *Config) delete(app AppConfig) {
return return
} }
} }
} }
func (c *Config) save() { func (c *Config) save() {
// c.mtx.Lock() // c.mtx.Lock()
// defer c.mtx.Unlock() // internal call // defer c.mtx.Unlock() // internal call
if c.Network.Token == 0 {
return
}
data, _ := json.MarshalIndent(c, "", " ") data, _ := json.MarshalIndent(c, "", " ")
err := os.WriteFile("config.json", data, 0644) err := os.WriteFile("config.json", data, 0644)
if err != nil { if err != nil {
@@ -256,6 +266,9 @@ func (c *Config) save() {
func (c *Config) saveCache() { func (c *Config) saveCache() {
// c.mtx.Lock() // c.mtx.Lock()
// defer c.mtx.Unlock() // internal call // defer c.mtx.Unlock() // internal call
if c.Network.Token == 0 {
return
}
data, _ := json.MarshalIndent(c, "", " ") data, _ := json.MarshalIndent(c, "", " ")
err := os.WriteFile("config.json0", data, 0644) err := os.WriteFile("config.json0", data, 0644)
if err != nil { if err != nil {
@@ -265,6 +278,7 @@ func (c *Config) saveCache() {
func init() { func init() {
gConf.LogLevel = int(LvINFO) gConf.LogLevel = int(LvINFO)
gConf.MaxLogSize = 1024 * 1024
gConf.Network.ShareBandwidth = 10 gConf.Network.ShareBandwidth = 10
gConf.Network.ServerHost = "api.openp2p.cn" gConf.Network.ServerHost = "api.openp2p.cn"
gConf.Network.ServerPort = WsPort gConf.Network.ServerPort = WsPort
@@ -448,6 +462,9 @@ func parseParams(subCommand string, cmd string) {
if f.Name == "loglevel" { if f.Name == "loglevel" {
gConf.LogLevel = *logLevel gConf.LogLevel = *logLevel
} }
if f.Name == "maxlogsize" {
gConf.MaxLogSize = *maxLogSize
}
if f.Name == "tcpport" { if f.Name == "tcpport" {
gConf.Network.TCPPort = *tcpPort gConf.Network.TCPPort = *tcpPort
} }
+120 -115
View File
@@ -1,115 +1,120 @@
package openp2p package openp2p
import ( import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"time" "time"
"github.com/openp2p-cn/service" "github.com/openp2p-cn/service"
) )
type daemon struct { type daemon struct {
running bool running bool
proc *os.Process proc *os.Process
} }
func (d *daemon) Start(s service.Service) error { func (d *daemon) Start(s service.Service) error {
gLog.Println(LvINFO, "daemon start") gLog.Println(LvINFO, "daemon start")
return nil return nil
} }
func (d *daemon) Stop(s service.Service) error { func (d *daemon) Stop(s service.Service) error {
gLog.Println(LvINFO, "service stop") gLog.Println(LvINFO, "service stop")
d.running = false d.running = false
if d.proc != nil { if d.proc != nil {
gLog.Println(LvINFO, "stop worker") gLog.Println(LvINFO, "stop worker")
d.proc.Kill() d.proc.Kill()
} }
if service.Interactive() { if service.Interactive() {
gLog.Println(LvINFO, "stop daemon") gLog.Println(LvINFO, "stop daemon")
os.Exit(0) os.Exit(0)
} }
return nil return nil
} }
func (d *daemon) run() { func (d *daemon) run() {
gLog.Println(LvINFO, "daemon run start") gLog.Println(LvINFO, "daemon run start")
defer gLog.Println(LvINFO, "daemon run end") defer gLog.Println(LvINFO, "daemon run end")
d.running = true d.running = true
binPath, _ := os.Executable() binPath, _ := os.Executable()
mydir, err := os.Getwd() mydir, err := os.Getwd()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
gLog.Println(LvINFO, mydir) gLog.Println(LvINFO, mydir)
conf := &service.Config{ conf := &service.Config{
Name: ProductName, Name: ProductName,
DisplayName: ProductName, DisplayName: ProductName,
Description: ProductName, Description: ProductName,
Executable: binPath, Executable: binPath,
} }
s, _ := service.New(d, conf) s, _ := service.New(d, conf)
go s.Run() go s.Run()
var args []string var args []string
// rm -d parameter // rm -d parameter
for i := 0; i < len(os.Args); i++ { for i := 0; i < len(os.Args); i++ {
if os.Args[i] == "-d" { if os.Args[i] == "-d" {
args = append(os.Args[0:i], os.Args[i+1:]...) args = append(os.Args[0:i], os.Args[i+1:]...)
break break
} }
} }
args = append(args, "-nv")
for { args = append(args, "-nv")
// start worker for {
tmpDump := filepath.Join("log", "dump.log.tmp") // start worker
dumpFile := filepath.Join("log", "dump.log") tmpDump := filepath.Join("log", "dump.log.tmp")
f, err := os.Create(filepath.Join(tmpDump)) dumpFile := filepath.Join("log", "dump.log")
if err != nil { f, err := os.Create(filepath.Join(tmpDump))
gLog.Printf(LvERROR, "start worker error:%s", err) if err != nil {
return gLog.Printf(LvERROR, "start worker error:%s", err)
} return
gLog.Println(LvINFO, "start worker process, args:", args) }
execSpec := &os.ProcAttr{Env: append(os.Environ(), "GOTRACEBACK=crash"), Files: []*os.File{os.Stdin, os.Stdout, f}} gLog.Println(LvINFO, "start worker process, args:", args)
p, err := os.StartProcess(binPath, args, execSpec) execSpec := &os.ProcAttr{Env: append(os.Environ(), "GOTRACEBACK=crash"), Files: []*os.File{os.Stdin, os.Stdout, f}}
if err != nil { lastRebootTime := time.Now()
gLog.Printf(LvERROR, "start worker error:%s", err) p, err := os.StartProcess(binPath, args, execSpec)
return if err != nil {
} gLog.Printf(LvERROR, "start worker error:%s", err)
d.proc = p return
_, _ = p.Wait() }
f.Close() d.proc = p
time.Sleep(time.Second) _, _ = p.Wait()
err = os.Rename(tmpDump, dumpFile) f.Close()
if err != nil { time.Sleep(time.Second)
gLog.Printf(LvERROR, "rename dump error:%s", err) err = os.Rename(tmpDump, dumpFile)
} if err != nil {
if !d.running { gLog.Printf(LvERROR, "rename dump error:%s", err)
return }
} if !d.running {
gLog.Printf(LvERROR, "worker stop, restart it after 10s") return
time.Sleep(time.Second * 10) }
} if time.Since(lastRebootTime) < time.Second*10 {
} gLog.Printf(LvERROR, "worker stop, restart it after 10s")
time.Sleep(time.Second * 10)
func (d *daemon) Control(ctrlComm string, exeAbsPath string, args []string) error { }
svcConfig := &service.Config{
Name: ProductName, }
DisplayName: ProductName, }
Description: ProductName,
Executable: exeAbsPath, func (d *daemon) Control(ctrlComm string, exeAbsPath string, args []string) error {
Arguments: args, svcConfig := &service.Config{
} Name: ProductName,
DisplayName: ProductName,
s, e := service.New(d, svcConfig) Description: ProductName,
if e != nil { Executable: exeAbsPath,
return e Arguments: args,
} }
e = service.Control(s, ctrlComm)
if e != nil { s, e := service.New(d, svcConfig)
return e if e != nil {
} return e
}
return nil e = service.Control(s, ctrlComm)
} if e != nil {
return e
}
return nil
}
+1
View File
@@ -29,4 +29,5 @@ var (
ErrPeerConnectRelay = errors.New("peer connect relayNode error") ErrPeerConnectRelay = errors.New("peer connect relayNode error")
ErrBuildTunnelBusy = errors.New("build tunnel busy") ErrBuildTunnelBusy = errors.New("build tunnel busy")
ErrMemAppTunnelNotFound = errors.New("memapp tunnel not found") ErrMemAppTunnelNotFound = errors.New("memapp tunnel not found")
ErrRemoteServiceUnable = errors.New("remote service unable")
) )
+482 -460
View File
@@ -1,460 +1,482 @@
package openp2p package openp2p
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "net"
"path/filepath" "os"
"reflect" "path/filepath"
"runtime" "reflect"
"time" "runtime"
"time"
"github.com/openp2p-cn/totp"
) "github.com/openp2p-cn/totp"
)
func handlePush(subType uint16, msg []byte) error {
pushHead := PushHeader{} func handlePush(subType uint16, msg []byte) error {
err := binary.Read(bytes.NewReader(msg[openP2PHeaderSize:openP2PHeaderSize+PushHeaderSize]), binary.LittleEndian, &pushHead) pushHead := PushHeader{}
if err != nil { err := binary.Read(bytes.NewReader(msg[openP2PHeaderSize:openP2PHeaderSize+PushHeaderSize]), binary.LittleEndian, &pushHead)
return err if err != nil {
} return err
gLog.Printf(LvDEBUG, "handle push msg type:%d, push header:%+v", subType, pushHead) }
switch subType { gLog.Printf(LvDEBUG, "handle push msg type:%d, push header:%+v", subType, pushHead)
case MsgPushConnectReq: switch subType {
err = handleConnectReq(msg) case MsgPushConnectReq:
case MsgPushRsp: err = handleConnectReq(msg)
rsp := PushRsp{} case MsgPushRsp:
if err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp); err != nil { rsp := PushRsp{}
gLog.Printf(LvERROR, "wrong pushRsp:%s", err) if err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp); err != nil {
return err gLog.Printf(LvERROR, "wrong pushRsp:%s", err)
} return err
if rsp.Error == 0 { }
gLog.Printf(LvDEBUG, "push ok, detail:%s", rsp.Detail) if rsp.Error == 0 {
} else { gLog.Printf(LvDEBUG, "push ok, detail:%s", rsp.Detail)
gLog.Printf(LvERROR, "push error:%d, detail:%s", rsp.Error, rsp.Detail) } else {
} gLog.Printf(LvERROR, "push error:%d, detail:%s", rsp.Error, rsp.Detail)
case MsgPushAddRelayTunnelReq: }
req := AddRelayTunnelReq{} case MsgPushAddRelayTunnelReq:
if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil { req := AddRelayTunnelReq{}
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err) if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil {
return err gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
} return err
config := AppConfig{} }
config.PeerNode = req.RelayName config := AppConfig{}
config.peerToken = req.RelayToken config.PeerNode = req.RelayName
go func(r AddRelayTunnelReq) { config.peerToken = req.RelayToken
t, errDt := GNetwork.addDirectTunnel(config, 0) config.relayMode = req.RelayMode
if errDt == nil { go func(r AddRelayTunnelReq) {
// notify peer relay ready t, errDt := GNetwork.addDirectTunnel(config, 0)
msg := TunnelMsg{ID: t.id} if errDt == nil {
GNetwork.push(r.From, MsgPushAddRelayTunnelRsp, msg) // notify peer relay ready
appConfig := config msg := TunnelMsg{ID: t.id}
appConfig.PeerNode = req.From GNetwork.push(r.From, MsgPushAddRelayTunnelRsp, msg)
} else { appConfig := config
gLog.Printf(LvERROR, "addDirectTunnel error:%s", errDt) appConfig.PeerNode = req.From
GNetwork.push(r.From, MsgPushAddRelayTunnelRsp, "error") // compatible with old version client, trigger unmarshal error } else {
} gLog.Printf(LvERROR, "addDirectTunnel error:%s", errDt)
}(req) GNetwork.push(r.From, MsgPushAddRelayTunnelRsp, "error") // compatible with old version client, trigger unmarshal error
case MsgPushServerSideSaveMemApp: }
req := ServerSideSaveMemApp{} }(req)
if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil { case MsgPushServerSideSaveMemApp:
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err) req := ServerSideSaveMemApp{}
return err if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil {
} gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
gLog.Println(LvDEBUG, "handle MsgPushServerSideSaveMemApp:", prettyJson(req)) return err
var existTunnel *P2PTunnel }
i, ok := GNetwork.allTunnels.Load(req.TunnelID) gLog.Println(LvDEBUG, "handle MsgPushServerSideSaveMemApp:", prettyJson(req))
if !ok { var existTunnel *P2PTunnel
time.Sleep(time.Millisecond * 100) i, ok := GNetwork.allTunnels.Load(req.TunnelID)
i, ok = GNetwork.allTunnels.Load(req.TunnelID) // retry sometimes will receive MsgPushServerSideSaveMemApp but p2ptunnel not store yet. if !ok {
if !ok { time.Sleep(time.Millisecond * 100)
gLog.Println(LvERROR, "handle MsgPushServerSideSaveMemApp error:", ErrMemAppTunnelNotFound) i, ok = GNetwork.allTunnels.Load(req.TunnelID) // retry sometimes will receive MsgPushServerSideSaveMemApp but p2ptunnel not store yet.
return ErrMemAppTunnelNotFound if !ok {
} gLog.Println(LvERROR, "handle MsgPushServerSideSaveMemApp error:", ErrMemAppTunnelNotFound)
} return ErrMemAppTunnelNotFound
existTunnel = i.(*P2PTunnel) }
peerID := NodeNameToID(req.From) }
existApp, appok := GNetwork.apps.Load(peerID) existTunnel = i.(*P2PTunnel)
if appok { peerID := NodeNameToID(req.From)
app := existApp.(*p2pApp) existApp, appok := GNetwork.apps.Load(peerID)
app.config.AppName = fmt.Sprintf("%d", peerID) if appok {
app.id = req.AppID app := existApp.(*p2pApp)
app.setRelayTunnelID(req.RelayTunnelID) app.config.AppName = fmt.Sprintf("%d", peerID)
app.relayMode = req.RelayMode app.id = req.AppID
app.hbTimeRelay = time.Now() app.setRelayTunnelID(req.RelayTunnelID)
if req.RelayTunnelID == 0 { app.relayMode = req.RelayMode
app.setDirectTunnel(existTunnel) app.hbTimeRelay = time.Now()
} else { if req.RelayTunnelID == 0 {
app.setRelayTunnel(existTunnel) app.setDirectTunnel(existTunnel)
} } else {
gLog.Println(LvDEBUG, "find existing memapp, update it") app.setRelayTunnel(existTunnel)
} else { }
appConfig := existTunnel.config gLog.Println(LvDEBUG, "find existing memapp, update it")
appConfig.SrcPort = 0 } else {
appConfig.Protocol = "" appConfig := existTunnel.config
appConfig.AppName = fmt.Sprintf("%d", peerID) appConfig.SrcPort = 0
appConfig.PeerNode = req.From appConfig.Protocol = ""
app := p2pApp{ appConfig.AppName = fmt.Sprintf("%d", peerID)
id: req.AppID, appConfig.PeerNode = req.From
config: appConfig, app := p2pApp{
relayMode: req.RelayMode, id: req.AppID,
running: true, config: appConfig,
hbTimeRelay: time.Now(), relayMode: req.RelayMode,
} running: true,
if req.RelayTunnelID == 0 { hbTimeRelay: time.Now(),
app.setDirectTunnel(existTunnel) }
} else { if req.RelayTunnelID == 0 {
app.setRelayTunnel(existTunnel) app.setDirectTunnel(existTunnel)
app.setRelayTunnelID(req.RelayTunnelID) } else {
} app.setRelayTunnel(existTunnel)
if req.RelayTunnelID != 0 { app.setRelayTunnelID(req.RelayTunnelID)
app.relayNode = req.Node }
} if req.RelayTunnelID != 0 {
GNetwork.apps.Store(NodeNameToID(req.From), &app) app.relayNode = req.Node
} }
GNetwork.apps.Store(NodeNameToID(req.From), &app)
return nil }
case MsgPushAPPKey:
req := APPKeySync{} return nil
if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil { case MsgPushAPPKey:
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err) req := APPKeySync{}
return err if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil {
} gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
SaveKey(req.AppID, req.AppKey) return err
case MsgPushUpdate: }
gLog.Println(LvINFO, "MsgPushUpdate") SaveKey(req.AppID, req.AppKey)
err := update(gConf.Network.ServerHost, gConf.Network.ServerPort) case MsgPushUpdate:
if err == nil { gLog.Println(LvINFO, "MsgPushUpdate")
os.Exit(0) err := update(gConf.Network.ServerHost, gConf.Network.ServerPort)
} if err == nil {
return err os.Exit(0)
case MsgPushRestart: }
gLog.Println(LvINFO, "MsgPushRestart") return err
os.Exit(0) case MsgPushRestart:
return err gLog.Println(LvINFO, "MsgPushRestart")
case MsgPushReportApps: os.Exit(0)
err = handleReportApps() return err
case MsgPushReportMemApps: case MsgPushReportApps:
err = handleReportMemApps() err = handleReportApps()
case MsgPushReportLog: case MsgPushReportMemApps:
err = handleLog(msg) err = handleReportMemApps()
case MsgPushReportGoroutine: case MsgPushReportLog:
err = handleReportGoroutine() err = handleLog(msg)
case MsgPushEditApp: case MsgPushReportGoroutine:
err = handleEditApp(msg) err = handleReportGoroutine()
case MsgPushEditNode: case MsgPushCheckRemoteService:
gLog.Println(LvINFO, "MsgPushEditNode") err = handleCheckRemoteService(msg)
req := EditNode{} case MsgPushEditApp:
if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil { err = handleEditApp(msg)
gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:])) case MsgPushEditNode:
return err gLog.Println(LvINFO, "MsgPushEditNode")
} req := EditNode{}
gConf.setNode(req.NewName) if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil {
gConf.setShareBandwidth(req.Bandwidth) gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:]))
os.Exit(0) return err
case MsgPushSwitchApp: }
gLog.Println(LvINFO, "MsgPushSwitchApp") gConf.setNode(req.NewName)
app := AppInfo{} gConf.setShareBandwidth(req.Bandwidth)
if err = json.Unmarshal(msg[openP2PHeaderSize:], &app); err != nil { os.Exit(0)
gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(app), err, string(msg[openP2PHeaderSize:])) case MsgPushSwitchApp:
return err gLog.Println(LvINFO, "MsgPushSwitchApp")
} app := AppInfo{}
config := AppConfig{Enabled: app.Enabled, SrcPort: app.SrcPort, Protocol: app.Protocol} if err = json.Unmarshal(msg[openP2PHeaderSize:], &app); err != nil {
gLog.Println(LvINFO, app.AppName, " switch to ", app.Enabled) gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(app), err, string(msg[openP2PHeaderSize:]))
gConf.switchApp(config, app.Enabled) return err
if app.Enabled == 0 { }
// disable APP config := AppConfig{Enabled: app.Enabled, SrcPort: app.SrcPort, Protocol: app.Protocol}
GNetwork.DeleteApp(config) gLog.Println(LvINFO, app.AppName, " switch to ", app.Enabled)
} gConf.switchApp(config, app.Enabled)
case MsgPushDstNodeOnline: if app.Enabled == 0 {
gLog.Println(LvINFO, "MsgPushDstNodeOnline") // disable APP
req := PushDstNodeOnline{} GNetwork.DeleteApp(config)
if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil { }
gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:])) case MsgPushDstNodeOnline:
return err gLog.Println(LvINFO, "MsgPushDstNodeOnline")
} req := PushDstNodeOnline{}
gLog.Println(LvINFO, "retry peerNode ", req.Node) if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil {
gConf.retryApp(req.Node) gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:]))
default: return err
i, ok := GNetwork.msgMap.Load(pushHead.From) }
if !ok { gLog.Println(LvINFO, "retry peerNode ", req.Node)
return ErrMsgChannelNotFound gConf.retryApp(req.Node)
} default:
ch := i.(chan msgCtx) i, ok := GNetwork.msgMap.Load(pushHead.From)
ch <- msgCtx{data: msg, ts: time.Now()} if !ok {
} return ErrMsgChannelNotFound
return err }
} ch := i.(chan msgCtx)
ch <- msgCtx{data: msg, ts: time.Now()}
func handleEditApp(msg []byte) (err error) { }
gLog.Println(LvINFO, "MsgPushEditApp") return err
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:])) func handleEditApp(msg []byte) (err error) {
return err gLog.Println(LvINFO, "MsgPushEditApp")
} newApp := AppInfo{}
oldConf := AppConfig{Enabled: 1} if err = json.Unmarshal(msg[openP2PHeaderSize:], &newApp); err != nil {
// protocol0+srcPort0 exist, delApp gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(newApp), err, string(msg[openP2PHeaderSize:]))
oldConf.AppName = newApp.AppName return err
oldConf.Protocol = newApp.Protocol0 }
oldConf.Whitelist = newApp.Whitelist oldConf := AppConfig{Enabled: 1}
oldConf.SrcPort = newApp.SrcPort0 // protocol0+srcPort0 exist, delApp
oldConf.PeerNode = newApp.PeerNode oldConf.AppName = newApp.AppName
oldConf.DstHost = newApp.DstHost oldConf.Protocol = newApp.Protocol0
oldConf.DstPort = newApp.DstPort oldConf.Whitelist = newApp.Whitelist
if newApp.Protocol0 != "" && newApp.SrcPort0 != 0 { // not edit oldConf.SrcPort = newApp.SrcPort0
gConf.delete(oldConf) oldConf.PeerNode = newApp.PeerNode
} oldConf.DstHost = newApp.DstHost
oldConf.DstPort = newApp.DstPort
// AddApp if newApp.Protocol0 != "" && newApp.SrcPort0 != 0 { // not edit
newConf := oldConf gConf.delete(oldConf)
newConf.Protocol = newApp.Protocol }
newConf.SrcPort = newApp.SrcPort
newConf.RelayNode = newApp.SpecRelayNode // AddApp
newConf.PunchPriority = newApp.PunchPriority newConf := oldConf
gConf.add(newConf, false) newConf.Protocol = newApp.Protocol
if newApp.Protocol0 != "" && newApp.SrcPort0 != 0 { // not edit newConf.SrcPort = newApp.SrcPort
GNetwork.DeleteApp(oldConf) // DeleteApp may cost some times, execute at the end newConf.RelayNode = newApp.SpecRelayNode
} newConf.PunchPriority = newApp.PunchPriority
return nil gConf.add(newConf, false)
} if newApp.Protocol0 != "" && newApp.SrcPort0 != 0 { // not edit
GNetwork.DeleteApp(oldConf) // DeleteApp may cost some times, execute at the end
func handleConnectReq(msg []byte) (err error) { }
req := PushConnectReq{} return nil
if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil { }
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
return err func handleConnectReq(msg []byte) (err error) {
} req := PushConnectReq{}
gLog.Printf(LvDEBUG, "%s is connecting...", req.From) if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil {
gLog.Println(LvDEBUG, "push connect response to ", req.From) gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
if compareVersion(req.Version, LeastSupportVersion) < 0 { return err
gLog.Println(LvERROR, ErrVersionNotCompatible.Error(), ":", req.From) }
rsp := PushConnectRsp{ gLog.Printf(LvDEBUG, "%s is connecting...", req.From)
Error: 10, gLog.Println(LvDEBUG, "push connect response to ", req.From)
Detail: ErrVersionNotCompatible.Error(), if compareVersion(req.Version, LeastSupportVersion) < 0 {
To: req.From, gLog.Println(LvERROR, ErrVersionNotCompatible.Error(), ":", req.From)
From: gConf.Network.Node, rsp := PushConnectRsp{
} Error: 10,
GNetwork.push(req.From, MsgPushConnectRsp, rsp) Detail: ErrVersionNotCompatible.Error(),
return ErrVersionNotCompatible To: req.From,
} From: gConf.Network.Node,
// verify totp token or token }
t := totp.TOTP{Step: totp.RelayTOTPStep} GNetwork.push(req.From, MsgPushConnectRsp, rsp)
if t.Verify(req.Token, gConf.Network.Token, time.Now().Unix()-GNetwork.dt/int64(time.Second)) { // localTs may behind, auto adjust ts return ErrVersionNotCompatible
gLog.Printf(LvINFO, "Access Granted") }
config := AppConfig{} // verify totp token or token
config.peerNatType = req.NatType t := totp.TOTP{Step: totp.RelayTOTPStep}
config.peerConeNatPort = req.ConeNatPort if t.Verify(req.Token, gConf.Network.Token, time.Now().Unix()-GNetwork.dt/int64(time.Second)) { // localTs may behind, auto adjust ts
config.peerIP = req.FromIP gLog.Printf(LvINFO, "Access Granted")
config.PeerNode = req.From config := AppConfig{}
config.peerVersion = req.Version config.peerNatType = req.NatType
config.fromToken = req.Token config.peerConeNatPort = req.ConeNatPort
config.peerIPv6 = req.IPv6 config.peerIP = req.FromIP
config.hasIPv4 = req.HasIPv4 config.PeerNode = req.From
config.hasUPNPorNATPMP = req.HasUPNPorNATPMP config.peerVersion = req.Version
config.linkMode = req.LinkMode config.fromToken = req.Token
config.isUnderlayServer = req.IsUnderlayServer config.peerIPv6 = req.IPv6
config.UnderlayProtocol = req.UnderlayProtocol config.hasIPv4 = req.HasIPv4
// share relay node will limit bandwidth config.hasUPNPorNATPMP = req.HasUPNPorNATPMP
if req.Token != gConf.Network.Token { config.linkMode = req.LinkMode
gLog.Printf(LvINFO, "set share bandwidth %d mbps", gConf.Network.ShareBandwidth) config.isUnderlayServer = req.IsUnderlayServer
config.shareBandwidth = gConf.Network.ShareBandwidth config.UnderlayProtocol = req.UnderlayProtocol
} // share relay node will limit bandwidth
// go GNetwork.AddTunnel(config, req.ID) if req.Token != gConf.Network.Token {
go func() { gLog.Printf(LvINFO, "set share bandwidth %d mbps", gConf.Network.ShareBandwidth)
GNetwork.addDirectTunnel(config, req.ID) config.shareBandwidth = gConf.Network.ShareBandwidth
}() }
return nil // go GNetwork.AddTunnel(config, req.ID)
} go func() {
gLog.Println(LvERROR, "Access Denied:", req.From) GNetwork.addDirectTunnel(config, req.ID)
rsp := PushConnectRsp{ }()
Error: 1, return nil
Detail: fmt.Sprintf("connect to %s error: Access Denied", gConf.Network.Node), }
To: req.From, gLog.Println(LvERROR, "Access Denied:", req.From)
From: gConf.Network.Node, rsp := PushConnectRsp{
} Error: 1,
return GNetwork.push(req.From, MsgPushConnectRsp, rsp) Detail: fmt.Sprintf("connect to %s error: Access Denied", gConf.Network.Node),
} To: req.From,
From: gConf.Network.Node,
func handleReportApps() (err error) { }
gLog.Println(LvINFO, "MsgPushReportApps") return GNetwork.push(req.From, MsgPushConnectRsp, rsp)
req := ReportApps{} }
gConf.mtx.Lock()
defer gConf.mtx.Unlock() func handleReportApps() (err error) {
gLog.Println(LvINFO, "MsgPushReportApps")
for _, config := range gConf.Apps { req := ReportApps{}
appActive := 0 gConf.mtx.Lock()
relayNode := "" defer gConf.mtx.Unlock()
specRelayNode := ""
relayMode := "" for _, config := range gConf.Apps {
linkMode := LinkModeUDPPunch appActive := 0
var connectTime string relayNode := ""
var retryTime string specRelayNode := ""
var app *p2pApp relayMode := ""
i, ok := GNetwork.apps.Load(config.ID()) linkMode := LinkModeUDPPunch
if ok { var connectTime string
app = i.(*p2pApp) var retryTime string
if app.isActive() { var app *p2pApp
appActive = 1 i, ok := GNetwork.apps.Load(config.ID())
} if ok {
if app.config.SrcPort == 0 { // memapp app = i.(*p2pApp)
continue if app.isActive() {
} appActive = 1
specRelayNode = app.config.RelayNode }
if !app.isDirect() { // TODO: should always report relay node for app edit if app.config.SrcPort == 0 { // memapp
relayNode = app.relayNode continue
relayMode = app.relayMode }
} specRelayNode = app.config.RelayNode
if !app.isDirect() { // TODO: should always report relay node for app edit
if app.Tunnel() != nil { relayNode = app.relayNode
linkMode = app.Tunnel().linkModeWeb relayMode = app.relayMode
} }
retryTime = app.RetryTime().Local().Format("2006-01-02T15:04:05-0700")
connectTime = app.ConnectTime().Local().Format("2006-01-02T15:04:05-0700") if app.Tunnel() != nil {
linkMode = app.Tunnel().linkModeWeb
} }
appInfo := AppInfo{ retryTime = app.RetryTime().Local().Format("2006-01-02T15:04:05-0700")
AppName: config.AppName, connectTime = app.ConnectTime().Local().Format("2006-01-02T15:04:05-0700")
Error: config.errMsg,
Protocol: config.Protocol, }
PunchPriority: config.PunchPriority, appInfo := AppInfo{
Whitelist: config.Whitelist, AppName: config.AppName,
SrcPort: config.SrcPort, Error: config.errMsg,
RelayNode: relayNode, Protocol: config.Protocol,
SpecRelayNode: specRelayNode, PunchPriority: config.PunchPriority,
RelayMode: relayMode, Whitelist: config.Whitelist,
LinkMode: linkMode, SrcPort: config.SrcPort,
PeerNode: config.PeerNode, RelayNode: relayNode,
DstHost: config.DstHost, SpecRelayNode: specRelayNode,
DstPort: config.DstPort, RelayMode: relayMode,
PeerUser: config.PeerUser, LinkMode: linkMode,
PeerIP: config.peerIP, PeerNode: config.PeerNode,
PeerNatType: config.peerNatType, DstHost: config.DstHost,
RetryTime: retryTime, DstPort: config.DstPort,
ConnectTime: connectTime, PeerUser: config.PeerUser,
IsActive: appActive, PeerIP: config.peerIP,
Enabled: config.Enabled, PeerNatType: config.peerNatType,
} RetryTime: retryTime,
req.Apps = append(req.Apps, appInfo) ConnectTime: connectTime,
} IsActive: appActive,
return GNetwork.write(MsgReport, MsgReportApps, &req) Enabled: config.Enabled,
}
} req.Apps = append(req.Apps, appInfo)
}
func handleReportMemApps() (err error) { return GNetwork.write(MsgReport, MsgReportApps, &req)
gLog.Println(LvINFO, "handleReportMemApps")
req := ReportApps{} }
gConf.mtx.Lock()
defer gConf.mtx.Unlock() func handleReportMemApps() (err error) {
GNetwork.sdwan.sysRoute.Range(func(key, value interface{}) bool { gLog.Println(LvINFO, "handleReportMemApps")
node := value.(*sdwanNode) req := ReportApps{}
appActive := 0 gConf.mtx.Lock()
relayMode := "" defer gConf.mtx.Unlock()
var connectTime string GNetwork.sdwan.sysRoute.Range(func(key, value interface{}) bool {
var retryTime string node := value.(*sdwanNode)
appActive := 0
i, ok := GNetwork.apps.Load(node.id) relayMode := ""
var app *p2pApp var connectTime string
if ok { var retryTime string
app = i.(*p2pApp)
if app.isActive() { i, ok := GNetwork.apps.Load(node.id)
appActive = 1 var app *p2pApp
} if ok {
if !app.isDirect() { app = i.(*p2pApp)
relayMode = app.relayMode if app.isActive() {
} appActive = 1
retryTime = app.RetryTime().Local().Format("2006-01-02T15:04:05-0700") }
connectTime = app.ConnectTime().Local().Format("2006-01-02T15:04:05-0700") if !app.isDirect() {
} relayMode = app.relayMode
appInfo := AppInfo{ }
RelayMode: relayMode, retryTime = app.RetryTime().Local().Format("2006-01-02T15:04:05-0700")
PeerNode: node.name, connectTime = app.ConnectTime().Local().Format("2006-01-02T15:04:05-0700")
IsActive: appActive, }
Enabled: 1, appInfo := AppInfo{
} RelayMode: relayMode,
if app != nil { PeerNode: node.name,
appInfo.AppName = app.config.AppName IsActive: appActive,
appInfo.Error = app.config.errMsg Enabled: 1,
appInfo.Protocol = app.config.Protocol }
appInfo.Whitelist = app.config.Whitelist if app != nil {
appInfo.SrcPort = app.config.SrcPort appInfo.AppName = app.config.AppName
if !app.isDirect() { appInfo.Error = app.config.errMsg
appInfo.RelayNode = app.relayNode appInfo.Protocol = app.config.Protocol
} appInfo.Whitelist = app.config.Whitelist
appInfo.SrcPort = app.config.SrcPort
if app.Tunnel() != nil { if !app.isDirect() {
appInfo.LinkMode = app.Tunnel().linkModeWeb appInfo.RelayNode = app.relayNode
} }
appInfo.DstHost = app.config.DstHost
appInfo.DstPort = app.config.DstPort if app.Tunnel() != nil {
appInfo.PeerUser = app.config.PeerUser appInfo.LinkMode = app.Tunnel().linkModeWeb
appInfo.PeerIP = app.config.peerIP }
appInfo.PeerNatType = app.config.peerNatType appInfo.DstHost = app.config.DstHost
appInfo.RetryTime = retryTime appInfo.DstPort = app.config.DstPort
appInfo.ConnectTime = connectTime appInfo.PeerUser = app.config.PeerUser
} appInfo.PeerIP = app.config.peerIP
req.Apps = append(req.Apps, appInfo) appInfo.PeerNatType = app.config.peerNatType
return true appInfo.RetryTime = retryTime
}) appInfo.ConnectTime = connectTime
gLog.Println(LvDEBUG, "handleReportMemApps res:", prettyJson(req)) }
return GNetwork.write(MsgReport, MsgReportMemApps, &req) req.Apps = append(req.Apps, appInfo)
} return true
})
func handleLog(msg []byte) (err error) { gLog.Println(LvDEBUG, "handleReportMemApps res:", prettyJson(req))
gLog.Println(LvDEBUG, "MsgPushReportLog") return GNetwork.write(MsgReport, MsgReportMemApps, &req)
const defaultLen = 1024 * 128 }
const maxLen = 1024 * 1024
req := ReportLogReq{} func handleLog(msg []byte) (err error) {
if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil { gLog.Println(LvDEBUG, "MsgPushReportLog")
gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:])) const defaultLen = 1024 * 128
return err const maxLen = 1024 * 1024
} req := ReportLogReq{}
if req.FileName == "" { if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil {
req.FileName = "openp2p.log" gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:]))
} else { return err
req.FileName = sanitizeFileName(req.FileName) }
} if req.FileName == "" {
f, err := os.Open(filepath.Join("log", req.FileName)) req.FileName = "openp2p.log"
if err != nil { } else {
gLog.Println(LvERROR, "read log file error:", err) req.FileName = sanitizeFileName(req.FileName)
return err }
} f, err := os.Open(filepath.Join("log", req.FileName))
fi, err := f.Stat() if err != nil {
if err != nil { gLog.Println(LvERROR, "read log file error:", err)
return err return err
} }
if req.Offset > fi.Size() { fi, err := f.Stat()
req.Offset = fi.Size() - defaultLen if err != nil {
} return err
// verify input parameters }
if req.Offset < 0 { if req.Offset > fi.Size() {
req.Offset = 0 req.Offset = fi.Size() - defaultLen
} }
if req.Len <= 0 || req.Len > maxLen { // verify input parameters
req.Len = defaultLen if req.Offset < 0 {
} req.Offset = 0
}
f.Seek(req.Offset, 0) if req.Len <= 0 || req.Len > maxLen {
buff := make([]byte, req.Len) req.Len = defaultLen
readLength, err := f.Read(buff) }
f.Close()
if err != nil { f.Seek(req.Offset, 0)
gLog.Println(LvERROR, "read log content error:", err) buff := make([]byte, req.Len)
return err readLength, err := f.Read(buff)
} f.Close()
rsp := ReportLogRsp{} if err != nil {
rsp.Content = string(buff[:readLength]) gLog.Println(LvERROR, "read log content error:", err)
rsp.FileName = req.FileName return err
rsp.Total = fi.Size() }
rsp.Len = req.Len rsp := ReportLogRsp{}
return GNetwork.write(MsgReport, MsgPushReportLog, &rsp) rsp.Content = string(buff[:readLength])
} rsp.FileName = req.FileName
rsp.Total = fi.Size()
func handleReportGoroutine() (err error) { rsp.Len = req.Len
gLog.Println(LvDEBUG, "handleReportGoroutine") return GNetwork.write(MsgReport, MsgPushReportLog, &rsp)
buf := make([]byte, 1024*128) }
stackLen := runtime.Stack(buf, true)
return GNetwork.write(MsgReport, MsgPushReportLog, string(buf[:stackLen])) func handleReportGoroutine() (err error) {
} gLog.Println(LvDEBUG, "handleReportGoroutine")
buf := make([]byte, 1024*128)
stackLen := runtime.Stack(buf, true)
return GNetwork.write(MsgReport, MsgPushReportLog, string(buf[:stackLen]))
}
func handleCheckRemoteService(msg []byte) (err error) {
gLog.Println(LvDEBUG, "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:]))
return err
}
rsp := PushRsp{Error: 0}
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", req.Host, req.Port), time.Second*3)
if err != nil {
rsp.Error = 1
rsp.Detail = ErrRemoteServiceUnable.Error()
} else {
conn.Close()
}
return GNetwork.write(MsgReport, MsgReportResponse, rsp)
}
+15 -15
View File
@@ -13,12 +13,12 @@ import (
func handshakeC2C(t *P2PTunnel) (err error) { 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) 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") defer gLog.Printf(LvDEBUG, "handshakeC2C end")
conn, err := net.ListenUDP("udp", t.la) conn, err := net.ListenUDP("udp", t.localHoleAddr)
if err != nil { if err != nil {
return err return err
} }
defer conn.Close() defer conn.Close()
_, err = UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id}) _, err = UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id})
if err != nil { if err != nil {
gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshake error:", err) gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshake error:", err)
return err return err
@@ -28,7 +28,7 @@ func handshakeC2C(t *P2PTunnel) (err error) {
gLog.Println(LvDEBUG, "handshakeC2C read MsgPunchHandshake error:", err) gLog.Println(LvDEBUG, "handshakeC2C read MsgPunchHandshake error:", err)
return err return err
} }
t.ra, _ = net.ResolveUDPAddr("udp", ra.String()) t.remoteHoleAddr, _ = net.ResolveUDPAddr("udp", ra.String())
var tunnelID uint64 var tunnelID uint64
if len(buff) > openP2PHeaderSize { if len(buff) > openP2PHeaderSize {
req := P2PHandshakeReq{} req := P2PHandshakeReq{}
@@ -40,7 +40,7 @@ func handshakeC2C(t *P2PTunnel) (err error) {
} }
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake && tunnelID == t.id { if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake && tunnelID == t.id {
gLog.Printf(LvDEBUG, "read %d handshake ", t.id) gLog.Printf(LvDEBUG, "read %d handshake ", t.id)
UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id}) UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
_, head, _, _, err = UDPRead(conn, HandshakeTimeout) _, head, _, _, err = UDPRead(conn, HandshakeTimeout)
if err != nil { if err != nil {
gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err) gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err)
@@ -49,7 +49,7 @@ func handshakeC2C(t *P2PTunnel) (err error) {
} }
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck && tunnelID == t.id { if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck && tunnelID == t.id {
gLog.Printf(LvDEBUG, "read %d handshake ack ", t.id) gLog.Printf(LvDEBUG, "read %d handshake ack ", t.id)
_, err = UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id}) _, err = UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
if err != nil { if err != nil {
gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err) gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err)
return err return err
@@ -70,7 +70,7 @@ func handshakeC2S(t *P2PTunnel) error {
startTime := time.Now() startTime := time.Now()
r := rand.New(rand.NewSource(time.Now().UnixNano())) r := rand.New(rand.NewSource(time.Now().UnixNano()))
randPorts := r.Perm(65532) randPorts := r.Perm(65532)
conn, err := net.ListenUDP("udp", t.la) conn, err := net.ListenUDP("udp", t.localHoleAddr)
if err != nil { if err != nil {
return err return err
} }
@@ -111,7 +111,7 @@ func handshakeC2S(t *P2PTunnel) error {
gLog.Println(LvERROR, "parse p2pheader error:", err) gLog.Println(LvERROR, "parse p2pheader error:", err)
return err return err
} }
t.ra, _ = net.ResolveUDPAddr("udp", dst.String()) t.remoteHoleAddr, _ = net.ResolveUDPAddr("udp", dst.String())
var tunnelID uint64 var tunnelID uint64
if len(buff) > openP2PHeaderSize { if len(buff) > openP2PHeaderSize {
req := P2PHandshakeReq{} req := P2PHandshakeReq{}
@@ -123,7 +123,7 @@ func handshakeC2S(t *P2PTunnel) error {
} }
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake && tunnelID == t.id { if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake && tunnelID == t.id {
gLog.Printf(LvDEBUG, "handshakeC2S read %d handshake ", t.id) gLog.Printf(LvDEBUG, "handshakeC2S read %d handshake ", t.id)
UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id}) UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
for { for {
_, head, buff, _, err = UDPRead(conn, HandshakeTimeout) _, head, buff, _, err = UDPRead(conn, HandshakeTimeout)
if err != nil { if err != nil {
@@ -146,8 +146,8 @@ func handshakeC2S(t *P2PTunnel) error {
} }
} }
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck { if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
gLog.Printf(LvDEBUG, "handshakeC2S read %d handshake ack %s", t.id, t.ra.String()) gLog.Printf(LvDEBUG, "handshakeC2S read %d handshake ack %s", t.id, t.remoteHoleAddr.String())
_, err = UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id}) _, err = UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
return err return err
} else { } else {
gLog.Println(LvDEBUG, "handshakeS2C read msg but not MsgPunchHandshakeAck") gLog.Println(LvDEBUG, "handshakeS2C read msg but not MsgPunchHandshakeAck")
@@ -178,7 +178,7 @@ func handshakeS2C(t *P2PTunnel) error {
return err return err
} }
defer conn.Close() defer conn.Close()
UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id}) UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id})
_, head, buff, _, err := UDPRead(conn, HandshakeTimeout) _, head, buff, _, err := UDPRead(conn, HandshakeTimeout)
if err != nil { if err != nil {
// gLog.Println(LevelDEBUG, "one of the handshake error:", err) // gLog.Println(LevelDEBUG, "one of the handshake error:", err)
@@ -199,7 +199,7 @@ func handshakeS2C(t *P2PTunnel) error {
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake && tunnelID == t.id { if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake && tunnelID == t.id {
gLog.Printf(LvDEBUG, "handshakeS2C read %d handshake ", t.id) gLog.Printf(LvDEBUG, "handshakeS2C read %d handshake ", t.id)
UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id}) UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
// may read several MsgPunchHandshake // may read several MsgPunchHandshake
for { for {
_, head, buff, _, err = UDPRead(conn, HandshakeTimeout) _, head, buff, _, err = UDPRead(conn, HandshakeTimeout)
@@ -224,7 +224,7 @@ func handshakeS2C(t *P2PTunnel) error {
} }
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck { if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
gLog.Printf(LvDEBUG, "handshakeS2C read %d handshake ack %s", t.id, conn.LocalAddr().String()) gLog.Printf(LvDEBUG, "handshakeS2C read %d handshake ack %s", t.id, conn.LocalAddr().String())
UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id}) UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
gotIt = true gotIt = true
la, _ := net.ResolveUDPAddr("udp", conn.LocalAddr().String()) la, _ := net.ResolveUDPAddr("udp", conn.LocalAddr().String())
gotCh <- la gotCh <- la
@@ -238,14 +238,14 @@ func handshakeS2C(t *P2PTunnel) error {
gLog.Printf(LvDEBUG, "send symmetric handshake end") gLog.Printf(LvDEBUG, "send symmetric handshake end")
if compareVersion(t.config.peerVersion, SymmetricSimultaneouslySendVersion) < 0 { // compatible with old client if compareVersion(t.config.peerVersion, SymmetricSimultaneouslySendVersion) < 0 { // compatible with old client
gLog.Println(LvDEBUG, "handshakeS2C ready, notify peer connect") gLog.Println(LvDEBUG, "handshakeS2C ready, notify peer connect")
t.pn.push(t.config.PeerNode, MsgPushHandshakeStart, TunnelMsg{ID: t.id}) GNetwork.push(t.config.PeerNode, MsgPushHandshakeStart, TunnelMsg{ID: t.id})
} }
select { select {
case <-time.After(HandshakeTimeout): case <-time.After(HandshakeTimeout):
return fmt.Errorf("wait handshake timeout") return fmt.Errorf("wait handshake timeout")
case la := <-gotCh: case la := <-gotCh:
t.la = la t.localHoleAddr = la
gLog.Println(LvDEBUG, "symmetric handshake ok", la) gLog.Println(LvDEBUG, "symmetric handshake ok", la)
gLog.Printf(LvINFO, "handshakeS2C ok. cost %dms", time.Since(startTime)/time.Millisecond) gLog.Printf(LvINFO, "handshakeS2C ok. cost %dms", time.Since(startTime)/time.Millisecond)
} }
+124 -124
View File
@@ -1,124 +1,124 @@
package openp2p package openp2p
import ( import (
"fmt" "fmt"
"io" "io"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
) )
func install() { func install() {
gLog.Println(LvINFO, "openp2p start. version: ", OpenP2PVersion) gLog.Println(LvINFO, "openp2p start. version: ", OpenP2PVersion)
gLog.Println(LvINFO, "Contact: QQ group 16947733, Email [email protected]") gLog.Println(LvINFO, "Contact: QQ group 16947733, Email [email protected]")
gLog.Println(LvINFO, "install start") gLog.Println(LvINFO, "install start")
defer gLog.Println(LvINFO, "install end") defer gLog.Println(LvINFO, "install end")
// auto uninstall // auto uninstall
err := os.MkdirAll(defaultInstallPath, 0775) err := os.MkdirAll(defaultInstallPath, 0775)
if err != nil { if err != nil {
gLog.Printf(LvERROR, "MkdirAll %s error:%s", defaultInstallPath, err) gLog.Printf(LvERROR, "MkdirAll %s error:%s", defaultInstallPath, err)
return return
} }
err = os.Chdir(defaultInstallPath) err = os.Chdir(defaultInstallPath)
if err != nil { if err != nil {
gLog.Println(LvERROR, "cd error:", err) gLog.Println(LvERROR, "cd error:", err)
return return
} }
uninstall() uninstall()
// save config file // save config file
parseParams("install", "") parseParams("install", "")
targetPath := filepath.Join(defaultInstallPath, defaultBinName) targetPath := filepath.Join(defaultInstallPath, defaultBinName)
d := daemon{} d := daemon{}
// copy files // copy files
binPath, _ := os.Executable() binPath, _ := os.Executable()
src, errFiles := os.Open(binPath) // can not use args[0], on Windows call openp2p is ok(=openp2p.exe) src, errFiles := os.Open(binPath) // can not use args[0], on Windows call openp2p is ok(=openp2p.exe)
if errFiles != nil { if errFiles != nil {
gLog.Printf(LvERROR, "os.OpenFile %s error:%s", os.Args[0], errFiles) gLog.Printf(LvERROR, "os.OpenFile %s error:%s", os.Args[0], errFiles)
return return
} }
dst, errFiles := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0775) dst, errFiles := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0775)
if errFiles != nil { if errFiles != nil {
gLog.Printf(LvERROR, "os.OpenFile %s error:%s", targetPath, errFiles) gLog.Printf(LvERROR, "os.OpenFile %s error:%s", targetPath, errFiles)
return return
} }
_, errFiles = io.Copy(dst, src) _, errFiles = io.Copy(dst, src)
if errFiles != nil { if errFiles != nil {
gLog.Printf(LvERROR, "io.Copy error:%s", errFiles) gLog.Printf(LvERROR, "io.Copy error:%s", errFiles)
return return
} }
src.Close() src.Close()
dst.Close() dst.Close()
// install system service // install system service
gLog.Println(LvINFO, "targetPath:", targetPath) gLog.Println(LvINFO, "targetPath:", targetPath)
err = d.Control("install", targetPath, []string{"-d"}) err = d.Control("install", targetPath, []string{"-d"})
if err == nil { if err == nil {
gLog.Println(LvINFO, "install system service ok.") gLog.Println(LvINFO, "install system service ok.")
} }
time.Sleep(time.Second * 2) time.Sleep(time.Second * 2)
err = d.Control("start", targetPath, []string{"-d"}) err = d.Control("start", targetPath, []string{"-d"})
if err != nil { if err != nil {
gLog.Println(LvERROR, "start openp2p service error:", err) gLog.Println(LvERROR, "start openp2p service error:", err)
} else { } else {
gLog.Println(LvINFO, "start openp2p service ok.") gLog.Println(LvINFO, "start openp2p service ok.")
} }
gLog.Println(LvINFO, "Visit WebUI on https://console.openp2p.cn") gLog.Println(LvINFO, "Visit WebUI on https://console.openp2p.cn")
} }
func installByFilename() { func installByFilename() {
params := strings.Split(filepath.Base(os.Args[0]), "-") params := strings.Split(filepath.Base(os.Args[0]), "-")
if len(params) < 4 { if len(params) < 4 {
return return
} }
serverHost := params[1] serverHost := params[1]
token := params[2] token := params[2]
gLog.Println(LvINFO, "install start") gLog.Println(LvINFO, "install start")
targetPath := os.Args[0] targetPath := os.Args[0]
args := []string{"install"} args := []string{"install"}
args = append(args, "-serverhost") args = append(args, "-serverhost")
args = append(args, serverHost) args = append(args, serverHost)
args = append(args, "-token") args = append(args, "-token")
args = append(args, token) args = append(args, token)
env := os.Environ() env := os.Environ()
cmd := exec.Command(targetPath, args...) cmd := exec.Command(targetPath, args...)
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Env = env cmd.Env = env
err := cmd.Run() err := cmd.Run()
if err != nil { if err != nil {
gLog.Println(LvERROR, "install by filename, start process error:", err) gLog.Println(LvERROR, "install by filename, start process error:", err)
return return
} }
gLog.Println(LvINFO, "install end") gLog.Println(LvINFO, "install end")
gLog.Println(LvINFO, "Visit WebUI on https://console.openp2p.cn") gLog.Println(LvINFO, "Visit WebUI on https://console.openp2p.cn")
fmt.Println("Press the Any Key to exit") fmt.Println("Press the Any Key to exit")
fmt.Scanln() fmt.Scanln()
os.Exit(0) os.Exit(0)
} }
func uninstall() { func uninstall() {
gLog.Println(LvINFO, "uninstall start") gLog.Println(LvINFO, "uninstall start")
defer gLog.Println(LvINFO, "uninstall end") defer gLog.Println(LvINFO, "uninstall end")
d := daemon{} d := daemon{}
err := d.Control("stop", "", nil) err := d.Control("stop", "", nil)
if err != nil { // service maybe not install if err != nil { // service maybe not install
return return
} }
err = d.Control("uninstall", "", nil) err = d.Control("uninstall", "", nil)
if err != nil { if err != nil {
gLog.Println(LvERROR, "uninstall system service error:", err) gLog.Println(LvERROR, "uninstall system service error:", err)
} else { } else {
gLog.Println(LvINFO, "uninstall system service ok.") gLog.Println(LvINFO, "uninstall system service ok.")
} }
binPath := filepath.Join(defaultInstallPath, defaultBinName) binPath := filepath.Join(defaultInstallPath, defaultBinName)
os.Remove(binPath + "0") os.Remove(binPath + "0")
os.Remove(binPath) os.Remove(binPath)
// os.RemoveAll(defaultInstallPath) // reserve config.json // os.RemoveAll(defaultInstallPath) // reserve config.json
} }
+74 -74
View File
@@ -1,74 +1,74 @@
package openp2p package openp2p
import ( import (
"log" "log"
"os/exec" "os/exec"
"runtime" "runtime"
) )
func allowTunForward() { func allowTunForward() {
if runtime.GOOS != "linux" { // only support Linux if runtime.GOOS != "linux" { // only support Linux
return return
} }
exec.Command("sh", "-c", `iptables -t filter -D FORWARD -i optun -j ACCEPT`).Run() exec.Command("sh", "-c", `iptables -t filter -D FORWARD -i optun -j ACCEPT`).Run()
exec.Command("sh", "-c", `iptables -t filter -D FORWARD -o optun -j ACCEPT`).Run() exec.Command("sh", "-c", `iptables -t filter -D FORWARD -o optun -j ACCEPT`).Run()
err := exec.Command("sh", "-c", `iptables -t filter -I FORWARD -i optun -j ACCEPT`).Run() err := exec.Command("sh", "-c", `iptables -t filter -I FORWARD -i optun -j ACCEPT`).Run()
if err != nil { if err != nil {
log.Println("allow foward in error:", err) log.Println("allow foward in error:", err)
} }
err = exec.Command("sh", "-c", `iptables -t filter -I FORWARD -o optun -j ACCEPT`).Run() err = exec.Command("sh", "-c", `iptables -t filter -I FORWARD -o optun -j ACCEPT`).Run()
if err != nil { if err != nil {
log.Println("allow foward out error:", err) log.Println("allow foward out error:", err)
} }
} }
func clearSNATRule() { func clearSNATRule() {
if runtime.GOOS != "linux" { if runtime.GOOS != "linux" {
return return
} }
execCommand("iptables", true, "-t", "nat", "-D", "POSTROUTING", "-j", "OPSDWAN") execCommand("iptables", true, "-t", "nat", "-D", "POSTROUTING", "-j", "OPSDWAN")
execCommand("iptables", true, "-t", "nat", "-F", "OPSDWAN") execCommand("iptables", true, "-t", "nat", "-F", "OPSDWAN")
execCommand("iptables", true, "-t", "nat", "-X", "OPSDWAN") execCommand("iptables", true, "-t", "nat", "-X", "OPSDWAN")
} }
func initSNATRule(localNet string) { func initSNATRule(localNet string) {
if runtime.GOOS != "linux" { if runtime.GOOS != "linux" {
return return
} }
clearSNATRule() clearSNATRule()
err := execCommand("iptables", true, "-t", "nat", "-N", "OPSDWAN") err := execCommand("iptables", true, "-t", "nat", "-N", "OPSDWAN")
if err != nil { if err != nil {
log.Println("iptables new sdwan chain error:", err) log.Println("iptables new sdwan chain error:", err)
return return
} }
err = execCommand("iptables", true, "-t", "nat", "-A", "POSTROUTING", "-j", "OPSDWAN") err = execCommand("iptables", true, "-t", "nat", "-A", "POSTROUTING", "-j", "OPSDWAN")
if err != nil { if err != nil {
log.Println("iptables append postrouting error:", err) log.Println("iptables append postrouting error:", err)
return return
} }
err = execCommand("iptables", true, "-t", "nat", "-A", "OPSDWAN", err = execCommand("iptables", true, "-t", "nat", "-A", "OPSDWAN",
"-o", "optun", "!", "-s", localNet, "-j", "MASQUERADE") "-o", "optun", "!", "-s", localNet, "-j", "MASQUERADE")
if err != nil { if err != nil {
log.Println("add optun snat error:", err) log.Println("add optun snat error:", err)
return return
} }
err = execCommand("iptables", true, "-t", "nat", "-A", "OPSDWAN", "!", "-o", "optun", err = execCommand("iptables", true, "-t", "nat", "-A", "OPSDWAN", "!", "-o", "optun",
"-s", localNet, "-j", "MASQUERADE") "-s", localNet, "-j", "MASQUERADE")
if err != nil { if err != nil {
log.Println("add optun snat error:", err) log.Println("add optun snat error:", err)
return return
} }
} }
func addSNATRule(target string) { func addSNATRule(target string) {
if runtime.GOOS != "linux" { if runtime.GOOS != "linux" {
return return
} }
err := execCommand("iptables", true, "-t", "nat", "-A", "OPSDWAN", "!", "-o", "optun", err := execCommand("iptables", true, "-t", "nat", "-A", "OPSDWAN", "!", "-o", "optun",
"-s", target, "-j", "MASQUERADE") "-s", target, "-j", "MASQUERADE")
if err != nil { if err != nil {
log.Println("iptables add optun snat error:", err) log.Println("iptables add optun snat error:", err)
return return
} }
} }
+193 -189
View File
@@ -1,189 +1,193 @@
package openp2p package openp2p
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"math/rand" "math/rand"
"net" "net"
"strconv" "strconv"
"strings" "strings"
"time" "time"
reuse "github.com/openp2p-cn/go-reuseport" reuse "github.com/openp2p-cn/go-reuseport"
) )
func natTCP(serverHost string, serverPort int) (publicIP string, publicPort int, localPort int) { func natTCP(serverHost string, serverPort int) (publicIP string, publicPort int, localPort int) {
// dialer := &net.Dialer{ // dialer := &net.Dialer{
// LocalAddr: &net.TCPAddr{ // LocalAddr: &net.TCPAddr{
// IP: net.ParseIP("0.0.0.0"), // IP: net.ParseIP("0.0.0.0"),
// Port: localPort, // 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 := 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)) // conn, err := net.Dial("tcp4", fmt.Sprintf("%s:%d", serverHost, serverPort))
// log.Println(LvINFO, conn.LocalAddr()) // log.Println(LvINFO, conn.LocalAddr())
if err != nil { if err != nil {
fmt.Printf("Dial tcp4 %s:%d error:%s", serverHost, serverPort, err) fmt.Printf("Dial tcp4 %s:%d error:%s", serverHost, serverPort, err)
return return
} }
defer conn.Close() defer conn.Close()
localPort, _ = strconv.Atoi(strings.Split(conn.LocalAddr().String(), ":")[1]) localPort, _ = strconv.Atoi(strings.Split(conn.LocalAddr().String(), ":")[1])
_, wrerr := conn.Write([]byte("1")) _, wrerr := conn.Write([]byte("1"))
if wrerr != nil { if wrerr != nil {
fmt.Printf("Write error: %s\n", wrerr) fmt.Printf("Write error: %s\n", wrerr)
return return
} }
b := make([]byte, 1000) b := make([]byte, 1000)
conn.SetReadDeadline(time.Now().Add(NatTestTimeout)) conn.SetReadDeadline(time.Now().Add(NatTestTimeout))
n, rderr := conn.Read(b) n, rderr := conn.Read(b)
if rderr != nil { if rderr != nil {
fmt.Printf("Read error: %s\n", rderr) fmt.Printf("Read error: %s\n", rderr)
return return
} }
arr := strings.Split(string(b[:n]), ":") arr := strings.Split(string(b[:n]), ":")
if len(arr) < 2 { if len(arr) < 2 {
return return
} }
publicIP = arr[0] publicIP = arr[0]
port, _ := strconv.ParseInt(arr[1], 10, 32) port, _ := strconv.ParseInt(arr[1], 10, 32)
publicPort = int(port) publicPort = int(port)
return return
} }
func natTest(serverHost string, serverPort int, localPort int) (publicIP string, publicPort int, err error) { func natTest(serverHost string, serverPort int, localPort int) (publicIP string, publicPort int, err error) {
gLog.Println(LvDEBUG, "natTest start") gLog.Println(LvDEBUG, "natTest start")
defer gLog.Println(LvDEBUG, "natTest end") defer gLog.Println(LvDEBUG, "natTest end")
conn, err := net.ListenPacket("udp", fmt.Sprintf(":%d", localPort)) conn, err := net.ListenPacket("udp", fmt.Sprintf(":%d", localPort))
if err != nil { if err != nil {
gLog.Println(LvERROR, "natTest listen udp error:", err) gLog.Println(LvERROR, "natTest listen udp error:", err)
return "", 0, err return "", 0, err
} }
defer conn.Close() defer conn.Close()
dst, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", serverHost, serverPort)) dst, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", serverHost, serverPort))
if err != nil { if err != nil {
return "", 0, err return "", 0, err
} }
// The connection can write data to the desired address. // The connection can write data to the desired address.
msg, err := newMessage(MsgNATDetect, 0, nil) msg, err := newMessage(MsgNATDetect, MsgNAT, nil)
_, err = conn.WriteTo(msg, dst) _, err = conn.WriteTo(msg, dst)
if err != nil { if err != nil {
return "", 0, err return "", 0, err
} }
deadline := time.Now().Add(NatTestTimeout) deadline := time.Now().Add(NatTestTimeout)
err = conn.SetReadDeadline(deadline) err = conn.SetReadDeadline(deadline)
if err != nil { if err != nil {
return "", 0, err return "", 0, err
} }
buffer := make([]byte, 1024) buffer := make([]byte, 1024)
nRead, _, err := conn.ReadFrom(buffer) nRead, _, err := conn.ReadFrom(buffer)
if err != nil { if err != nil {
gLog.Println(LvERROR, "NAT detect error:", err) gLog.Println(LvERROR, "NAT detect error:", err)
return "", 0, err return "", 0, err
} }
natRsp := NatDetectRsp{} natRsp := NatDetectRsp{}
json.Unmarshal(buffer[openP2PHeaderSize:nRead], &natRsp) json.Unmarshal(buffer[openP2PHeaderSize:nRead], &natRsp)
return natRsp.IP, natRsp.Port, nil return natRsp.IP, natRsp.Port, nil
} }
func getNATType(host string, udp1 int, udp2 int) (publicIP string, NATType int, err error) { func getNATType(host string, udp1 int, udp2 int) (publicIP string, NATType int, err error) {
// the random local port may be used by other. // the random local port may be used by other.
localPort := int(rand.Uint32()%15000 + 50000) localPort := int(rand.Uint32()%15000 + 50000)
ip1, port1, err := natTest(host, udp1, localPort) ip1, port1, err := natTest(host, udp1, localPort)
if err != nil { if err != nil {
return "", 0, err return "", 0, err
} }
_, port2, err := natTest(host, udp2, localPort) // 2rd nat test not need testing publicip _, port2, err := natTest(host, udp2, localPort) // 2rd nat test not need testing publicip
gLog.Printf(LvDEBUG, "local port:%d nat port:%d", localPort, port2) gLog.Printf(LvDEBUG, "local port:%d nat port:%d", localPort, port2)
if err != nil { if err != nil {
return "", 0, err return "", 0, err
} }
natType := NATSymmetric natType := NATSymmetric
if port1 == port2 { if port1 == port2 {
natType = NATCone natType = NATCone
} }
return ip1, natType, nil return ip1, natType, nil
} }
func publicIPTest(publicIP string, echoPort int) (hasPublicIP int, hasUPNPorNATPMP int) { func publicIPTest(publicIP string, echoPort int) (hasPublicIP int, hasUPNPorNATPMP int) {
if publicIP == "" || echoPort == 0 { if publicIP == "" || echoPort == 0 {
return return
} }
var echoConn *net.UDPConn var echoConn *net.UDPConn
gLog.Println(LvDEBUG, "echo server start") gLog.Println(LvDEBUG, "echo server start")
var err error var err error
echoConn, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: echoPort}) echoConn, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: echoPort})
if err != nil { // listen error if err != nil { // listen error
gLog.Println(LvERROR, "echo server listen error:", err) gLog.Println(LvERROR, "echo server listen error:", err)
return return
} }
defer echoConn.Close() defer echoConn.Close()
go func() { // testing for public ip
// close outside for breaking the ReadFromUDP for i := 0; i < 2; i++ {
// wait 30s for echo testing if i == 1 {
buf := make([]byte, 1600) // test upnp or nat-pmp
echoConn.SetReadDeadline(time.Now().Add(time.Second * 30)) gLog.Println(LvDEBUG, "upnp test start")
n, addr, err := echoConn.ReadFromUDP(buf) nat, err := Discover()
if err != nil { if err != nil || nat == nil {
return gLog.Println(LvDEBUG, "could not perform UPNP discover:", err)
} break
echoConn.WriteToUDP(buf[0:n], addr) }
gLog.Println(LvDEBUG, "echo server end") ext, err := nat.GetExternalAddress()
}() if err != nil {
// testing for public ip gLog.Println(LvDEBUG, "could not perform UPNP external address:", err)
for i := 0; i < 2; i++ { break
if i == 1 { }
// test upnp or nat-pmp gLog.Println(LvINFO, "PublicIP:", ext)
gLog.Println(LvDEBUG, "upnp test start")
nat, err := Discover() externalPort, err := nat.AddPortMapping("udp", echoPort, echoPort, "openp2p", 30) // 30 seconds fot upnp testing
if err != nil || nat == nil { if err != nil {
gLog.Println(LvDEBUG, "could not perform UPNP discover:", err) gLog.Println(LvDEBUG, "could not add udp UPNP port mapping", externalPort)
break break
} } else {
ext, err := nat.GetExternalAddress() nat.AddPortMapping("tcp", echoPort, echoPort, "openp2p", 604800) // 7 days for tcp connection
if err != nil { }
gLog.Println(LvDEBUG, "could not perform UPNP external address:", err) }
break gLog.Printf(LvDEBUG, "public ip test start %s:%d", publicIP, echoPort)
} conn, err := net.ListenUDP("udp", nil)
gLog.Println(LvINFO, "PublicIP:", ext) if err != nil {
break
externalPort, err := nat.AddPortMapping("udp", echoPort, echoPort, "openp2p", 30) // 30 seconds fot upnp testing }
if err != nil { defer conn.Close()
gLog.Println(LvDEBUG, "could not add udp UPNP port mapping", externalPort) dst, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", gConf.Network.ServerHost, gConf.Network.ServerPort))
break if err != nil {
} else { break
nat.AddPortMapping("tcp", echoPort, echoPort, "openp2p", 604800) // 7 days for tcp connection }
}
} // The connection can write data to the desired address.
gLog.Printf(LvDEBUG, "public ip test start %s:%d", publicIP, echoPort) msg, _ := newMessage(MsgNATDetect, MsgPublicIP, NatDetectReq{EchoPort: echoPort})
conn, err := net.ListenUDP("udp", nil) _, err = conn.WriteTo(msg, dst)
if err != nil { if err != nil {
break continue
} }
defer conn.Close() buf := make([]byte, 1600)
dst, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", publicIP, echoPort))
if err != nil { // wait for echo testing
break echoConn.SetReadDeadline(time.Now().Add(PublicIPEchoTimeout))
} nRead, _, err := echoConn.ReadFromUDP(buf)
conn.WriteTo([]byte("echo"), dst) if err != nil {
buf := make([]byte, 1600) gLog.Println(LvDEBUG, "PublicIP detect error:", err)
continue
// wait for echo testing }
conn.SetReadDeadline(time.Now().Add(PublicIPEchoTimeout)) natRsp := NatDetectRsp{}
_, _, err = conn.ReadFromUDP(buf) err = json.Unmarshal(buf[openP2PHeaderSize:nRead], &natRsp)
if err == nil { if err != nil {
if i == 1 { gLog.Println(LvDEBUG, "PublicIP detect error:", err)
gLog.Println(LvDEBUG, "UPNP or NAT-PMP:YES") continue
hasUPNPorNATPMP = 1 }
} else { if natRsp.Port == echoPort {
gLog.Println(LvDEBUG, "public ip:YES") if i == 1 {
hasPublicIP = 1 gLog.Println(LvDEBUG, "UPNP or NAT-PMP:YES")
} hasUPNPorNATPMP = 1
break } else {
} gLog.Println(LvDEBUG, "public ip:YES")
} hasPublicIP = 1
return }
} break
}
}
return
}
+121 -121
View File
@@ -1,121 +1,121 @@
package openp2p package openp2p
import ( import (
"fmt" "fmt"
"math/rand" "math/rand"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"time" "time"
) )
var GNetwork *P2PNetwork var GNetwork *P2PNetwork
func Run() { func Run() {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
baseDir := filepath.Dir(os.Args[0]) baseDir := filepath.Dir(os.Args[0])
os.Chdir(baseDir) // for system service os.Chdir(baseDir) // for system service
gLog = NewLogger(baseDir, ProductName, LvDEBUG, 1024*1024, LogFile|LogConsole) gLog = NewLogger(baseDir, ProductName, LvDEBUG, 1024*1024, LogFile|LogConsole)
if len(os.Args) > 1 { if len(os.Args) > 1 {
switch os.Args[1] { switch os.Args[1] {
case "version", "-v", "--version": case "version", "-v", "--version":
fmt.Println(OpenP2PVersion) fmt.Println(OpenP2PVersion)
return return
case "install": case "install":
install() install()
return return
case "uninstall": case "uninstall":
uninstall() uninstall()
return return
} }
} else { } else {
installByFilename() installByFilename()
} }
parseParams("", "") parseParams("", "")
gLog.Println(LvINFO, "openp2p start. version: ", OpenP2PVersion) gLog.Println(LvINFO, "openp2p start. version: ", OpenP2PVersion)
gLog.Println(LvINFO, "Contact: QQ group 16947733, Email [email protected]") gLog.Println(LvINFO, "Contact: QQ group 16947733, Email [email protected]")
if gConf.daemonMode { if gConf.daemonMode {
d := daemon{} d := daemon{}
d.run() d.run()
return return
} }
gLog.Println(LvINFO, &gConf) gLog.Println(LvINFO, &gConf)
setFirewall() setFirewall()
err := setRLimit() err := setRLimit()
if err != nil { if err != nil {
gLog.Println(LvINFO, "setRLimit error:", err) gLog.Println(LvINFO, "setRLimit error:", err)
} }
GNetwork = P2PNetworkInstance() GNetwork = P2PNetworkInstance()
if ok := GNetwork.Connect(30000); !ok { if ok := GNetwork.Connect(30000); !ok {
gLog.Println(LvERROR, "P2PNetwork login error") gLog.Println(LvERROR, "P2PNetwork login error")
return return
} }
// gLog.Println(LvINFO, "waiting for connection...") // gLog.Println(LvINFO, "waiting for connection...")
forever := make(chan bool) forever := make(chan bool)
<-forever <-forever
} }
// for Android app // for Android app
// gomobile not support uint64 exported to java // gomobile not support uint64 exported to java
func RunAsModule(baseDir string, token string, bw int, logLevel int) *P2PNetwork { func RunAsModule(baseDir string, token string, bw int, logLevel int) *P2PNetwork {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
os.Chdir(baseDir) // for system service os.Chdir(baseDir) // for system service
gLog = NewLogger(baseDir, ProductName, LvINFO, 1024*1024, LogFile|LogConsole) gLog = NewLogger(baseDir, ProductName, LvINFO, 1024*1024, LogFile|LogConsole)
parseParams("", "") parseParams("", "")
n, err := strconv.ParseUint(token, 10, 64) n, err := strconv.ParseUint(token, 10, 64)
if err == nil && n > 0 { if err == nil && n > 0 {
gConf.setToken(n) gConf.setToken(n)
} }
if n <= 0 && gConf.Network.Token == 0 { // not input token if n <= 0 && gConf.Network.Token == 0 { // not input token
return nil return nil
} }
// gLog.setLevel(LogLevel(logLevel)) // gLog.setLevel(LogLevel(logLevel))
gConf.setShareBandwidth(bw) gConf.setShareBandwidth(bw)
gLog.Println(LvINFO, "openp2p start. version: ", OpenP2PVersion) gLog.Println(LvINFO, "openp2p start. version: ", OpenP2PVersion)
gLog.Println(LvINFO, "Contact: QQ group 16947733, Email [email protected]") gLog.Println(LvINFO, "Contact: QQ group 16947733, Email [email protected]")
gLog.Println(LvINFO, &gConf) gLog.Println(LvINFO, &gConf)
GNetwork = P2PNetworkInstance() GNetwork = P2PNetworkInstance()
if ok := GNetwork.Connect(30000); !ok { if ok := GNetwork.Connect(30000); !ok {
gLog.Println(LvERROR, "P2PNetwork login error") gLog.Println(LvERROR, "P2PNetwork login error")
return nil return nil
} }
// gLog.Println(LvINFO, "waiting for connection...") // gLog.Println(LvINFO, "waiting for connection...")
return GNetwork return GNetwork
} }
func RunCmd(cmd string) { func RunCmd(cmd string) {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
baseDir := filepath.Dir(os.Args[0]) baseDir := filepath.Dir(os.Args[0])
os.Chdir(baseDir) // for system service os.Chdir(baseDir) // for system service
gLog = NewLogger(baseDir, ProductName, LvINFO, 1024*1024, LogFile|LogConsole) gLog = NewLogger(baseDir, ProductName, LvINFO, 1024*1024, LogFile|LogConsole)
parseParams("", cmd) parseParams("", cmd)
setFirewall() setFirewall()
err := setRLimit() err := setRLimit()
if err != nil { if err != nil {
gLog.Println(LvINFO, "setRLimit error:", err) gLog.Println(LvINFO, "setRLimit error:", err)
} }
GNetwork = P2PNetworkInstance() GNetwork = P2PNetworkInstance()
if ok := GNetwork.Connect(30000); !ok { if ok := GNetwork.Connect(30000); !ok {
gLog.Println(LvERROR, "P2PNetwork login error") gLog.Println(LvERROR, "P2PNetwork login error")
return return
} }
forever := make(chan bool) forever := make(chan bool)
<-forever <-forever
} }
func GetToken(baseDir string) string { func GetToken(baseDir string) string {
os.Chdir(baseDir) os.Chdir(baseDir)
gConf.load() gConf.load()
return fmt.Sprintf("%d", gConf.Network.Token) return fmt.Sprintf("%d", gConf.Network.Token)
} }
func Stop() { func Stop() {
os.Exit(0) os.Exit(0)
} }
+20 -20
View File
@@ -1,20 +1,20 @@
package openp2p package openp2p
import ( import (
"github.com/openp2p-cn/wireguard-go/tun" "github.com/openp2p-cn/wireguard-go/tun"
) )
var AndroidSDWANConfig chan []byte var AndroidSDWANConfig chan []byte
type optun struct { type optun struct {
tunName string tunName string
dev tun.Device dev tun.Device
} }
func (t *optun) Stop() error { func (t *optun) Stop() error {
t.dev.Close() t.dev.Close()
return nil return nil
} }
func init() { func init() {
AndroidSDWANConfig = make(chan []byte, 1) AndroidSDWANConfig = make(chan []byte, 1)
} }
+85 -85
View File
@@ -1,85 +1,85 @@
// optun_android.go // optun_android.go
//go:build android //go:build android
// +build android // +build android
package openp2p package openp2p
import ( import (
"net" "net"
) )
const ( const (
tunIfaceName = "optun" tunIfaceName = "optun"
PIHeaderSize = 0 PIHeaderSize = 0
) )
var AndroidReadTun chan []byte // TODO: multi channel var AndroidReadTun chan []byte // TODO: multi channel
var AndroidWriteTun chan []byte var AndroidWriteTun chan []byte
func (t *optun) Start(localAddr string, detail *SDWANInfo) error { func (t *optun) Start(localAddr string, detail *SDWANInfo) error {
return nil return nil
} }
func (t *optun) Read(bufs [][]byte, sizes []int, offset int) (n int, err error) { func (t *optun) Read(bufs [][]byte, sizes []int, offset int) (n int, err error) {
bufs[0] = <-AndroidReadTun bufs[0] = <-AndroidReadTun
sizes[0] = len(bufs[0]) sizes[0] = len(bufs[0])
return 1, nil return 1, nil
} }
func (t *optun) Write(bufs [][]byte, offset int) (int, error) { func (t *optun) Write(bufs [][]byte, offset int) (int, error) {
AndroidWriteTun <- bufs[0] AndroidWriteTun <- bufs[0]
return len(bufs[0]), nil return len(bufs[0]), nil
} }
func AndroidRead(data []byte, len int) { func AndroidRead(data []byte, len int) {
head := PacketHeader{} head := PacketHeader{}
parseHeader(data, &head) 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.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)
buf := make([]byte, len) buf := make([]byte, len)
copy(buf, data) copy(buf, data)
AndroidReadTun <- buf AndroidReadTun <- buf
} }
func AndroidWrite(buf []byte) int { func AndroidWrite(buf []byte) int {
p := <-AndroidWriteTun p := <-AndroidWriteTun
copy(buf, p) copy(buf, p)
return len(p) return len(p)
} }
func GetAndroidSDWANConfig(buf []byte) int { func GetAndroidSDWANConfig(buf []byte) int {
p := <-AndroidSDWANConfig p := <-AndroidSDWANConfig
copy(buf, p) copy(buf, p)
gLog.Printf(LvINFO, "AndroidSDWANConfig=%s", p) gLog.Printf(LvINFO, "AndroidSDWANConfig=%s", p)
return len(p) return len(p)
} }
func GetAndroidNodeName() string { func GetAndroidNodeName() string {
gLog.Printf(LvINFO, "GetAndroidNodeName=%s", gConf.Network.Node) gLog.Printf(LvINFO, "GetAndroidNodeName=%s", gConf.Network.Node)
return gConf.Network.Node return gConf.Network.Node
} }
func setTunAddr(ifname, localAddr, remoteAddr string, wintun interface{}) error { func setTunAddr(ifname, localAddr, remoteAddr string, wintun interface{}) error {
// TODO: // TODO:
return nil return nil
} }
func addRoute(dst, gw, ifname string) error { func addRoute(dst, gw, ifname string) error {
// TODO: // TODO:
return nil return nil
} }
func delRoute(dst, gw string) error { func delRoute(dst, gw string) error {
// TODO: // TODO:
return nil return nil
} }
func delRoutesByGateway(gateway string) error { func delRoutesByGateway(gateway string) error {
// TODO: // TODO:
return nil return nil
} }
func init() { func init() {
AndroidReadTun = make(chan []byte, 1000) AndroidReadTun = make(chan []byte, 1000)
AndroidWriteTun = make(chan []byte, 1000) AndroidWriteTun = make(chan []byte, 1000)
} }
+7 -6
View File
@@ -52,7 +52,7 @@ func addRoute(dst, gw, ifname string) error {
} }
func delRoute(dst, gw string) error { func delRoute(dst, gw string) error {
err := exec.Command("route", "delete", dst, gw).Run() err := exec.Command("route", "delete", dst, "-gateway", gw).Run()
return err return err
} }
func delRoutesByGateway(gateway string) error { func delRoutesByGateway(gateway string) error {
@@ -68,13 +68,14 @@ func delRoutesByGateway(gateway string) error {
continue continue
} }
fields := strings.Fields(line) fields := strings.Fields(line)
if len(fields) >= 7 && fields[0] == "default" && fields[len(fields)-1] == gateway { if len(fields) >= 2 {
delCmd := exec.Command("route", "delete", "default", gateway) cmd := exec.Command("route", "delete", fields[0], gateway)
err := delCmd.Run() err := cmd.Run()
if err != nil { if err != nil {
return err gLog.Printf(LvERROR, "Delete route %s error:%s", fields[0], err)
continue
} }
fmt.Printf("Delete route ok: %s %s\n", "default", gateway) gLog.Printf(LvINFO, "Delete route ok: %s %s\n", fields[0], gateway)
} }
} }
return nil return nil
+134 -133
View File
@@ -1,133 +1,134 @@
//go:build !android //go:build !android
// +build !android // +build !android
// optun_linux.go // optun_linux.go
package openp2p package openp2p
import ( import (
"fmt" "fmt"
"net" "net"
"os/exec" "os/exec"
"strings" "strings"
"github.com/openp2p-cn/wireguard-go/tun" "github.com/openp2p-cn/wireguard-go/tun"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
) )
const ( const (
tunIfaceName = "optun" tunIfaceName = "optun"
PIHeaderSize = 0 PIHeaderSize = 0
) )
var previousIP = "" var previousIP = ""
func (t *optun) Start(localAddr string, detail *SDWANInfo) error { func (t *optun) Start(localAddr string, detail *SDWANInfo) error {
var err error var err error
t.tunName = tunIfaceName t.tunName = tunIfaceName
t.dev, err = tun.CreateTUN(t.tunName, 1420) t.dev, err = tun.CreateTUN(t.tunName, 1420)
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
func (t *optun) Read(bufs [][]byte, sizes []int, offset int) (n int, err error) { func (t *optun) Read(bufs [][]byte, sizes []int, offset int) (n int, err error) {
return t.dev.Read(bufs, sizes, offset) return t.dev.Read(bufs, sizes, offset)
} }
func (t *optun) Write(bufs [][]byte, offset int) (int, error) { func (t *optun) Write(bufs [][]byte, offset int) (int, error) {
return t.dev.Write(bufs, offset) return t.dev.Write(bufs, offset)
} }
func setTunAddr(ifname, localAddr, remoteAddr string, wintun interface{}) error { func setTunAddr(ifname, localAddr, remoteAddr string, wintun interface{}) error {
ifce, err := netlink.LinkByName(ifname) ifce, err := netlink.LinkByName(ifname)
if err != nil { if err != nil {
return err return err
} }
netlink.LinkSetMTU(ifce, 1375) netlink.LinkSetMTU(ifce, 1375)
netlink.LinkSetTxQLen(ifce, 100) netlink.LinkSetTxQLen(ifce, 100)
netlink.LinkSetUp(ifce) netlink.LinkSetUp(ifce)
ln, err := netlink.ParseIPNet(localAddr) ln, err := netlink.ParseIPNet(localAddr)
if err != nil { if err != nil {
return err return err
} }
ln.Mask = net.CIDRMask(32, 32) ln.Mask = net.CIDRMask(32, 32)
rn, err := netlink.ParseIPNet(remoteAddr) rn, err := netlink.ParseIPNet(remoteAddr)
if err != nil { if err != nil {
return err return err
} }
rn.Mask = net.CIDRMask(32, 32) rn.Mask = net.CIDRMask(32, 32)
addr := &netlink.Addr{ addr := &netlink.Addr{
IPNet: ln, IPNet: ln,
Peer: rn, Peer: rn,
} }
if previousIP != "" { if previousIP != "" {
lnDel, err := netlink.ParseIPNet(previousIP) lnDel, err := netlink.ParseIPNet(previousIP)
if err != nil { if err != nil {
return err return err
} }
lnDel.Mask = net.CIDRMask(32, 32) lnDel.Mask = net.CIDRMask(32, 32)
addrDel := &netlink.Addr{ addrDel := &netlink.Addr{
IPNet: lnDel, IPNet: lnDel,
Peer: rn, Peer: rn,
} }
netlink.AddrDel(ifce, addrDel) netlink.AddrDel(ifce, addrDel)
} }
previousIP = localAddr previousIP = localAddr
return netlink.AddrAdd(ifce, addr) return netlink.AddrAdd(ifce, addr)
} }
func addRoute(dst, gw, ifname string) error { func addRoute(dst, gw, ifname string) error {
_, networkid, err := net.ParseCIDR(dst) _, networkid, err := net.ParseCIDR(dst)
if err != nil { if err != nil {
return err return err
} }
ipGW := net.ParseIP(gw) ipGW := net.ParseIP(gw)
if ipGW == nil { if ipGW == nil {
return fmt.Errorf("parse gateway %s failed", gw) return fmt.Errorf("parse gateway %s failed", gw)
} }
route := &netlink.Route{ route := &netlink.Route{
Dst: networkid, Dst: networkid,
Gw: ipGW, Gw: ipGW,
} }
return netlink.RouteAdd(route) return netlink.RouteAdd(route)
} }
func delRoute(dst, gw string) error { func delRoute(dst, gw string) error {
_, networkid, err := net.ParseCIDR(dst) _, networkid, err := net.ParseCIDR(dst)
if err != nil { if err != nil {
return err return err
} }
route := &netlink.Route{ route := &netlink.Route{
Dst: networkid, Dst: networkid,
} }
return netlink.RouteDel(route) return netlink.RouteDel(route)
} }
func delRoutesByGateway(gateway string) error { func delRoutesByGateway(gateway string) error {
cmd := exec.Command("route", "-n") cmd := exec.Command("route", "-n")
output, err := cmd.Output() output, err := cmd.Output()
if err != nil { if err != nil {
return err return err
} }
lines := strings.Split(string(output), "\n") lines := strings.Split(string(output), "\n")
for _, line := range lines { for _, line := range lines {
if !strings.Contains(line, gateway) { if !strings.Contains(line, gateway) {
continue continue
} }
fields := strings.Fields(line) fields := strings.Fields(line)
if len(fields) >= 8 && fields[1] == "0.0.0.0" && fields[7] == gateway { if len(fields) >= 8 && fields[1] == "0.0.0.0" && fields[7] == gateway {
delCmd := exec.Command("route", "del", "-net", fields[0], "gw", gateway) delCmd := exec.Command("route", "del", "-net", fields[0], "gw", gateway)
err := delCmd.Run() err := delCmd.Run()
if err != nil { if err != nil {
return err gLog.Printf(LvERROR, "Delete route %s error:%s", fields[0], err)
} continue
fmt.Printf("Delete route ok: %s %s %s\n", fields[0], fields[1], gateway) }
} gLog.Printf(LvINFO, "Delete route ok: %s %s %s\n", fields[0], fields[1], gateway)
} }
return nil }
} return nil
}
+143 -142
View File
@@ -1,142 +1,143 @@
package openp2p package openp2p
import ( import (
"fmt" "fmt"
"net" "net"
"net/netip" "net/netip"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"github.com/openp2p-cn/wireguard-go/tun" "github.com/openp2p-cn/wireguard-go/tun"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
) )
const ( const (
tunIfaceName = "optun" tunIfaceName = "optun"
PIHeaderSize = 0 PIHeaderSize = 0
) )
func (t *optun) Start(localAddr string, detail *SDWANInfo) error { func (t *optun) Start(localAddr string, detail *SDWANInfo) error {
// check wintun.dll // check wintun.dll
tmpFile := filepath.Dir(os.Args[0]) + "/wintun.dll" tmpFile := filepath.Dir(os.Args[0]) + "/wintun.dll"
fs, err := os.Stat(tmpFile) fs, err := os.Stat(tmpFile)
if err != nil || fs.Size() == 0 { if err != nil || fs.Size() == 0 {
url := fmt.Sprintf("https://openp2p.cn/download/v1/latest/wintun/%s/wintun.dll", runtime.GOARCH) url := fmt.Sprintf("https://openp2p.cn/download/v1/latest/wintun/%s/wintun.dll", runtime.GOARCH)
err = downloadFile(url, "", tmpFile) err = downloadFile(url, "", tmpFile)
if err != nil { if err != nil {
os.Remove(tmpFile) os.Remove(tmpFile)
return err return err
} }
} }
t.tunName = tunIfaceName t.tunName = tunIfaceName
uuid := &windows.GUID{ uuid := &windows.GUID{
Data1: 0xf411e821, Data1: 0xf411e821,
Data2: 0xb310, Data2: 0xb310,
Data3: 0x4567, Data3: 0x4567,
Data4: [8]byte{0x80, 0x42, 0x83, 0x7e, 0xf4, 0x56, 0xce, 0x13}, 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, 1420)
if err != nil { // retry if err != nil { // retry
t.dev, err = tun.CreateTUNWithRequestedGUID(t.tunName, uuid, 1420) t.dev, err = tun.CreateTUNWithRequestedGUID(t.tunName, uuid, 1420)
} }
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
func (t *optun) Read(bufs [][]byte, sizes []int, offset int) (n int, err error) { func (t *optun) Read(bufs [][]byte, sizes []int, offset int) (n int, err error) {
return t.dev.Read(bufs, sizes, offset) return t.dev.Read(bufs, sizes, offset)
} }
func (t *optun) Write(bufs [][]byte, offset int) (int, error) { func (t *optun) Write(bufs [][]byte, offset int) (int, error) {
return t.dev.Write(bufs, offset) return t.dev.Write(bufs, offset)
} }
func setTunAddr(ifname, localAddr, remoteAddr string, wintun interface{}) error { func setTunAddr(ifname, localAddr, remoteAddr string, wintun interface{}) error {
nativeTunDevice := wintun.(*tun.NativeTun) nativeTunDevice := wintun.(*tun.NativeTun)
link := winipcfg.LUID(nativeTunDevice.LUID()) link := winipcfg.LUID(nativeTunDevice.LUID())
ip, err := netip.ParsePrefix(localAddr) ip, err := netip.ParsePrefix(localAddr)
if err != nil { if err != nil {
gLog.Printf(LvERROR, "ParsePrefix error:%s, luid:%d,localAddr:%s", err, nativeTunDevice.LUID(), localAddr) gLog.Printf(LvERROR, "ParsePrefix error:%s, luid:%d,localAddr:%s", err, nativeTunDevice.LUID(), localAddr)
return err return err
} }
err = link.SetIPAddresses([]netip.Prefix{ip}) err = link.SetIPAddresses([]netip.Prefix{ip})
if err != nil { if err != nil {
gLog.Printf(LvERROR, "SetIPAddresses error:%s, netip.Prefix:%+v", err, []netip.Prefix{ip}) gLog.Printf(LvERROR, "SetIPAddresses error:%s, netip.Prefix:%+v", err, []netip.Prefix{ip})
return err return err
} }
return nil return nil
} }
func addRoute(dst, gw, ifname string) error { func addRoute(dst, gw, ifname string) error {
_, dstNet, err := net.ParseCIDR(dst) _, dstNet, err := net.ParseCIDR(dst)
if err != nil { if err != nil {
return err return err
} }
i, err := net.InterfaceByName(ifname) i, err := net.InterfaceByName(ifname)
if err != nil { if err != nil {
return err return err
} }
params := make([]string, 0) params := make([]string, 0)
params = append(params, "add") params = append(params, "add")
params = append(params, dstNet.IP.String()) params = append(params, dstNet.IP.String())
params = append(params, "mask") params = append(params, "mask")
params = append(params, net.IP(dstNet.Mask).String()) params = append(params, net.IP(dstNet.Mask).String())
params = append(params, gw) params = append(params, gw)
params = append(params, "if") params = append(params, "if")
params = append(params, strconv.Itoa(i.Index)) params = append(params, strconv.Itoa(i.Index))
// gLogger.Println(LevelINFO, "windows add route params:", params) // gLogger.Println(LevelINFO, "windows add route params:", params)
execCommand("route", true, params...) execCommand("route", true, params...)
return nil return nil
} }
func delRoute(dst, gw string) error { func delRoute(dst, gw string) error {
_, dstNet, err := net.ParseCIDR(dst) _, dstNet, err := net.ParseCIDR(dst)
if err != nil { if err != nil {
return err return err
} }
params := make([]string, 0) params := make([]string, 0)
params = append(params, "delete") params = append(params, "delete")
params = append(params, dstNet.IP.String()) params = append(params, dstNet.IP.String())
params = append(params, "mask") params = append(params, "mask")
params = append(params, net.IP(dstNet.Mask).String()) params = append(params, net.IP(dstNet.Mask).String())
params = append(params, gw) params = append(params, gw)
// gLogger.Println(LevelINFO, "windows delete route params:", params) // gLogger.Println(LevelINFO, "windows delete route params:", params)
execCommand("route", true, params...) execCommand("route", true, params...)
return nil return nil
} }
func delRoutesByGateway(gateway string) error { func delRoutesByGateway(gateway string) error {
cmd := exec.Command("route", "print", "-4") cmd := exec.Command("route", "print", "-4")
output, err := cmd.Output() output, err := cmd.Output()
if err != nil { if err != nil {
return err return err
} }
lines := strings.Split(string(output), "\n") lines := strings.Split(string(output), "\n")
for _, line := range lines { for _, line := range lines {
if !strings.Contains(line, gateway) { if !strings.Contains(line, gateway) {
continue continue
} }
fields := strings.Fields(line) fields := strings.Fields(line)
if len(fields) >= 5 { if len(fields) >= 5 {
cmd := exec.Command("route", "delete", fields[0], "mask", fields[1], gateway) cmd := exec.Command("route", "delete", fields[0], "mask", fields[1], gateway)
err := cmd.Run() err := cmd.Run()
if err != nil { if err != nil {
fmt.Println("Delete route error:", err) gLog.Printf(LvERROR, "Delete route %s error:%s", fields[0], err)
} continue
fmt.Printf("Delete route ok: %s %s %s\n", fields[0], fields[1], gateway) }
} gLog.Printf(LvINFO, "Delete route ok: %s %s %s\n", fields[0], fields[1], gateway)
} }
return nil }
} return nil
}
+14 -14
View File
@@ -138,7 +138,7 @@ func (app *p2pApp) checkDirectTunnel() error {
app.config.retryNum = 1 app.config.retryNum = 1
} }
if app.config.retryNum > 0 { // first time not show reconnect log if app.config.retryNum > 0 { // first time not show reconnect log
gLog.Printf(LvINFO, "detect app %s appid:%d disconnect, reconnecting the %d times...", app.config.PeerNode, app.id, app.config.retryNum) gLog.Printf(LvINFO, "detect app %s appid:%d disconnect, reconnecting the %d times...", app.config.LogPeerNode(), app.id, app.config.retryNum)
} }
app.config.retryNum++ app.config.retryNum++
app.config.retryTime = time.Now() app.config.retryTime = time.Now()
@@ -149,7 +149,7 @@ func (app *p2pApp) checkDirectTunnel() error {
app.config.errMsg = err.Error() app.config.errMsg = err.Error()
if err == ErrPeerOffline && app.config.retryNum > 2 { // stop retry, waiting for online if err == ErrPeerOffline && app.config.retryNum > 2 { // stop retry, waiting for online
app.config.retryNum = retryLimit app.config.retryNum = retryLimit
gLog.Printf(LvINFO, " %s offline, it will auto reconnect when peer node online", app.config.PeerNode) gLog.Printf(LvINFO, " %s offline, it will auto reconnect when peer node online", app.config.LogPeerNode())
} }
if err == ErrBuildTunnelBusy { if err == ErrBuildTunnelBusy {
app.config.retryNum-- app.config.retryNum--
@@ -174,7 +174,7 @@ func (app *p2pApp) buildDirectTunnel() error {
pn := GNetwork pn := GNetwork
initErr := pn.requestPeerInfo(&app.config) initErr := pn.requestPeerInfo(&app.config)
if initErr != nil { if initErr != nil {
gLog.Printf(LvERROR, "%s init error:%s", app.config.PeerNode, initErr) gLog.Printf(LvERROR, "%s requestPeerInfo error:%s", app.config.LogPeerNode(), initErr)
return initErr return initErr
} }
t, err = pn.addDirectTunnel(app.config, 0) t, err = pn.addDirectTunnel(app.config, 0)
@@ -212,7 +212,7 @@ func (app *p2pApp) buildDirectTunnel() error {
AppID: app.id, AppID: app.id,
AppKey: app.key, AppKey: app.key,
} }
gLog.Printf(LvDEBUG, "sync appkey direct to %s", app.config.PeerNode) gLog.Printf(LvDEBUG, "sync appkey direct to %s", app.config.LogPeerNode())
pn.push(app.config.PeerNode, MsgPushAPPKey, &syncKeyReq) pn.push(app.config.PeerNode, MsgPushAPPKey, &syncKeyReq)
app.setDirectTunnel(t) app.setDirectTunnel(t)
@@ -220,7 +220,7 @@ func (app *p2pApp) buildDirectTunnel() error {
if app.config.SrcPort == 0 { if app.config.SrcPort == 0 {
req := ServerSideSaveMemApp{From: gConf.Network.Node, Node: gConf.Network.Node, TunnelID: t.id, RelayTunnelID: 0, AppID: app.id} req := ServerSideSaveMemApp{From: gConf.Network.Node, Node: gConf.Network.Node, TunnelID: t.id, RelayTunnelID: 0, AppID: app.id}
pn.push(app.config.PeerNode, MsgPushServerSideSaveMemApp, &req) pn.push(app.config.PeerNode, MsgPushServerSideSaveMemApp, &req)
gLog.Printf(LvDEBUG, "push %s ServerSideSaveMemApp: %s", app.config.PeerNode, prettyJson(req)) gLog.Printf(LvDEBUG, "push %s ServerSideSaveMemApp: %s", app.config.LogPeerNode(), prettyJson(req))
} }
gLog.Printf(LvDEBUG, "%s use tunnel %d", app.config.AppName, t.id) gLog.Printf(LvDEBUG, "%s use tunnel %d", app.config.AppName, t.id)
return nil return nil
@@ -244,7 +244,7 @@ func (app *p2pApp) checkRelayTunnel() error {
app.retryRelayNum = 1 app.retryRelayNum = 1
} }
if app.retryRelayNum > 0 { // first time not show reconnect log if app.retryRelayNum > 0 { // first time not show reconnect log
gLog.Printf(LvINFO, "detect app %s appid:%d relay disconnect, reconnecting the %d times...", app.config.PeerNode, app.id, app.retryRelayNum) gLog.Printf(LvINFO, "detect app %s appid:%d relay disconnect, reconnecting the %d times...", app.config.LogPeerNode(), app.id, app.retryRelayNum)
} }
app.setRelayTunnel(nil) // reset relayTunnel app.setRelayTunnel(nil) // reset relayTunnel
app.retryRelayNum++ app.retryRelayNum++
@@ -256,7 +256,7 @@ func (app *p2pApp) checkRelayTunnel() error {
app.errMsg = err.Error() app.errMsg = err.Error()
if err == ErrPeerOffline && app.retryRelayNum > 2 { // stop retry, waiting for online if err == ErrPeerOffline && app.retryRelayNum > 2 { // stop retry, waiting for online
app.retryRelayNum = retryLimit app.retryRelayNum = retryLimit
gLog.Printf(LvINFO, " %s offline, it will auto reconnect when peer node online", app.config.PeerNode) gLog.Printf(LvINFO, " %s offline, it will auto reconnect when peer node online", app.config.LogPeerNode())
} }
} }
if app.Tunnel() != nil { if app.Tunnel() != nil {
@@ -282,7 +282,7 @@ func (app *p2pApp) buildRelayTunnel() error {
config := app.config config := app.config
initErr := pn.requestPeerInfo(&config) initErr := pn.requestPeerInfo(&config)
if initErr != nil { if initErr != nil {
gLog.Printf(LvERROR, "%s init error:%s", config.PeerNode, initErr) gLog.Printf(LvERROR, "%s init error:%s", config.LogPeerNode(), initErr)
return initErr return initErr
} }
@@ -318,7 +318,7 @@ func (app *p2pApp) buildRelayTunnel() error {
AppID: app.id, AppID: app.id,
AppKey: app.key, AppKey: app.key,
} }
gLog.Printf(LvDEBUG, "sync appkey relay to %s", config.PeerNode) gLog.Printf(LvDEBUG, "sync appkey relay to %s", config.LogPeerNode())
pn.push(config.PeerNode, MsgPushAPPKey, &syncKeyReq) pn.push(config.PeerNode, MsgPushAPPKey, &syncKeyReq)
app.setRelayTunnelID(rtid) app.setRelayTunnelID(rtid)
app.setRelayTunnel(t) app.setRelayTunnel(t)
@@ -330,7 +330,7 @@ func (app *p2pApp) buildRelayTunnel() error {
if config.SrcPort == 0 { if config.SrcPort == 0 {
req := ServerSideSaveMemApp{From: gConf.Network.Node, Node: relayNode, TunnelID: rtid, RelayTunnelID: t.id, AppID: app.id, RelayMode: relayMode} req := ServerSideSaveMemApp{From: gConf.Network.Node, Node: relayNode, TunnelID: rtid, RelayTunnelID: t.id, AppID: app.id, RelayMode: relayMode}
pn.push(config.PeerNode, MsgPushServerSideSaveMemApp, &req) pn.push(config.PeerNode, MsgPushServerSideSaveMemApp, &req)
gLog.Printf(LvDEBUG, "push %s relay ServerSideSaveMemApp: %s", config.PeerNode, prettyJson(req)) gLog.Printf(LvDEBUG, "push %s relay ServerSideSaveMemApp: %s", config.LogPeerNode(), prettyJson(req))
} }
gLog.Printf(LvDEBUG, "%s use tunnel %d", app.config.AppName, t.id) gLog.Printf(LvDEBUG, "%s use tunnel %d", app.config.AppName, t.id)
return nil return nil
@@ -594,8 +594,8 @@ func (app *p2pApp) close() {
func (app *p2pApp) relayHeartbeatLoop() { func (app *p2pApp) relayHeartbeatLoop() {
app.wg.Add(1) app.wg.Add(1)
defer app.wg.Done() defer app.wg.Done()
gLog.Printf(LvDEBUG, "%s appid:%d relayHeartbeat to rtid:%d start", app.config.PeerNode, app.id, app.rtid) gLog.Printf(LvDEBUG, "%s appid:%d relayHeartbeat to rtid:%d start", app.config.LogPeerNode(), app.id, app.rtid)
defer gLog.Printf(LvDEBUG, "%s appid:%d relayHeartbeat to rtid%d end", app.config.PeerNode, app.id, app.rtid) defer gLog.Printf(LvDEBUG, "%s appid:%d relayHeartbeat to rtid%d end", app.config.LogPeerNode(), app.id, app.rtid)
for app.running { for app.running {
if app.RelayTunnel() == nil || !app.RelayTunnel().isRuning() { if app.RelayTunnel() == nil || !app.RelayTunnel().isRuning() {
@@ -606,11 +606,11 @@ func (app *p2pApp) relayHeartbeatLoop() {
AppID: app.id} AppID: app.id}
err := app.RelayTunnel().WriteMessage(app.rtid, MsgP2P, MsgRelayHeartbeat, &req) err := app.RelayTunnel().WriteMessage(app.rtid, MsgP2P, MsgRelayHeartbeat, &req)
if err != nil { if err != nil {
gLog.Printf(LvERROR, "%s appid:%d rtid:%d write relay tunnel heartbeat error %s", app.config.PeerNode, app.id, app.rtid, err) gLog.Printf(LvERROR, "%s appid:%d rtid:%d write relay tunnel heartbeat error %s", app.config.LogPeerNode(), app.id, app.rtid, err)
return return
} }
// TODO: debug relay heartbeat // TODO: debug relay heartbeat
gLog.Printf(LvDEBUG, "%s appid:%d rtid:%d write relay tunnel heartbeat ok", app.config.PeerNode, app.id, app.rtid) gLog.Printf(LvDEBUG, "%s appid:%d rtid:%d write relay tunnel heartbeat ok", app.config.LogPeerNode(), app.id, app.rtid)
time.Sleep(TunnelHeartbeatTime) time.Sleep(TunnelHeartbeatTime)
} }
} }
+977 -978
View File
@@ -1,978 +1,977 @@
package openp2p package openp2p
import ( import (
"bytes" "bytes"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/binary" "encoding/binary"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"math/rand" "math/rand"
"net/http" "net/http"
"net/url" "net/url"
"reflect" "reflect"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
) )
var ( var (
v4l *v4Listener v4l *v4Listener
instance *P2PNetwork instance *P2PNetwork
onceP2PNetwork sync.Once onceP2PNetwork sync.Once
onceV4Listener sync.Once onceV4Listener sync.Once
) )
const ( const (
retryLimit = 20 retryLimit = 20
retryInterval = 10 * time.Second retryInterval = 10 * time.Second
DefaultLoginMaxDelaySeconds = 60 DefaultLoginMaxDelaySeconds = 60
) )
// golang not support float64 const // golang not support float64 const
var ( var (
ma10 float64 = 1.0 / 10 ma10 float64 = 1.0 / 10
ma5 float64 = 1.0 / 5 ma5 float64 = 1.0 / 5
) )
type NodeData struct { type NodeData struct {
NodeID uint64 NodeID uint64
Data []byte Data []byte
} }
type P2PNetwork struct { type P2PNetwork struct {
conn *websocket.Conn conn *websocket.Conn
online bool online bool
running bool running bool
restartCh chan bool restartCh chan bool
wgReconnect sync.WaitGroup wgReconnect sync.WaitGroup
writeMtx sync.Mutex writeMtx sync.Mutex
reqGatewayMtx sync.Mutex reqGatewayMtx sync.Mutex
hbTime time.Time hbTime time.Time
// for sync server time // for sync server time
t1 int64 // nanoSeconds t1 int64 // nanoSeconds
preRtt int64 // nanoSeconds preRtt int64 // nanoSeconds
dt int64 // client faster then server dt nanoSeconds dt int64 // client faster then server dt nanoSeconds
ddtma int64 ddtma int64
ddt int64 // differential of dt ddt int64 // differential of dt
msgMap sync.Map //key: nodeID msgMap sync.Map //key: nodeID
// msgMap map[uint64]chan pushMsg //key: nodeID // msgMap map[uint64]chan pushMsg //key: nodeID
allTunnels sync.Map // key: tid allTunnels sync.Map // key: tid
apps sync.Map //key: config.ID(); value: *p2pApp apps sync.Map //key: config.ID(); value: *p2pApp
limiter *SpeedLimiter limiter *SpeedLimiter
nodeData chan *NodeData nodeData chan *NodeData
sdwan *p2pSDWAN sdwan *p2pSDWAN
tunnelCloseCh chan *P2PTunnel tunnelCloseCh chan *P2PTunnel
loginMaxDelaySeconds int loginMaxDelaySeconds int
} }
type msgCtx struct { type msgCtx struct {
data []byte data []byte
ts time.Time ts time.Time
} }
func P2PNetworkInstance() *P2PNetwork { func P2PNetworkInstance() *P2PNetwork {
if instance == nil { if instance == nil {
onceP2PNetwork.Do(func() { onceP2PNetwork.Do(func() {
instance = &P2PNetwork{ instance = &P2PNetwork{
restartCh: make(chan bool, 1), restartCh: make(chan bool, 1),
tunnelCloseCh: make(chan *P2PTunnel, 100), tunnelCloseCh: make(chan *P2PTunnel, 100),
nodeData: make(chan *NodeData, 10000), nodeData: make(chan *NodeData, 10000),
online: false, online: false,
running: true, running: true,
limiter: newSpeedLimiter(gConf.Network.ShareBandwidth*1024*1024/8, 1), limiter: newSpeedLimiter(gConf.Network.ShareBandwidth*1024*1024/8, 1),
dt: 0, dt: 0,
ddt: 0, ddt: 0,
loginMaxDelaySeconds: DefaultLoginMaxDelaySeconds, loginMaxDelaySeconds: DefaultLoginMaxDelaySeconds,
} }
instance.msgMap.Store(uint64(0), make(chan msgCtx, 50)) // for gateway instance.msgMap.Store(uint64(0), make(chan msgCtx, 50)) // for gateway
instance.StartSDWAN() instance.StartSDWAN()
instance.init() instance.init()
go instance.run() go instance.run()
go func() { go func() {
for { for {
instance.refreshIPv6() instance.refreshIPv6()
time.Sleep(time.Hour) time.Sleep(time.Hour)
} }
}() }()
cleanTempFiles() cleanTempFiles()
}) })
} }
return instance return instance
} }
func (pn *P2PNetwork) run() { func (pn *P2PNetwork) run() {
heartbeatTimer := time.NewTicker(NetworkHeartbeatTime) heartbeatTimer := time.NewTicker(NetworkHeartbeatTime)
pn.t1 = time.Now().UnixNano() pn.t1 = time.Now().UnixNano()
pn.write(MsgHeartbeat, 0, "") pn.write(MsgHeartbeat, 0, "")
for { for {
select { select {
case <-heartbeatTimer.C: case <-heartbeatTimer.C:
pn.t1 = time.Now().UnixNano() pn.t1 = time.Now().UnixNano()
pn.write(MsgHeartbeat, 0, "") pn.write(MsgHeartbeat, 0, "")
case <-pn.restartCh: case <-pn.restartCh:
gLog.Printf(LvDEBUG, "got restart channel") gLog.Printf(LvDEBUG, "got restart channel")
pn.online = false GNetwork.sdwan.reset()
pn.wgReconnect.Wait() // wait read/autorunapp goroutine end pn.online = false
delay := ClientAPITimeout + time.Duration(rand.Int()%pn.loginMaxDelaySeconds)*time.Second pn.wgReconnect.Wait() // wait read/autorunapp goroutine end
time.Sleep(delay) delay := ClientAPITimeout + time.Duration(rand.Int()%pn.loginMaxDelaySeconds)*time.Second
err := pn.init() time.Sleep(delay)
if err != nil { err := pn.init()
gLog.Println(LvERROR, "P2PNetwork init error:", err) if err != nil {
} gLog.Println(LvERROR, "P2PNetwork init error:", err)
gConf.retryAllApp() }
case t := <-pn.tunnelCloseCh: gConf.retryAllApp()
gLog.Printf(LvDEBUG, "got tunnelCloseCh %s", t.config.PeerNode)
pn.apps.Range(func(id, i interface{}) bool { case t := <-pn.tunnelCloseCh:
app := i.(*p2pApp) gLog.Printf(LvDEBUG, "got tunnelCloseCh %s", t.config.LogPeerNode())
if app.DirectTunnel() == t { pn.apps.Range(func(id, i interface{}) bool {
app.setDirectTunnel(nil) app := i.(*p2pApp)
} if app.DirectTunnel() == t {
if app.RelayTunnel() == t { app.setDirectTunnel(nil)
app.setRelayTunnel(nil) }
} if app.RelayTunnel() == t {
return true app.setRelayTunnel(nil)
}) }
} return true
} })
} }
}
func (pn *P2PNetwork) NotifyTunnelClose(t *P2PTunnel) bool { }
select {
case pn.tunnelCloseCh <- t: func (pn *P2PNetwork) NotifyTunnelClose(t *P2PTunnel) bool {
return true select {
default: case pn.tunnelCloseCh <- t:
} return true
return false default:
} }
return false
func (pn *P2PNetwork) Connect(timeout int) bool { }
// waiting for heartbeat
for i := 0; i < (timeout / 1000); i++ { func (pn *P2PNetwork) Connect(timeout int) bool {
if pn.hbTime.After(time.Now().Add(-NetworkHeartbeatTime)) { // waiting for heartbeat
return true for i := 0; i < (timeout / 1000); i++ {
} if pn.hbTime.After(time.Now().Add(-NetworkHeartbeatTime)) {
time.Sleep(time.Second) return true
} }
return false time.Sleep(time.Second)
} }
return false
func (pn *P2PNetwork) runAll() { }
gConf.mtx.Lock() // lock for copy gConf.Apps and the modification of config(it's pointer)
defer gConf.mtx.Unlock() func (pn *P2PNetwork) runAll() {
allApps := gConf.Apps // read a copy, other thread will modify the gConf.Apps gConf.mtx.Lock() // lock for copy gConf.Apps and the modification of config(it's pointer)
for _, config := range allApps { defer gConf.mtx.Unlock()
if config.AppName == "" { allApps := gConf.Apps // read a copy, other thread will modify the gConf.Apps
config.AppName = fmt.Sprintf("%d", config.ID()) for _, config := range allApps {
} if config.AppName == "" {
if config.Enabled == 0 { config.AppName = fmt.Sprintf("%d", config.ID())
continue }
} if config.Enabled == 0 {
if _, ok := pn.apps.Load(config.ID()); ok { continue
continue }
} if _, ok := pn.apps.Load(config.ID()); ok {
continue
config.peerToken = gConf.Network.Token }
gConf.mtx.Unlock() // AddApp will take a period of time, let outside modify gConf
pn.AddApp(*config) config.peerToken = gConf.Network.Token
gConf.mtx.Lock() gConf.mtx.Unlock() // AddApp will take a period of time, let outside modify gConf
pn.AddApp(*config)
} gConf.mtx.Lock()
}
}
func (pn *P2PNetwork) autorunApp() { }
gLog.Println(LvINFO, "autorunApp start")
pn.wgReconnect.Add(1) func (pn *P2PNetwork) autorunApp() {
defer pn.wgReconnect.Done() gLog.Println(LvINFO, "autorunApp start")
for pn.running && pn.online { pn.wgReconnect.Add(1)
time.Sleep(time.Second) defer pn.wgReconnect.Done()
pn.runAll() for pn.running && pn.online {
} time.Sleep(time.Second)
gLog.Println(LvINFO, "autorunApp end") pn.runAll()
} }
gLog.Println(LvINFO, "autorunApp end")
func (pn *P2PNetwork) addRelayTunnel(config AppConfig) (*P2PTunnel, uint64, string, error) { }
gLog.Printf(LvINFO, "addRelayTunnel to %s start", config.PeerNode)
defer gLog.Printf(LvINFO, "addRelayTunnel to %s end", config.PeerNode) func (pn *P2PNetwork) addRelayTunnel(config AppConfig) (*P2PTunnel, uint64, string, error) {
relayConfig := AppConfig{ gLog.Printf(LvINFO, "addRelayTunnel to %s start", config.LogPeerNode())
PeerNode: config.RelayNode, defer gLog.Printf(LvINFO, "addRelayTunnel to %s end", config.LogPeerNode())
peerToken: config.peerToken} relayConfig := AppConfig{
relayMode := "private" PeerNode: config.RelayNode,
if relayConfig.PeerNode == "" { peerToken: config.peerToken,
// find existing relay tunnel relayMode: "private"}
pn.apps.Range(func(id, i interface{}) bool { if relayConfig.PeerNode == "" {
app := i.(*p2pApp) // find existing relay tunnel
if app.config.PeerNode != config.PeerNode { pn.apps.Range(func(id, i interface{}) bool {
return true app := i.(*p2pApp)
} if app.config.PeerNode != config.PeerNode {
if app.RelayTunnel() == nil { return true
return true }
} if app.RelayTunnel() == nil {
relayConfig.PeerNode = app.RelayTunnel().config.PeerNode return true
gLog.Printf(LvDEBUG, "found existing relay tunnel %s", relayConfig.PeerNode) }
return false relayConfig.PeerNode = app.RelayTunnel().config.PeerNode
}) gLog.Printf(LvDEBUG, "found existing relay tunnel %s", relayConfig.LogPeerNode())
if relayConfig.PeerNode == "" { // request relay node return false
pn.reqGatewayMtx.Lock() })
pn.write(MsgRelay, MsgRelayNodeReq, &RelayNodeReq{config.PeerNode}) if relayConfig.PeerNode == "" { // request relay node
head, body := pn.read("", MsgRelay, MsgRelayNodeRsp, ClientAPITimeout) pn.reqGatewayMtx.Lock()
pn.reqGatewayMtx.Unlock() pn.write(MsgRelay, MsgRelayNodeReq, &RelayNodeReq{config.PeerNode})
if head == nil { head, body := pn.read("", MsgRelay, MsgRelayNodeRsp, ClientAPITimeout)
return nil, 0, "", errors.New("read MsgRelayNodeRsp error") pn.reqGatewayMtx.Unlock()
} if head == nil {
rsp := RelayNodeRsp{} return nil, 0, "", errors.New("read MsgRelayNodeRsp error")
if err := json.Unmarshal(body, &rsp); err != nil { }
return nil, 0, "", errors.New("unmarshal MsgRelayNodeRsp error") rsp := RelayNodeRsp{}
} if err := json.Unmarshal(body, &rsp); err != nil {
if rsp.RelayName == "" || rsp.RelayToken == 0 { return nil, 0, "", errors.New("unmarshal MsgRelayNodeRsp error")
gLog.Printf(LvERROR, "MsgRelayNodeReq error") }
return nil, 0, "", errors.New("MsgRelayNodeReq error") if rsp.RelayName == "" || rsp.RelayToken == 0 {
} gLog.Printf(LvERROR, "MsgRelayNodeReq error")
gLog.Printf(LvDEBUG, "got relay node:%s", rsp.RelayName) return nil, 0, "", errors.New("MsgRelayNodeReq error")
}
relayConfig.PeerNode = rsp.RelayName gLog.Printf(LvDEBUG, "got relay node:%s", relayConfig.LogPeerNode())
relayConfig.peerToken = rsp.RelayToken
relayMode = rsp.Mode relayConfig.PeerNode = rsp.RelayName
} relayConfig.peerToken = rsp.RelayToken
relayConfig.relayMode = rsp.Mode
} }
///
t, err := pn.addDirectTunnel(relayConfig, 0) }
if err != nil { ///
gLog.Println(LvERROR, "direct connect error:", err) t, err := pn.addDirectTunnel(relayConfig, 0)
return nil, 0, "", ErrConnectRelayNode // relay offline will stop retry if err != nil {
} gLog.Println(LvERROR, "direct connect error:", err)
// notify peer addRelayTunnel return nil, 0, "", ErrConnectRelayNode // relay offline will stop retry
req := AddRelayTunnelReq{ }
From: gConf.Network.Node, // notify peer addRelayTunnel
RelayName: relayConfig.PeerNode, req := AddRelayTunnelReq{
RelayToken: relayConfig.peerToken, From: gConf.Network.Node,
RelayMode: relayMode, RelayName: relayConfig.PeerNode,
RelayTunnelID: t.id, RelayToken: relayConfig.peerToken,
} RelayMode: relayConfig.relayMode,
gLog.Printf(LvDEBUG, "push %s the relay node(%s)", config.PeerNode, relayConfig.PeerNode) RelayTunnelID: t.id,
pn.push(config.PeerNode, MsgPushAddRelayTunnelReq, &req) }
gLog.Printf(LvDEBUG, "push %s the relay node(%s)", config.LogPeerNode(), relayConfig.LogPeerNode())
// wait relay ready pn.push(config.PeerNode, MsgPushAddRelayTunnelReq, &req)
head, body := pn.read(config.PeerNode, MsgPush, MsgPushAddRelayTunnelRsp, PeerAddRelayTimeount)
if head == nil { // wait relay ready
gLog.Printf(LvERROR, "read MsgPushAddRelayTunnelRsp error") head, body := pn.read(config.PeerNode, MsgPush, MsgPushAddRelayTunnelRsp, PeerAddRelayTimeount)
return nil, 0, "", errors.New("read MsgPushAddRelayTunnelRsp error") if head == nil {
} gLog.Printf(LvERROR, "read MsgPushAddRelayTunnelRsp error")
rspID := TunnelMsg{} return nil, 0, "", errors.New("read MsgPushAddRelayTunnelRsp error")
if err = json.Unmarshal(body, &rspID); err != nil { }
gLog.Println(LvDEBUG, ErrPeerConnectRelay) rspID := TunnelMsg{}
return nil, 0, "", ErrPeerConnectRelay if err = json.Unmarshal(body, &rspID); err != nil {
} gLog.Println(LvDEBUG, ErrPeerConnectRelay)
return t, rspID.ID, relayMode, err return nil, 0, "", ErrPeerConnectRelay
} }
return t, rspID.ID, relayConfig.relayMode, err
// use *AppConfig to save status }
func (pn *P2PNetwork) AddApp(config AppConfig) error {
gLog.Printf(LvINFO, "addApp %s to %s:%s:%d start", config.AppName, config.PeerNode, config.DstHost, config.DstPort) // use *AppConfig to save status
defer gLog.Printf(LvINFO, "addApp %s to %s:%s:%d end", config.AppName, config.PeerNode, config.DstHost, config.DstPort) func (pn *P2PNetwork) AddApp(config AppConfig) error {
if !pn.online { gLog.Printf(LvINFO, "addApp %s to %s:%s:%d start", config.AppName, config.LogPeerNode(), config.DstHost, config.DstPort)
return errors.New("P2PNetwork offline") defer gLog.Printf(LvINFO, "addApp %s to %s:%s:%d end", config.AppName, config.LogPeerNode(), config.DstHost, config.DstPort)
} if !pn.online {
if _, ok := pn.msgMap.Load(NodeNameToID(config.PeerNode)); !ok { return errors.New("P2PNetwork offline")
pn.msgMap.Store(NodeNameToID(config.PeerNode), make(chan msgCtx, 50)) }
} if _, ok := pn.msgMap.Load(NodeNameToID(config.PeerNode)); !ok {
// check if app already exist? pn.msgMap.Store(NodeNameToID(config.PeerNode), make(chan msgCtx, 50))
if _, ok := pn.apps.Load(config.ID()); ok { }
return errors.New("P2PApp already exist") // check if app already exist?
} if _, ok := pn.apps.Load(config.ID()); ok {
return errors.New("P2PApp already exist")
app := p2pApp{ }
// tunnel: t,
id: rand.Uint64(), app := p2pApp{
key: rand.Uint64(), // tunnel: t,
config: config, id: rand.Uint64(),
iptree: NewIPTree(config.Whitelist), key: rand.Uint64(),
running: true, config: config,
hbTimeRelay: time.Now(), iptree: NewIPTree(config.Whitelist),
} running: true,
if _, ok := pn.msgMap.Load(NodeNameToID(config.PeerNode)); !ok { hbTimeRelay: time.Now(),
pn.msgMap.Store(NodeNameToID(config.PeerNode), make(chan msgCtx, 50)) }
} if _, ok := pn.msgMap.Load(NodeNameToID(config.PeerNode)); !ok {
pn.apps.Store(config.ID(), &app) pn.msgMap.Store(NodeNameToID(config.PeerNode), make(chan msgCtx, 50))
gLog.Printf(LvDEBUG, "Store app %d", config.ID()) }
go app.checkP2PTunnel() pn.apps.Store(config.ID(), &app)
return nil gLog.Printf(LvDEBUG, "Store app %d", config.ID())
} go app.checkP2PTunnel()
return nil
func (pn *P2PNetwork) DeleteApp(config AppConfig) { }
gLog.Printf(LvINFO, "DeleteApp %s to %s:%s:%d start", config.AppName, config.PeerNode, config.DstHost, config.DstPort)
defer gLog.Printf(LvINFO, "DeleteApp %s to %s:%s:%d end", config.AppName, config.PeerNode, config.DstHost, config.DstPort) func (pn *P2PNetwork) DeleteApp(config AppConfig) {
// close the apps of this config gLog.Printf(LvINFO, "DeleteApp %s to %s:%s:%d start", config.AppName, config.LogPeerNode(), config.DstHost, config.DstPort)
i, ok := pn.apps.Load(config.ID()) defer gLog.Printf(LvINFO, "DeleteApp %s to %s:%s:%d end", config.AppName, config.LogPeerNode(), config.DstHost, config.DstPort)
if ok { // close the apps of this config
app := i.(*p2pApp) i, ok := pn.apps.Load(config.ID())
gLog.Printf(LvINFO, "app %s exist, delete it", app.config.AppName) if ok {
app.close() app := i.(*p2pApp)
pn.apps.Delete(config.ID()) gLog.Printf(LvINFO, "app %s exist, delete it", app.config.AppName)
} app.close()
} pn.apps.Delete(config.ID())
}
func (pn *P2PNetwork) findTunnel(peerNode string) (t *P2PTunnel) { }
t = nil
// find existing tunnel to peer func (pn *P2PNetwork) findTunnel(peerNode string) (t *P2PTunnel) {
pn.allTunnels.Range(func(id, i interface{}) bool { t = nil
tmpt := i.(*P2PTunnel) // find existing tunnel to peer
if tmpt.config.PeerNode == peerNode { pn.allTunnels.Range(func(id, i interface{}) bool {
gLog.Println(LvINFO, "tunnel already exist ", peerNode) tmpt := i.(*P2PTunnel)
isActive := tmpt.checkActive() if tmpt.config.PeerNode == peerNode {
// inactive, close it gLog.Println(LvINFO, "tunnel already exist ", peerNode)
if !isActive { isActive := tmpt.checkActive()
gLog.Println(LvINFO, "but it's not active, close it ", peerNode) // inactive, close it
tmpt.close() if !isActive {
} else { gLog.Println(LvINFO, "but it's not active, close it ", peerNode)
t = tmpt tmpt.close()
} } else {
return false t = tmpt
} }
return true return false
}) }
return t return true
} })
return t
func (pn *P2PNetwork) addDirectTunnel(config AppConfig, tid uint64) (t *P2PTunnel, err error) { }
gLog.Printf(LvDEBUG, "addDirectTunnel %s%d to %s:%s:%d tid:%d start", config.Protocol, config.SrcPort, config.PeerNode, config.DstHost, config.DstPort, tid)
defer gLog.Printf(LvDEBUG, "addDirectTunnel %s%d to %s:%s:%d tid:%d end", config.Protocol, config.SrcPort, config.PeerNode, config.DstHost, config.DstPort, tid) func (pn *P2PNetwork) addDirectTunnel(config AppConfig, tid uint64) (t *P2PTunnel, err error) {
isClient := false gLog.Printf(LvDEBUG, "addDirectTunnel %s%d to %s:%s:%d tid:%d start", config.Protocol, config.SrcPort, config.LogPeerNode(), config.DstHost, config.DstPort, tid)
// client side tid=0, assign random uint64 defer gLog.Printf(LvDEBUG, "addDirectTunnel %s%d to %s:%s:%d tid:%d end", config.Protocol, config.SrcPort, config.LogPeerNode(), config.DstHost, config.DstPort, tid)
if tid == 0 { isClient := false
tid = rand.Uint64() // client side tid=0, assign random uint64
isClient = true if tid == 0 {
} tid = rand.Uint64()
if _, ok := pn.msgMap.Load(NodeNameToID(config.PeerNode)); !ok { isClient = true
pn.msgMap.Store(NodeNameToID(config.PeerNode), make(chan msgCtx, 50)) }
} if _, ok := pn.msgMap.Load(NodeNameToID(config.PeerNode)); !ok {
pn.msgMap.Store(NodeNameToID(config.PeerNode), make(chan msgCtx, 50))
// server side }
if !isClient {
t, err = pn.newTunnel(config, tid, isClient) // server side
return t, err // always return if !isClient {
} t, err = pn.newTunnel(config, tid, isClient)
// client side return t, err // always return
// peer info }
initErr := pn.requestPeerInfo(&config) // client side
if initErr != nil { // peer info
gLog.Printf(LvERROR, "%s init error:%s", config.PeerNode, initErr) initErr := pn.requestPeerInfo(&config)
if initErr != nil {
return nil, initErr gLog.Printf(LvERROR, "%s init error:%s", config.LogPeerNode(), initErr)
}
gLog.Printf(LvDEBUG, "config.peerNode=%s,config.peerVersion=%s,config.peerIP=%s,config.peerLanIP=%s,gConf.Network.publicIP=%s,config.peerIPv6=%s,config.hasIPv4=%d,config.hasUPNPorNATPMP=%d,gConf.Network.hasIPv4=%d,gConf.Network.hasUPNPorNATPMP=%d,config.peerNatType=%d,gConf.Network.natType=%d,", return nil, initErr
config.PeerNode, config.peerVersion, config.peerIP, config.peerLanIP, gConf.Network.publicIP, config.peerIPv6, config.hasIPv4, config.hasUPNPorNATPMP, gConf.Network.hasIPv4, gConf.Network.hasUPNPorNATPMP, config.peerNatType, gConf.Network.natType) }
// try Intranet gLog.Printf(LvDEBUG, "config.peerNode=%s,config.peerVersion=%s,config.peerIP=%s,config.peerLanIP=%s,gConf.Network.publicIP=%s,config.peerIPv6=%s,config.hasIPv4=%d,config.hasUPNPorNATPMP=%d,gConf.Network.hasIPv4=%d,gConf.Network.hasUPNPorNATPMP=%d,config.peerNatType=%d,gConf.Network.natType=%d,",
if config.peerIP == gConf.Network.publicIP && compareVersion(config.peerVersion, SupportIntranetVersion) >= 0 { // old version client has no peerLanIP config.LogPeerNode(), config.peerVersion, config.peerIP, config.peerLanIP, gConf.Network.publicIP, config.peerIPv6, config.hasIPv4, config.hasUPNPorNATPMP, gConf.Network.hasIPv4, gConf.Network.hasUPNPorNATPMP, config.peerNatType, gConf.Network.natType)
gLog.Println(LvINFO, "try Intranet") // try Intranet
config.linkMode = LinkModeIntranet if config.peerIP == gConf.Network.publicIP && compareVersion(config.peerVersion, SupportIntranetVersion) >= 0 { // old version client has no peerLanIP
config.isUnderlayServer = 0 gLog.Println(LvINFO, "try Intranet")
if t, err = pn.newTunnel(config, tid, isClient); err == nil { config.linkMode = LinkModeIntranet
return t, nil config.isUnderlayServer = 0
} if t, err = pn.newTunnel(config, tid, isClient); err == nil {
} return t, nil
// try TCP6 }
if IsIPv6(config.peerIPv6) && IsIPv6(gConf.IPv6()) { }
gLog.Println(LvINFO, "try TCP6") // try TCP6
config.linkMode = LinkModeTCP6 if IsIPv6(config.peerIPv6) && IsIPv6(gConf.IPv6()) {
config.isUnderlayServer = 0 gLog.Println(LvINFO, "try TCP6")
if t, err = pn.newTunnel(config, tid, isClient); err == nil { config.linkMode = LinkModeTCP6
return t, nil config.isUnderlayServer = 0
} if t, err = pn.newTunnel(config, tid, isClient); err == nil {
} return t, nil
}
// try UDP6? maybe no }
// try TCP4 // try UDP6? maybe no
if config.hasIPv4 == 1 || gConf.Network.hasIPv4 == 1 || config.hasUPNPorNATPMP == 1 || gConf.Network.hasUPNPorNATPMP == 1 {
gLog.Println(LvINFO, "try TCP4") // try TCP4
config.linkMode = LinkModeTCP4 if config.hasIPv4 == 1 || gConf.Network.hasIPv4 == 1 || config.hasUPNPorNATPMP == 1 || gConf.Network.hasUPNPorNATPMP == 1 {
if gConf.Network.hasIPv4 == 1 || gConf.Network.hasUPNPorNATPMP == 1 { gLog.Println(LvINFO, "try TCP4")
config.isUnderlayServer = 1 config.linkMode = LinkModeTCP4
} else { if gConf.Network.hasIPv4 == 1 || gConf.Network.hasUPNPorNATPMP == 1 {
config.isUnderlayServer = 0 config.isUnderlayServer = 1
} } else {
if t, err = pn.newTunnel(config, tid, isClient); err == nil { config.isUnderlayServer = 0
return t, nil }
} if t, err = pn.newTunnel(config, tid, isClient); err == nil {
} return t, nil
// try UDP4? maybe no }
var primaryPunchFunc func() (*P2PTunnel, error) }
var secondaryPunchFunc func() (*P2PTunnel, error) // try UDP4? maybe no
funcUDP := func() (t *P2PTunnel, err error) { var primaryPunchFunc func() (*P2PTunnel, error)
if config.PunchPriority&PunchPriorityUDPDisable != 0 { var secondaryPunchFunc func() (*P2PTunnel, error)
return funcUDP := func() (t *P2PTunnel, err error) {
} if config.PunchPriority&PunchPriorityUDPDisable != 0 {
// try UDPPunch return
for i := 0; i < Cone2ConeUDPPunchMaxRetry; i++ { // when both 2 nats has restrict firewall, simultaneous punching needs to be very precise, it takes a few tries }
if config.peerNatType == NATCone || gConf.Network.natType == NATCone { // try UDPPunch
gLog.Println(LvINFO, "try UDP4 Punch") for i := 0; i < Cone2ConeUDPPunchMaxRetry; i++ { // when both 2 nats has restrict firewall, simultaneous punching needs to be very precise, it takes a few tries
config.linkMode = LinkModeUDPPunch if config.peerNatType == NATCone || gConf.Network.natType == NATCone {
config.isUnderlayServer = 0 gLog.Println(LvINFO, "try UDP4 Punch")
if t, err = pn.newTunnel(config, tid, isClient); err == nil { config.linkMode = LinkModeUDPPunch
return t, nil config.isUnderlayServer = 0
} if t, err = pn.newTunnel(config, tid, isClient); err == nil {
} return t, nil
if !(config.peerNatType == NATCone && gConf.Network.natType == NATCone) { // not cone2cone, no more try }
break }
} if !(config.peerNatType == NATCone && gConf.Network.natType == NATCone) { // not cone2cone, no more try
} break
return }
} }
funcTCP := func() (t *P2PTunnel, err error) { return
if config.PunchPriority&PunchPriorityTCPDisable != 0 { }
return funcTCP := func() (t *P2PTunnel, err error) {
} if config.PunchPriority&PunchPriorityTCPDisable != 0 {
// try TCPPunch return
for i := 0; i < Cone2ConeTCPPunchMaxRetry; i++ { // when both 2 nats has restrict firewall, simultaneous punching needs to be very precise, it takes a few tries }
if config.peerNatType == NATCone || gConf.Network.natType == NATCone { // try TCPPunch
gLog.Println(LvINFO, "try TCP4 Punch") for i := 0; i < Cone2ConeTCPPunchMaxRetry; i++ { // when both 2 nats has restrict firewall, simultaneous punching needs to be very precise, it takes a few tries
config.linkMode = LinkModeTCPPunch if config.peerNatType == NATCone || gConf.Network.natType == NATCone {
config.isUnderlayServer = 0 gLog.Println(LvINFO, "try TCP4 Punch")
if t, err = pn.newTunnel(config, tid, isClient); err == nil { config.linkMode = LinkModeTCPPunch
gLog.Println(LvINFO, "TCP4 Punch ok") config.isUnderlayServer = 0
return t, nil if t, err = pn.newTunnel(config, tid, isClient); err == nil {
} gLog.Println(LvINFO, "TCP4 Punch ok")
} return t, nil
} }
return }
} }
if config.PunchPriority&PunchPriorityTCPFirst != 0 { return
primaryPunchFunc = funcTCP }
secondaryPunchFunc = funcUDP if config.PunchPriority&PunchPriorityTCPFirst != 0 {
} else { primaryPunchFunc = funcTCP
primaryPunchFunc = funcTCP secondaryPunchFunc = funcUDP
secondaryPunchFunc = funcUDP } else {
} primaryPunchFunc = funcTCP
if t, err = primaryPunchFunc(); t != nil && err == nil { secondaryPunchFunc = funcUDP
return t, err }
} if t, err = primaryPunchFunc(); t != nil && err == nil {
if t, err = secondaryPunchFunc(); t != nil && err == nil { return t, err
return t, err }
} if t, err = secondaryPunchFunc(); t != nil && err == nil {
return t, err
// TODO: s2s won't return err }
return nil, err
} // TODO: s2s won't return err
return nil, err
func (pn *P2PNetwork) newTunnel(config AppConfig, tid uint64, isClient bool) (t *P2PTunnel, err error) { }
if isClient { // only client side find existing tunnel
if existTunnel := pn.findTunnel(config.PeerNode); existTunnel != nil { func (pn *P2PNetwork) newTunnel(config AppConfig, tid uint64, isClient bool) (t *P2PTunnel, err error) {
return existTunnel, nil if isClient { // only client side find existing tunnel
} if existTunnel := pn.findTunnel(config.PeerNode); existTunnel != nil {
} return existTunnel, nil
}
t = &P2PTunnel{pn: pn, }
config: config,
id: tid, t = &P2PTunnel{
writeData: make(chan []byte, WriteDataChanSize), config: config,
writeDataSmall: make(chan []byte, WriteDataChanSize/30), id: tid,
} writeData: make(chan []byte, WriteDataChanSize),
t.initPort() writeDataSmall: make(chan []byte, WriteDataChanSize/30),
if isClient { }
if err = t.connect(); err != nil { t.initPort()
gLog.Println(LvERROR, "p2pTunnel connect error:", err) if isClient {
return if err = t.connect(); err != nil {
} gLog.Println(LvERROR, "p2pTunnel connect error:", err)
} else { return
if err = t.listen(); err != nil { }
gLog.Println(LvERROR, "p2pTunnel listen error:", err) } else {
return if err = t.listen(); err != nil {
} gLog.Println(LvERROR, "p2pTunnel listen error:", err)
} return
// store it when success }
gLog.Printf(LvDEBUG, "store tunnel %d", tid) }
pn.allTunnels.Store(tid, t) // store it when success
return gLog.Printf(LvDEBUG, "store tunnel %d", tid)
} pn.allTunnels.Store(tid, t)
func (pn *P2PNetwork) init() error { return
gLog.Println(LvINFO, "P2PNetwork init start") }
defer gLog.Println(LvINFO, "P2PNetwork init end") func (pn *P2PNetwork) init() error {
pn.wgReconnect.Add(1) gLog.Println(LvINFO, "P2PNetwork init start")
defer pn.wgReconnect.Done() defer gLog.Println(LvINFO, "P2PNetwork init end")
var err error pn.wgReconnect.Add(1)
for { defer pn.wgReconnect.Done()
// detect nat type var err error
gConf.Network.publicIP, gConf.Network.natType, err = getNATType(gConf.Network.ServerHost, gConf.Network.UDPPort1, gConf.Network.UDPPort2) for {
if err != nil { // detect nat type
gLog.Println(LvDEBUG, "detect NAT type error:", err) gConf.Network.publicIP, gConf.Network.natType, err = getNATType(gConf.Network.ServerHost, gConf.Network.UDPPort1, gConf.Network.UDPPort2)
break if err != nil {
} gLog.Println(LvDEBUG, "detect NAT type error:", err)
if gConf.Network.hasIPv4 == 0 && gConf.Network.hasUPNPorNATPMP == 0 { // if already has ipv4 or upnp no need test again break
gConf.Network.hasIPv4, gConf.Network.hasUPNPorNATPMP = publicIPTest(gConf.Network.publicIP, gConf.Network.TCPPort) }
} if gConf.Network.hasIPv4 == 0 && gConf.Network.hasUPNPorNATPMP == 0 { // if already has ipv4 or upnp no need test again
gConf.Network.hasIPv4, gConf.Network.hasUPNPorNATPMP = publicIPTest(gConf.Network.publicIP, gConf.Network.TCPPort)
// for testcase }
if strings.Contains(gConf.Network.Node, "openp2pS2STest") {
gConf.Network.natType = NATSymmetric // for testcase
gConf.Network.hasIPv4 = 0 if strings.Contains(gConf.Network.Node, "openp2pS2STest") {
gConf.Network.hasUPNPorNATPMP = 0 gConf.Network.natType = NATSymmetric
gLog.Println(LvINFO, "openp2pS2STest debug") gConf.Network.hasIPv4 = 0
gConf.Network.hasUPNPorNATPMP = 0
} gLog.Println(LvINFO, "openp2pS2STest debug")
if strings.Contains(gConf.Network.Node, "openp2pC2CTest") {
gConf.Network.natType = NATCone }
gConf.Network.hasIPv4 = 0 if strings.Contains(gConf.Network.Node, "openp2pC2CTest") {
gConf.Network.hasUPNPorNATPMP = 0 gConf.Network.natType = NATCone
gLog.Println(LvINFO, "openp2pC2CTest debug") gConf.Network.hasIPv4 = 0
} gConf.Network.hasUPNPorNATPMP = 0
gLog.Println(LvINFO, "openp2pC2CTest debug")
if gConf.Network.hasIPv4 == 1 || gConf.Network.hasUPNPorNATPMP == 1 { }
onceV4Listener.Do(func() {
v4l = &v4Listener{port: gConf.Network.TCPPort} if gConf.Network.hasIPv4 == 1 || gConf.Network.hasUPNPorNATPMP == 1 {
go v4l.start() onceV4Listener.Do(func() {
}) v4l = &v4Listener{port: gConf.Network.TCPPort}
} go v4l.start()
gLog.Printf(LvINFO, "hasIPv4:%d, UPNP:%d, NAT type:%d, publicIP:%s", gConf.Network.hasIPv4, gConf.Network.hasUPNPorNATPMP, gConf.Network.natType, gConf.Network.publicIP) })
gatewayURL := fmt.Sprintf("%s:%d", gConf.Network.ServerHost, gConf.Network.ServerPort) }
uri := "/api/v1/login" gLog.Printf(LvINFO, "hasIPv4:%d, UPNP:%d, NAT type:%d, publicIP:%s", gConf.Network.hasIPv4, gConf.Network.hasUPNPorNATPMP, gConf.Network.natType, gConf.Network.publicIP)
caCertPool, errCert := x509.SystemCertPool() gatewayURL := fmt.Sprintf("%s:%d", gConf.Network.ServerHost, gConf.Network.ServerPort)
if errCert != nil { uri := "/api/v1/login"
gLog.Println(LvERROR, "Failed to load system root CAs:", errCert) caCertPool, errCert := x509.SystemCertPool()
} else { if errCert != nil {
caCertPool = x509.NewCertPool() gLog.Println(LvERROR, "Failed to load system root CAs:", errCert)
} caCertPool = x509.NewCertPool()
caCertPool.AppendCertsFromPEM([]byte(rootCA)) }
caCertPool.AppendCertsFromPEM([]byte(ISRGRootX1)) caCertPool.AppendCertsFromPEM([]byte(rootCA))
config := tls.Config{ caCertPool.AppendCertsFromPEM([]byte(ISRGRootX1))
RootCAs: caCertPool, config := tls.Config{
InsecureSkipVerify: false} // let's encrypt root cert "DST Root CA X3" expired at 2021/09/29. many old system(windows server 2008 etc) will not trust our cert RootCAs: caCertPool,
websocket.DefaultDialer.TLSClientConfig = &config InsecureSkipVerify: false} // let's encrypt root cert "DST Root CA X3" expired at 2021/09/29. many old system(windows server 2008 etc) will not trust our cert
websocket.DefaultDialer.HandshakeTimeout = ClientAPITimeout websocket.DefaultDialer.TLSClientConfig = &config
u := url.URL{Scheme: "wss", Host: gatewayURL, Path: uri} websocket.DefaultDialer.HandshakeTimeout = ClientAPITimeout
q := u.Query() u := url.URL{Scheme: "wss", Host: gatewayURL, Path: uri}
q.Add("node", gConf.Network.Node) q := u.Query()
q.Add("token", fmt.Sprintf("%d", gConf.Network.Token)) q.Add("node", gConf.Network.Node)
q.Add("version", OpenP2PVersion) q.Add("token", fmt.Sprintf("%d", gConf.Network.Token))
q.Add("nattype", fmt.Sprintf("%d", gConf.Network.natType)) q.Add("version", OpenP2PVersion)
q.Add("sharebandwidth", fmt.Sprintf("%d", gConf.Network.ShareBandwidth)) q.Add("nattype", fmt.Sprintf("%d", gConf.Network.natType))
u.RawQuery = q.Encode() q.Add("sharebandwidth", fmt.Sprintf("%d", gConf.Network.ShareBandwidth))
var ws *websocket.Conn u.RawQuery = q.Encode()
ws, _, err = websocket.DefaultDialer.Dial(u.String(), nil) var ws *websocket.Conn
if err != nil { ws, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
gLog.Println(LvERROR, "Dial error:", err) if err != nil {
break gLog.Println(LvERROR, "Dial error:", err)
} break
pn.running = true }
pn.online = true pn.running = true
pn.conn = ws pn.online = true
localAddr := strings.Split(ws.LocalAddr().String(), ":") pn.conn = ws
if len(localAddr) == 2 { localAddr := strings.Split(ws.LocalAddr().String(), ":")
gConf.Network.localIP = localAddr[0] if len(localAddr) == 2 {
} else { gConf.Network.localIP = localAddr[0]
err = errors.New("get local ip failed") } else {
break err = errors.New("get local ip failed")
} break
go pn.readLoop() }
gConf.Network.mac = getmac(gConf.Network.localIP) go pn.readLoop()
gConf.Network.os = getOsName() gConf.Network.mac = getmac(gConf.Network.localIP)
go func() { gConf.Network.os = getOsName()
req := ReportBasic{ go func() {
Mac: gConf.Network.mac, req := ReportBasic{
LanIP: gConf.Network.localIP, Mac: gConf.Network.mac,
OS: gConf.Network.os, LanIP: gConf.Network.localIP,
HasIPv4: gConf.Network.hasIPv4, OS: gConf.Network.os,
HasUPNPorNATPMP: gConf.Network.hasUPNPorNATPMP, HasIPv4: gConf.Network.hasIPv4,
Version: OpenP2PVersion, HasUPNPorNATPMP: gConf.Network.hasUPNPorNATPMP,
} Version: OpenP2PVersion,
rsp := netInfo() }
gLog.Println(LvDEBUG, "netinfo:", rsp) rsp := netInfo()
if rsp != nil && rsp.Country != "" { gLog.Println(LvDEBUG, "netinfo:", rsp)
if IsIPv6(rsp.IP.String()) { if rsp != nil && rsp.Country != "" {
gConf.setIPv6(rsp.IP.String()) if IsIPv6(rsp.IP.String()) {
} gConf.setIPv6(rsp.IP.String())
req.NetInfo = *rsp }
} else { req.NetInfo = *rsp
pn.refreshIPv6() } else {
} pn.refreshIPv6()
req.IPv6 = gConf.IPv6() }
pn.write(MsgReport, MsgReportBasic, &req) req.IPv6 = gConf.IPv6()
}() pn.write(MsgReport, MsgReportBasic, &req)
go pn.autorunApp() }()
pn.write(MsgSDWAN, MsgSDWANInfoReq, nil) go pn.autorunApp()
gLog.Println(LvDEBUG, "P2PNetwork init ok") pn.write(MsgSDWAN, MsgSDWANInfoReq, nil)
break gLog.Println(LvDEBUG, "P2PNetwork init ok")
} break
if err != nil { }
// init failed, retry if err != nil {
pn.close() // init failed, retry
gLog.Println(LvERROR, "P2PNetwork init error:", err) pn.close()
} gLog.Println(LvERROR, "P2PNetwork init error:", err)
return err }
} return err
}
func (pn *P2PNetwork) handleMessage(msg []byte) {
head := openP2PHeader{} func (pn *P2PNetwork) handleMessage(msg []byte) {
err := binary.Read(bytes.NewReader(msg[:openP2PHeaderSize]), binary.LittleEndian, &head) head := openP2PHeader{}
if err != nil { err := binary.Read(bytes.NewReader(msg[:openP2PHeaderSize]), binary.LittleEndian, &head)
gLog.Println(LvERROR, "handleMessage error:", err) if err != nil {
return gLog.Println(LvERROR, "handleMessage error:", err)
} return
switch head.MainType { }
case MsgLogin: switch head.MainType {
// gLog.Println(LevelINFO,string(msg)) case MsgLogin:
rsp := LoginRsp{} // gLog.Println(LevelINFO,string(msg))
if err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp); err != nil { rsp := LoginRsp{}
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(rsp), err) if err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp); err != nil {
return gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(rsp), err)
} return
if rsp.Error != 0 { }
gLog.Printf(LvERROR, "login error:%d, detail:%s", rsp.Error, rsp.Detail) if rsp.Error != 0 {
pn.running = false gLog.Printf(LvERROR, "login error:%d, detail:%s", rsp.Error, rsp.Detail)
} else { pn.running = false
gConf.Network.Token = rsp.Token } else {
gConf.Network.User = rsp.User gConf.setToken(rsp.Token)
gConf.setToken(rsp.Token) gConf.setUser(rsp.User)
gConf.setUser(rsp.User) if len(rsp.Node) >= MinNodeNameLen {
if len(rsp.Node) >= MinNodeNameLen { gConf.setNode(rsp.Node)
gConf.setNode(rsp.Node) }
} if rsp.LoginMaxDelay > 0 {
if rsp.LoginMaxDelay > 0 { pn.loginMaxDelaySeconds = rsp.LoginMaxDelay
pn.loginMaxDelaySeconds = rsp.LoginMaxDelay }
} gLog.Printf(LvINFO, "login ok. user=%s,node=%s", rsp.User, rsp.Node)
gLog.Printf(LvINFO, "login ok. user=%s,node=%s", rsp.User, rsp.Node) }
} case MsgHeartbeat:
case MsgHeartbeat: gLog.Printf(LvDev, "P2PNetwork heartbeat ok")
gLog.Printf(LvDev, "P2PNetwork heartbeat ok") pn.hbTime = time.Now()
pn.hbTime = time.Now() rtt := pn.hbTime.UnixNano() - pn.t1
rtt := pn.hbTime.UnixNano() - pn.t1 if rtt > int64(PunchTsDelay) || (pn.preRtt > 0 && rtt > pn.preRtt*5) {
if rtt > int64(PunchTsDelay) || (pn.preRtt > 0 && rtt > pn.preRtt*5) { gLog.Printf(LvINFO, "rtt=%d too large ignore", rtt)
gLog.Printf(LvINFO, "rtt=%d too large ignore", rtt) return // invalid hb rsp
return // invalid hb rsp }
} pn.preRtt = rtt
pn.preRtt = rtt t2 := int64(binary.LittleEndian.Uint64(msg[openP2PHeaderSize : openP2PHeaderSize+8]))
t2 := int64(binary.LittleEndian.Uint64(msg[openP2PHeaderSize : openP2PHeaderSize+8])) thisdt := pn.t1 + rtt/2 - t2
thisdt := pn.t1 + rtt/2 - t2 newdt := thisdt
newdt := thisdt if pn.dt != 0 {
if pn.dt != 0 { ddt := thisdt - pn.dt
ddt := thisdt - pn.dt pn.ddt = ddt
pn.ddt = ddt if pn.ddtma == 0 {
if pn.ddtma == 0 { pn.ddtma = pn.ddt
pn.ddtma = pn.ddt } else {
} else { pn.ddtma = int64(float64(pn.ddtma)*(1-ma10) + float64(pn.ddt)*ma10) // avoid int64 overflow
pn.ddtma = int64(float64(pn.ddtma)*(1-ma10) + float64(pn.ddt)*ma10) // avoid int64 overflow newdt = pn.dt + pn.ddtma
newdt = pn.dt + pn.ddtma }
} }
} pn.dt = newdt
pn.dt = newdt gLog.Printf(LvDEBUG, "synctime thisdt=%dms dt=%dms ddt=%dns ddtma=%dns rtt=%dms ", thisdt/int64(time.Millisecond), pn.dt/int64(time.Millisecond), pn.ddt, pn.ddtma, rtt/int64(time.Millisecond))
gLog.Printf(LvDEBUG, "synctime thisdt=%dms dt=%dms ddt=%dns ddtma=%dns rtt=%dms ", thisdt/int64(time.Millisecond), pn.dt/int64(time.Millisecond), pn.ddt, pn.ddtma, rtt/int64(time.Millisecond)) case MsgPush:
case MsgPush: handlePush(head.SubType, msg)
handlePush(head.SubType, msg) case MsgSDWAN:
case MsgSDWAN: handleSDWAN(head.SubType, msg)
handleSDWAN(head.SubType, msg) default:
default: i, ok := pn.msgMap.Load(uint64(0))
i, ok := pn.msgMap.Load(uint64(0)) if ok {
if ok { ch := i.(chan msgCtx)
ch := i.(chan msgCtx) ch <- msgCtx{data: msg, ts: time.Now()}
ch <- msgCtx{data: msg, ts: time.Now()} }
}
return
return }
} }
}
func (pn *P2PNetwork) readLoop() {
func (pn *P2PNetwork) readLoop() { gLog.Printf(LvDEBUG, "P2PNetwork readLoop start")
gLog.Printf(LvDEBUG, "P2PNetwork readLoop start") pn.wgReconnect.Add(1)
pn.wgReconnect.Add(1) defer pn.wgReconnect.Done()
defer pn.wgReconnect.Done() for pn.running {
for pn.running { pn.conn.SetReadDeadline(time.Now().Add(NetworkHeartbeatTime + 10*time.Second))
pn.conn.SetReadDeadline(time.Now().Add(NetworkHeartbeatTime + 10*time.Second)) _, msg, err := pn.conn.ReadMessage()
_, msg, err := pn.conn.ReadMessage() if err != nil {
if err != nil { gLog.Printf(LvERROR, "P2PNetwork read error:%s", err)
gLog.Printf(LvERROR, "P2PNetwork read error:%s", err) pn.close()
pn.close() break
break }
} pn.handleMessage(msg)
pn.handleMessage(msg) }
} gLog.Printf(LvDEBUG, "P2PNetwork readLoop end")
gLog.Printf(LvDEBUG, "P2PNetwork readLoop end") }
}
func (pn *P2PNetwork) write(mainType uint16, subType uint16, packet interface{}) error {
func (pn *P2PNetwork) write(mainType uint16, subType uint16, packet interface{}) error { if !pn.online {
if !pn.online { return errors.New("P2P network offline")
return errors.New("P2P network offline") }
} msg, err := newMessage(mainType, subType, packet)
msg, err := newMessage(mainType, subType, packet) if err != nil {
if err != nil { return err
return err }
} pn.writeMtx.Lock()
pn.writeMtx.Lock() defer pn.writeMtx.Unlock()
defer pn.writeMtx.Unlock() if err = pn.conn.WriteMessage(websocket.BinaryMessage, msg); err != nil {
if err = pn.conn.WriteMessage(websocket.BinaryMessage, msg); err != nil { gLog.Printf(LvERROR, "write msgType %d,%d error:%s", mainType, subType, err)
gLog.Printf(LvERROR, "write msgType %d,%d error:%s", mainType, subType, err) pn.close()
pn.close() }
} return err
return err }
}
func (pn *P2PNetwork) relay(to uint64, body []byte) error {
func (pn *P2PNetwork) relay(to uint64, body []byte) error { i, ok := pn.allTunnels.Load(to)
i, ok := pn.allTunnels.Load(to) if !ok {
if !ok { return ErrRelayTunnelNotFound
return ErrRelayTunnelNotFound }
} tunnel := i.(*P2PTunnel)
tunnel := i.(*P2PTunnel) if tunnel.config.shareBandwidth > 0 {
if tunnel.config.shareBandwidth > 0 { pn.limiter.Add(len(body), true)
pn.limiter.Add(len(body), true) }
} var err error
var err error if err = tunnel.conn.WriteBuffer(body); err != nil {
if err = tunnel.conn.WriteBuffer(body); err != nil { gLog.Printf(LvERROR, "relay to %d len=%d error:%s", to, len(body), err)
gLog.Printf(LvERROR, "relay to %d len=%d error:%s", to, len(body), err) }
} return err
return err }
}
func (pn *P2PNetwork) push(to string, subType uint16, packet interface{}) error {
func (pn *P2PNetwork) push(to string, subType uint16, packet interface{}) error { // gLog.Printf(LvDEBUG, "push msgType %d to %s", subType, to)
gLog.Printf(LvDEBUG, "push msgType %d to %s", subType, to) if !pn.online {
if !pn.online { return errors.New("client offline")
return errors.New("client offline") }
} pushHead := PushHeader{}
pushHead := PushHeader{} pushHead.From = gConf.nodeID()
pushHead.From = gConf.nodeID() pushHead.To = NodeNameToID(to)
pushHead.To = NodeNameToID(to) pushHeadBuf := new(bytes.Buffer)
pushHeadBuf := new(bytes.Buffer) err := binary.Write(pushHeadBuf, binary.LittleEndian, pushHead)
err := binary.Write(pushHeadBuf, binary.LittleEndian, pushHead) if err != nil {
if err != nil { return err
return err }
} data, err := json.Marshal(packet)
data, err := json.Marshal(packet) if err != nil {
if err != nil { return err
return err }
} // gLog.Println(LevelINFO,"write packet:", string(data))
// gLog.Println(LevelINFO,"write packet:", string(data)) pushMsg := append(encodeHeader(MsgPush, subType, uint32(len(data)+PushHeaderSize)), pushHeadBuf.Bytes()...)
pushMsg := append(encodeHeader(MsgPush, subType, uint32(len(data)+PushHeaderSize)), pushHeadBuf.Bytes()...) pushMsg = append(pushMsg, data...)
pushMsg = append(pushMsg, data...) pn.writeMtx.Lock()
pn.writeMtx.Lock() defer pn.writeMtx.Unlock()
defer pn.writeMtx.Unlock() if err = pn.conn.WriteMessage(websocket.BinaryMessage, pushMsg); err != nil {
if err = pn.conn.WriteMessage(websocket.BinaryMessage, pushMsg); err != nil { gLog.Printf(LvERROR, "push to %s error:%s", to, err)
gLog.Printf(LvERROR, "push to %s error:%s", to, err) pn.close()
pn.close() }
} return err
return err }
}
func (pn *P2PNetwork) close() {
func (pn *P2PNetwork) close() { if pn.running {
if pn.running { if pn.conn != nil {
if pn.conn != nil { pn.conn.Close()
pn.conn.Close() }
} pn.running = false
pn.running = false }
} select {
select { case pn.restartCh <- true:
case pn.restartCh <- true: default:
default: }
} }
}
func (pn *P2PNetwork) read(node string, mainType uint16, subType uint16, timeout time.Duration) (head *openP2PHeader, body []byte) {
func (pn *P2PNetwork) read(node string, mainType uint16, subType uint16, timeout time.Duration) (head *openP2PHeader, body []byte) { var nodeID uint64
var nodeID uint64 if node == "" {
if node == "" { nodeID = 0
nodeID = 0 } else {
} else { nodeID = NodeNameToID(node)
nodeID = NodeNameToID(node) }
} i, ok := pn.msgMap.Load(nodeID)
i, ok := pn.msgMap.Load(nodeID) if !ok {
if !ok { gLog.Printf(LvERROR, "read msg error: %s not found", node)
gLog.Printf(LvERROR, "read msg error: %s not found", node) return
return }
} ch := i.(chan msgCtx)
ch := i.(chan msgCtx) for {
for { select {
select { case <-time.After(timeout):
case <-time.After(timeout): gLog.Printf(LvERROR, "read msg error %d:%d timeout", mainType, subType)
gLog.Printf(LvERROR, "read msg error %d:%d timeout", mainType, subType) return
return case msg := <-ch:
case msg := <-ch: head = &openP2PHeader{}
head = &openP2PHeader{} err := binary.Read(bytes.NewReader(msg.data[:openP2PHeaderSize]), binary.LittleEndian, head)
err := binary.Read(bytes.NewReader(msg.data[:openP2PHeaderSize]), binary.LittleEndian, head) if err != nil {
if err != nil { gLog.Println(LvERROR, "read msg error:", err)
gLog.Println(LvERROR, "read msg error:", err) break
break }
} if time.Since(msg.ts) > ReadMsgTimeout {
if time.Since(msg.ts) > ReadMsgTimeout { gLog.Printf(LvDEBUG, "read msg error expired %d:%d", head.MainType, head.SubType)
gLog.Printf(LvDEBUG, "read msg error expired %d:%d", head.MainType, head.SubType) continue
continue }
} if head.MainType != mainType || head.SubType != subType {
if head.MainType != mainType || head.SubType != subType { gLog.Printf(LvDEBUG, "read msg error type %d:%d, requeue it", head.MainType, head.SubType)
gLog.Printf(LvDEBUG, "read msg error type %d:%d, requeue it", head.MainType, head.SubType) ch <- msg
ch <- msg time.Sleep(time.Second)
time.Sleep(time.Second) continue
continue }
} if mainType == MsgPush {
if mainType == MsgPush { body = msg.data[openP2PHeaderSize+PushHeaderSize:]
body = msg.data[openP2PHeaderSize+PushHeaderSize:] } else {
} else { body = msg.data[openP2PHeaderSize:]
body = msg.data[openP2PHeaderSize:] }
} return
return }
} }
} }
}
func (pn *P2PNetwork) updateAppHeartbeat(appID uint64) {
func (pn *P2PNetwork) updateAppHeartbeat(appID uint64) { pn.apps.Range(func(id, i interface{}) bool {
pn.apps.Range(func(id, i interface{}) bool { app := i.(*p2pApp)
app := i.(*p2pApp) if app.id == appID {
if app.id == appID { app.updateHeartbeat()
app.updateHeartbeat() }
} return true
return true })
}) }
}
// ipv6 will expired need to refresh.
// ipv6 will expired need to refresh. func (pn *P2PNetwork) refreshIPv6() {
func (pn *P2PNetwork) refreshIPv6() { for i := 0; i < 2; i++ {
for i := 0; i < 2; i++ { client := &http.Client{Timeout: time.Second * 10}
client := &http.Client{Timeout: time.Second * 10} r, err := client.Get("http://ipv6.ddnspod.com/")
r, err := client.Get("http://ipv6.ddnspod.com/") if err != nil {
if err != nil { gLog.Println(LvDEBUG, "refreshIPv6 error:", err)
gLog.Println(LvDEBUG, "refreshIPv6 error:", err) continue
continue }
} defer r.Body.Close()
defer r.Body.Close() buf := make([]byte, 1024)
buf := make([]byte, 1024) n, err := r.Body.Read(buf)
n, err := r.Body.Read(buf) if n <= 0 {
if n <= 0 { gLog.Println(LvINFO, "refreshIPv6 error:", err, n)
gLog.Println(LvINFO, "refreshIPv6 error:", err, n) continue
continue }
} if IsIPv6(string(buf[:n])) {
if IsIPv6(string(buf[:n])) { gConf.setIPv6(string(buf[:n]))
gConf.setIPv6(string(buf[:n])) }
} break
break }
}
}
}
func (pn *P2PNetwork) requestPeerInfo(config *AppConfig) error {
func (pn *P2PNetwork) requestPeerInfo(config *AppConfig) error { // request peer info
// request peer info // TODO: multi-thread issue
// TODO: multi-thread issue pn.reqGatewayMtx.Lock()
pn.reqGatewayMtx.Lock() pn.write(MsgQuery, MsgQueryPeerInfoReq, &QueryPeerInfoReq{config.peerToken, config.PeerNode})
pn.write(MsgQuery, MsgQueryPeerInfoReq, &QueryPeerInfoReq{config.peerToken, config.PeerNode}) head, body := pn.read("", MsgQuery, MsgQueryPeerInfoRsp, ClientAPITimeout)
head, body := pn.read("", MsgQuery, MsgQueryPeerInfoRsp, ClientAPITimeout) pn.reqGatewayMtx.Unlock()
pn.reqGatewayMtx.Unlock() if head == nil {
if head == nil { gLog.Println(LvERROR, "requestPeerInfo error")
gLog.Println(LvERROR, "requestPeerInfo error") return ErrNetwork // network error, should not be ErrPeerOffline
return ErrNetwork // network error, should not be ErrPeerOffline }
} rsp := QueryPeerInfoRsp{}
rsp := QueryPeerInfoRsp{} if err := json.Unmarshal(body, &rsp); err != nil {
if err := json.Unmarshal(body, &rsp); err != nil { return ErrMsgFormat
return ErrMsgFormat }
} if rsp.Online == 0 {
if rsp.Online == 0 { return ErrPeerOffline
return ErrPeerOffline }
} if compareVersion(rsp.Version, LeastSupportVersion) < 0 {
if compareVersion(rsp.Version, LeastSupportVersion) < 0 { return ErrVersionNotCompatible
return ErrVersionNotCompatible }
} config.peerVersion = rsp.Version
config.peerVersion = rsp.Version config.peerLanIP = rsp.LanIP
config.peerLanIP = rsp.LanIP config.hasIPv4 = rsp.HasIPv4
config.hasIPv4 = rsp.HasIPv4 config.peerIP = rsp.IPv4
config.peerIP = rsp.IPv4 config.peerIPv6 = rsp.IPv6
config.peerIPv6 = rsp.IPv6 config.hasUPNPorNATPMP = rsp.HasUPNPorNATPMP
config.hasUPNPorNATPMP = rsp.HasUPNPorNATPMP config.peerNatType = rsp.NatType
config.peerNatType = rsp.NatType ///
/// return nil
return nil }
}
func (pn *P2PNetwork) StartSDWAN() {
func (pn *P2PNetwork) StartSDWAN() { // request peer info
// request peer info pn.sdwan = &p2pSDWAN{}
pn.sdwan = &p2pSDWAN{} }
}
func (pn *P2PNetwork) ConnectNode(node string) error {
func (pn *P2PNetwork) ConnectNode(node string) error { if gConf.nodeID() < NodeNameToID(node) {
if gConf.nodeID() < NodeNameToID(node) { return errors.New("only the bigger nodeid connect")
return errors.New("only the bigger nodeid connect") }
} peerNodeID := fmt.Sprintf("%d", NodeNameToID(node))
peerNodeID := fmt.Sprintf("%d", NodeNameToID(node)) config := AppConfig{Enabled: 1}
config := AppConfig{Enabled: 1} config.AppName = peerNodeID
config.AppName = peerNodeID config.SrcPort = 0
config.SrcPort = 0 config.PeerNode = node
config.PeerNode = node sdwan := gConf.getSDWAN()
sdwan := gConf.getSDWAN() config.PunchPriority = int(sdwan.PunchPriority)
config.PunchPriority = int(sdwan.PunchPriority) if node != sdwan.CentralNode && gConf.Network.Node != sdwan.CentralNode { // neither is centralnode
if node != sdwan.CentralNode && gConf.Network.Node != sdwan.CentralNode { // neither is centralnode config.RelayNode = sdwan.CentralNode
config.RelayNode = sdwan.CentralNode config.ForceRelay = int(sdwan.ForceRelay)
config.ForceRelay = int(sdwan.ForceRelay) if sdwan.Mode == SDWANModeCentral {
if sdwan.Mode == SDWANModeCentral { config.ForceRelay = 1
config.ForceRelay = 1 }
} }
}
gConf.add(config, true)
gConf.add(config, true) return nil
return nil }
}
func (pn *P2PNetwork) WriteNode(nodeID uint64, buff []byte) error {
func (pn *P2PNetwork) WriteNode(nodeID uint64, buff []byte) error { i, ok := pn.apps.Load(nodeID)
i, ok := pn.apps.Load(nodeID) if !ok {
if !ok { return errors.New("peer not found")
return errors.New("peer not found") }
} var err error
var err error app := i.(*p2pApp)
app := i.(*p2pApp) if app.Tunnel() == nil {
if app.Tunnel() == nil { return errors.New("peer tunnel nil")
return errors.New("peer tunnel nil") }
} // TODO: move to app.write
// TODO: move to app.write gLog.Printf(LvDev, "%d tunnel write node data bodylen=%d, relay=%t", app.Tunnel().id, len(buff), !app.isDirect())
gLog.Printf(LvDev, "%d tunnel write node data bodylen=%d, relay=%t", app.Tunnel().id, len(buff), !app.isDirect()) if app.isDirect() { // direct
if app.isDirect() { // direct app.Tunnel().asyncWriteNodeData(MsgP2P, MsgNodeData, buff)
app.Tunnel().asyncWriteNodeData(MsgP2P, MsgNodeData, buff) } else { // relay
} else { // relay fromNodeIDHead := new(bytes.Buffer)
fromNodeIDHead := new(bytes.Buffer) binary.Write(fromNodeIDHead, binary.LittleEndian, gConf.nodeID())
binary.Write(fromNodeIDHead, binary.LittleEndian, gConf.nodeID()) all := app.RelayHead().Bytes()
all := app.RelayHead().Bytes() all = append(all, encodeHeader(MsgP2P, MsgRelayNodeData, uint32(len(buff)+overlayHeaderSize))...)
all = append(all, encodeHeader(MsgP2P, MsgRelayNodeData, uint32(len(buff)+overlayHeaderSize))...) all = append(all, fromNodeIDHead.Bytes()...)
all = append(all, fromNodeIDHead.Bytes()...) all = append(all, buff...)
all = append(all, buff...) app.Tunnel().asyncWriteNodeData(MsgP2P, MsgRelayData, all)
app.Tunnel().asyncWriteNodeData(MsgP2P, MsgRelayData, all) }
}
return err
return err }
}
func (pn *P2PNetwork) WriteBroadcast(buff []byte) error {
func (pn *P2PNetwork) WriteBroadcast(buff []byte) error { ///
/// pn.apps.Range(func(id, i interface{}) bool {
pn.apps.Range(func(id, i interface{}) bool { // newDestIP := net.ParseIP("10.2.3.2")
// newDestIP := net.ParseIP("10.2.3.2") // copy(buff[16:20], newDestIP.To4())
// copy(buff[16:20], newDestIP.To4()) // binary.BigEndian.PutUint16(buff[10:12], 0) // set checksum=0 for calc checksum
// binary.BigEndian.PutUint16(buff[10:12], 0) // set checksum=0 for calc checksum // ipChecksum := calculateChecksum(buff[0:20])
// ipChecksum := calculateChecksum(buff[0:20]) // binary.BigEndian.PutUint16(buff[10:12], ipChecksum)
// binary.BigEndian.PutUint16(buff[10:12], ipChecksum) // binary.BigEndian.PutUint16(buff[26:28], 0x082e)
// binary.BigEndian.PutUint16(buff[26:28], 0x082e) app := i.(*p2pApp)
app := i.(*p2pApp) if app.Tunnel() == nil {
if app.Tunnel() == nil { return true
return true }
} if app.config.SrcPort != 0 { // normal portmap app
if app.config.SrcPort != 0 { // normal portmap app return true
return true }
} if app.config.peerIP == gConf.Network.publicIP { // mostly in a lan
if app.config.peerIP == gConf.Network.publicIP { // mostly in a lan return true
return true }
} if app.isDirect() { // direct
if app.isDirect() { // direct app.Tunnel().conn.WriteBytes(MsgP2P, MsgNodeData, buff)
app.Tunnel().conn.WriteBytes(MsgP2P, MsgNodeData, buff) } else { // relay
} else { // relay fromNodeIDHead := new(bytes.Buffer)
fromNodeIDHead := new(bytes.Buffer) binary.Write(fromNodeIDHead, binary.LittleEndian, gConf.nodeID())
binary.Write(fromNodeIDHead, binary.LittleEndian, gConf.nodeID()) all := app.RelayHead().Bytes()
all := app.RelayHead().Bytes() all = append(all, encodeHeader(MsgP2P, MsgRelayNodeData, uint32(len(buff)+overlayHeaderSize))...)
all = append(all, encodeHeader(MsgP2P, MsgRelayNodeData, uint32(len(buff)+overlayHeaderSize))...) all = append(all, fromNodeIDHead.Bytes()...)
all = append(all, fromNodeIDHead.Bytes()...) all = append(all, buff...)
all = append(all, buff...) app.Tunnel().conn.WriteBytes(MsgP2P, MsgRelayData, all)
app.Tunnel().conn.WriteBytes(MsgP2P, MsgRelayData, all) }
} return true
return true })
}) return nil
return nil }
}
func (pn *P2PNetwork) ReadNode(tm time.Duration) *NodeData {
func (pn *P2PNetwork) ReadNode(tm time.Duration) *NodeData { select {
select { case nd := <-pn.nodeData:
case nd := <-pn.nodeData: return nd
return nd case <-time.After(tm):
case <-time.After(tm): }
} return nil
return nil }
}
+805 -806
View File
@@ -1,806 +1,805 @@
package openp2p package openp2p
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"math/rand" "math/rand"
"net" "net"
"reflect" "reflect"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
) )
const WriteDataChanSize int = 3000 const WriteDataChanSize int = 3000
var buildTunnelMtx sync.Mutex var buildTunnelMtx sync.Mutex
type P2PTunnel struct { type P2PTunnel struct {
pn *P2PNetwork conn underlay
conn underlay hbTime time.Time
hbTime time.Time hbMtx sync.Mutex
hbMtx sync.Mutex config AppConfig
config AppConfig localHoleAddr *net.UDPAddr // local hole address
la *net.UDPAddr // local hole address remoteHoleAddr *net.UDPAddr // remote hole address
ra *net.UDPAddr // remote hole address overlayConns sync.Map // both TCP and UDP
overlayConns sync.Map // both TCP and UDP id uint64 // client side alloc rand.uint64 = server side
id uint64 // client side alloc rand.uint64 = server side running bool
running bool runMtx sync.Mutex
runMtx sync.Mutex tunnelServer bool // different from underlayServer
tunnelServer bool // different from underlayServer coneLocalPort int
coneLocalPort int coneNatPort int
coneNatPort int linkModeWeb string // use config.linkmode
linkModeWeb string // use config.linkmode punchTs uint64
punchTs uint64 writeData chan []byte
writeData chan []byte writeDataSmall chan []byte
writeDataSmall chan []byte }
}
func (t *P2PTunnel) initPort() {
func (t *P2PTunnel) initPort() { t.running = true
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
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 {
if t.config.linkMode == LinkModeTCP6 || t.config.linkMode == LinkModeTCP4 || t.config.linkMode == LinkModeIntranet { t.coneLocalPort = gConf.Network.TCPPort
t.coneLocalPort = gConf.Network.TCPPort t.coneNatPort = gConf.Network.TCPPort // symmetric doesn't need coneNatPort
t.coneNatPort = gConf.Network.TCPPort // symmetric doesn't need coneNatPort }
} if t.config.linkMode == LinkModeUDPPunch {
if t.config.linkMode == LinkModeUDPPunch { // prepare one random cone hole manually
// prepare one random cone hole manually _, natPort, _ := natTest(gConf.Network.ServerHost, gConf.Network.UDPPort1, localPort)
_, natPort, _ := natTest(gConf.Network.ServerHost, gConf.Network.UDPPort1, localPort) t.coneLocalPort = localPort
t.coneLocalPort = localPort t.coneNatPort = natPort
t.coneNatPort = natPort }
} if t.config.linkMode == LinkModeTCPPunch {
if t.config.linkMode == LinkModeTCPPunch { // prepare one random cone hole by system automatically
// prepare one random cone hole by system automatically _, natPort, localPort2 := natTCP(gConf.Network.ServerHost, IfconfigPort1)
_, natPort, localPort2 := natTCP(gConf.Network.ServerHost, IfconfigPort1) t.coneLocalPort = localPort2
t.coneLocalPort = localPort2 t.coneNatPort = natPort
t.coneNatPort = natPort }
} t.localHoleAddr = &net.UDPAddr{IP: net.ParseIP(gConf.Network.localIP), Port: t.coneLocalPort}
t.la = &net.UDPAddr{IP: net.ParseIP(gConf.Network.localIP), Port: t.coneLocalPort} gLog.Printf(LvDEBUG, "prepare punching port %d:%d", t.coneLocalPort, t.coneNatPort)
gLog.Printf(LvDEBUG, "prepare punching port %d:%d", t.coneLocalPort, t.coneNatPort) }
}
func (t *P2PTunnel) connect() error {
func (t *P2PTunnel) connect() error { gLog.Printf(LvDEBUG, "start p2pTunnel to %s ", t.config.LogPeerNode())
gLog.Printf(LvDEBUG, "start p2pTunnel to %s ", t.config.PeerNode) t.tunnelServer = false
t.tunnelServer = false appKey := uint64(0)
appKey := uint64(0) req := PushConnectReq{
req := PushConnectReq{ Token: t.config.peerToken,
Token: t.config.peerToken, From: gConf.Network.Node,
From: gConf.Network.Node, FromIP: gConf.Network.publicIP,
FromIP: gConf.Network.publicIP, ConeNatPort: t.coneNatPort,
ConeNatPort: t.coneNatPort, NatType: gConf.Network.natType,
NatType: gConf.Network.natType, HasIPv4: gConf.Network.hasIPv4,
HasIPv4: gConf.Network.hasIPv4, IPv6: gConf.IPv6(),
IPv6: gConf.IPv6(), HasUPNPorNATPMP: gConf.Network.hasUPNPorNATPMP,
HasUPNPorNATPMP: gConf.Network.hasUPNPorNATPMP, ID: t.id,
ID: t.id, AppKey: appKey,
AppKey: appKey, Version: OpenP2PVersion,
Version: OpenP2PVersion, LinkMode: t.config.linkMode,
LinkMode: t.config.linkMode, IsUnderlayServer: t.config.isUnderlayServer ^ 1, // peer
IsUnderlayServer: t.config.isUnderlayServer ^ 1, // peer UnderlayProtocol: t.config.UnderlayProtocol,
UnderlayProtocol: t.config.UnderlayProtocol, }
} if req.Token == 0 { // no relay token
if req.Token == 0 { // no relay token req.Token = gConf.Network.Token
req.Token = gConf.Network.Token }
} GNetwork.push(t.config.PeerNode, MsgPushConnectReq, req)
t.pn.push(t.config.PeerNode, MsgPushConnectReq, req) head, body := GNetwork.read(t.config.PeerNode, MsgPush, MsgPushConnectRsp, UnderlayConnectTimeout*3)
head, body := t.pn.read(t.config.PeerNode, MsgPush, MsgPushConnectRsp, UnderlayConnectTimeout*3) if head == nil {
if head == nil { return errors.New("connect error")
return errors.New("connect error") }
} rsp := PushConnectRsp{}
rsp := PushConnectRsp{} if err := json.Unmarshal(body, &rsp); err != nil {
if err := json.Unmarshal(body, &rsp); err != nil { gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(rsp), err)
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(rsp), err) return err
return err }
} // gLog.Println(LevelINFO, rsp)
// gLog.Println(LevelINFO, rsp) if rsp.Error != 0 {
if rsp.Error != 0 { return errors.New(rsp.Detail)
return errors.New(rsp.Detail) }
} t.config.peerNatType = rsp.NatType
t.config.peerNatType = rsp.NatType t.config.hasIPv4 = rsp.HasIPv4
t.config.hasIPv4 = rsp.HasIPv4 t.config.peerIPv6 = rsp.IPv6
t.config.peerIPv6 = rsp.IPv6 t.config.hasUPNPorNATPMP = rsp.HasUPNPorNATPMP
t.config.hasUPNPorNATPMP = rsp.HasUPNPorNATPMP t.config.peerVersion = rsp.Version
t.config.peerVersion = rsp.Version t.config.peerConeNatPort = rsp.ConeNatPort
t.config.peerConeNatPort = rsp.ConeNatPort t.config.peerIP = rsp.FromIP
t.config.peerIP = rsp.FromIP t.punchTs = rsp.PunchTs
t.punchTs = rsp.PunchTs err := t.start()
err := t.start() if err != nil {
if err != nil { gLog.Println(LvERROR, "handshake error:", err)
gLog.Println(LvERROR, "handshake error:", err) }
} return err
return err }
}
func (t *P2PTunnel) isRuning() bool {
func (t *P2PTunnel) isRuning() bool { t.runMtx.Lock()
t.runMtx.Lock() defer t.runMtx.Unlock()
defer t.runMtx.Unlock() return t.running
return t.running }
}
func (t *P2PTunnel) setRun(running bool) {
func (t *P2PTunnel) setRun(running bool) { t.runMtx.Lock()
t.runMtx.Lock() defer t.runMtx.Unlock()
defer t.runMtx.Unlock() t.running = running
t.running = running }
}
func (t *P2PTunnel) isActive() bool {
func (t *P2PTunnel) isActive() bool { if !t.isRuning() || t.conn == nil {
if !t.isRuning() || t.conn == nil { return false
return false }
} t.hbMtx.Lock()
t.hbMtx.Lock() defer t.hbMtx.Unlock()
defer t.hbMtx.Unlock() res := time.Now().Before(t.hbTime.Add(TunnelHeartbeatTime * 2))
res := time.Now().Before(t.hbTime.Add(TunnelHeartbeatTime * 2)) if !res {
if !res { gLog.Printf(LvDEBUG, "%d tunnel isActive false", t.id)
gLog.Printf(LvDEBUG, "%d tunnel isActive false", t.id) }
} return res
return res }
}
func (t *P2PTunnel) checkActive() bool {
func (t *P2PTunnel) checkActive() bool { if !t.isActive() {
if !t.isActive() { return false
return false }
} hbt := time.Now()
hbt := time.Now() t.conn.WriteBytes(MsgP2P, MsgTunnelHeartbeat, nil)
t.conn.WriteBytes(MsgP2P, MsgTunnelHeartbeat, nil) isActive := false
isActive := false // wait at most 5s
// wait at most 5s for i := 0; i < 50 && !isActive; i++ {
for i := 0; i < 50 && !isActive; i++ { t.hbMtx.Lock()
t.hbMtx.Lock() if t.hbTime.After(hbt) {
if t.hbTime.After(hbt) { isActive = true
isActive = true }
} t.hbMtx.Unlock()
t.hbMtx.Unlock() time.Sleep(time.Millisecond * 100)
time.Sleep(time.Millisecond * 100) }
} gLog.Printf(LvINFO, "checkActive %t. hbtime=%d", isActive, t.hbTime)
gLog.Printf(LvINFO, "checkActive %t. hbtime=%d", isActive, t.hbTime) return isActive
return isActive }
}
// call when user delete tunnel
// call when user delete tunnel func (t *P2PTunnel) close() {
func (t *P2PTunnel) close() { GNetwork.NotifyTunnelClose(t)
t.pn.NotifyTunnelClose(t) if !t.running {
if !t.running { return
return }
} t.setRun(false)
t.setRun(false) if t.conn != nil {
if t.conn != nil { t.conn.Close()
t.conn.Close() }
} GNetwork.allTunnels.Delete(t.id)
t.pn.allTunnels.Delete(t.id) gLog.Printf(LvINFO, "%d p2ptunnel close %s ", t.id, t.config.LogPeerNode())
gLog.Printf(LvINFO, "%d p2ptunnel close %s ", t.id, t.config.PeerNode) }
}
func (t *P2PTunnel) start() error {
func (t *P2PTunnel) start() error { if t.config.linkMode == LinkModeUDPPunch {
if t.config.linkMode == LinkModeUDPPunch { if err := t.handshake(); err != nil {
if err := t.handshake(); err != nil { return err
return err }
} }
} err := t.connectUnderlay()
err := t.connectUnderlay() if err != nil {
if err != nil { gLog.Println(LvERROR, err)
gLog.Println(LvERROR, err) return err
return err }
} return nil
return nil }
}
func (t *P2PTunnel) handshake() error {
func (t *P2PTunnel) handshake() error { if t.config.peerConeNatPort > 0 { // only peer is cone should prepare t.ra
if t.config.peerConeNatPort > 0 { // only peer is cone should prepare t.ra var err error
var err error t.remoteHoleAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", t.config.peerIP, t.config.peerConeNatPort))
t.ra, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", t.config.peerIP, t.config.peerConeNatPort)) if err != nil {
if err != nil { return err
return err }
} }
} if compareVersion(t.config.peerVersion, SyncServerTimeVersion) < 0 {
if compareVersion(t.config.peerVersion, SyncServerTimeVersion) < 0 { gLog.Printf(LvDEBUG, "peer version %s less than %s", t.config.peerVersion, SyncServerTimeVersion)
gLog.Printf(LvDEBUG, "peer version %s less than %s", t.config.peerVersion, SyncServerTimeVersion) } else {
} else { ts := time.Duration(int64(t.punchTs) + GNetwork.dt + GNetwork.ddtma*int64(time.Since(GNetwork.hbTime)+PunchTsDelay)/int64(NetworkHeartbeatTime) - time.Now().UnixNano())
ts := time.Duration(int64(t.punchTs) + t.pn.dt + t.pn.ddtma*int64(time.Since(t.pn.hbTime)+PunchTsDelay)/int64(NetworkHeartbeatTime) - time.Now().UnixNano()) if ts > PunchTsDelay || ts < 0 {
if ts > PunchTsDelay || ts < 0 { ts = PunchTsDelay
ts = PunchTsDelay }
} gLog.Printf(LvDEBUG, "sleep %d ms", ts/time.Millisecond)
gLog.Printf(LvDEBUG, "sleep %d ms", ts/time.Millisecond) time.Sleep(ts)
time.Sleep(ts) }
} gLog.Println(LvDEBUG, "handshake to ", t.config.LogPeerNode())
gLog.Println(LvDEBUG, "handshake to ", t.config.PeerNode) var err error
var err error if gConf.Network.natType == NATCone && t.config.peerNatType == NATCone {
if gConf.Network.natType == NATCone && t.config.peerNatType == NATCone { err = handshakeC2C(t)
err = handshakeC2C(t) } else if t.config.peerNatType == NATSymmetric && gConf.Network.natType == NATSymmetric {
} else if t.config.peerNatType == NATSymmetric && gConf.Network.natType == NATSymmetric { err = ErrorS2S
err = ErrorS2S t.close()
t.close() } else if t.config.peerNatType == NATSymmetric && gConf.Network.natType == NATCone {
} else if t.config.peerNatType == NATSymmetric && gConf.Network.natType == NATCone { err = handshakeC2S(t)
err = handshakeC2S(t) } else if t.config.peerNatType == NATCone && gConf.Network.natType == NATSymmetric {
} else if t.config.peerNatType == NATCone && gConf.Network.natType == NATSymmetric { err = handshakeS2C(t)
err = handshakeS2C(t) } else {
} else { return errors.New("unknown error")
return errors.New("unknown error") }
} if err != nil {
if err != nil { gLog.Println(LvERROR, "punch handshake error:", err)
gLog.Println(LvERROR, "punch handshake error:", err) return err
return err }
} gLog.Printf(LvDEBUG, "handshake to %s ok", t.config.LogPeerNode())
gLog.Printf(LvDEBUG, "handshake to %s ok", t.config.PeerNode) return nil
return nil }
}
func (t *P2PTunnel) connectUnderlay() (err error) {
func (t *P2PTunnel) connectUnderlay() (err error) { switch t.config.linkMode {
switch t.config.linkMode { case LinkModeTCP6:
case LinkModeTCP6: t.conn, err = t.connectUnderlayTCP6()
t.conn, err = t.connectUnderlayTCP6() case LinkModeTCP4:
case LinkModeTCP4: t.conn, err = t.connectUnderlayTCP()
t.conn, err = t.connectUnderlayTCP() case LinkModeTCPPunch:
case LinkModeTCPPunch: if gConf.Network.natType == NATSymmetric || t.config.peerNatType == NATSymmetric {
if gConf.Network.natType == NATSymmetric || t.config.peerNatType == NATSymmetric { t.conn, err = t.connectUnderlayTCPSymmetric()
t.conn, err = t.connectUnderlayTCPSymmetric() } else {
} else { t.conn, err = t.connectUnderlayTCP()
t.conn, err = t.connectUnderlayTCP() }
} case LinkModeIntranet:
case LinkModeIntranet: t.conn, err = t.connectUnderlayTCP()
t.conn, err = t.connectUnderlayTCP() case LinkModeUDPPunch:
case LinkModeUDPPunch: t.conn, err = t.connectUnderlayUDP()
t.conn, err = t.connectUnderlayUDP()
}
} if err != nil {
if err != nil { return err
return err }
} if t.conn == nil {
if t.conn == nil { return errors.New("connect underlay error")
return errors.New("connect underlay error") }
} t.setRun(true)
t.setRun(true) go t.readLoop()
go t.readLoop() go t.writeLoop()
go t.writeLoop() return nil
return nil }
}
func (t *P2PTunnel) connectUnderlayUDP() (c underlay, err error) {
func (t *P2PTunnel) connectUnderlayUDP() (c underlay, err error) { gLog.Printf(LvDEBUG, "connectUnderlayUDP %s start ", t.config.LogPeerNode())
gLog.Printf(LvDEBUG, "connectUnderlayUDP %s start ", t.config.PeerNode) defer gLog.Printf(LvDEBUG, "connectUnderlayUDP %s end ", t.config.LogPeerNode())
defer gLog.Printf(LvDEBUG, "connectUnderlayUDP %s end ", t.config.PeerNode) var ul underlay
var ul underlay underlayProtocol := t.config.UnderlayProtocol
underlayProtocol := t.config.UnderlayProtocol if underlayProtocol == "" {
if underlayProtocol == "" { underlayProtocol = "quic"
underlayProtocol = "quic" }
} if t.config.isUnderlayServer == 1 {
if t.config.isUnderlayServer == 1 { time.Sleep(time.Millisecond * 10) // punching udp port will need some times in some env
time.Sleep(time.Millisecond * 10) // punching udp port will need some times in some env go GNetwork.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
go t.pn.push(t.config.PeerNode, MsgPushUnderlayConnect, nil) if t.config.UnderlayProtocol == "kcp" {
if t.config.UnderlayProtocol == "kcp" { ul, err = listenKCP(t.localHoleAddr.String(), TunnelIdleTimeout)
ul, err = listenKCP(t.la.String(), TunnelIdleTimeout) } else {
} else { ul, err = listenQuic(t.localHoleAddr.String(), TunnelIdleTimeout)
ul, err = listenQuic(t.la.String(), TunnelIdleTimeout) }
}
if err != nil {
if err != nil { gLog.Printf(LvINFO, "listen %s error:%s", underlayProtocol, err)
gLog.Printf(LvINFO, "listen %s error:%s", underlayProtocol, err) return nil, err
return nil, err }
}
_, buff, err := ul.ReadBuffer()
_, buff, err := ul.ReadBuffer() if err != nil {
if err != nil { ul.Close()
ul.Close() return nil, fmt.Errorf("read start msg error:%s", err)
return nil, fmt.Errorf("read start msg error:%s", err) }
} if buff != nil {
if buff != nil { gLog.Println(LvDEBUG, string(buff))
gLog.Println(LvDEBUG, string(buff)) }
} ul.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, []byte("OpenP2P,hello2"))
ul.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, []byte("OpenP2P,hello2")) gLog.Printf(LvDEBUG, "%s connection ok", underlayProtocol)
gLog.Printf(LvDEBUG, "%s connection ok", underlayProtocol) return ul, nil
return ul, nil }
}
//else
//else conn, errL := net.ListenUDP("udp", t.localHoleAddr)
conn, errL := net.ListenUDP("udp", t.la) if errL != nil {
if errL != nil { time.Sleep(time.Millisecond * 10)
time.Sleep(time.Millisecond * 10) conn, errL = net.ListenUDP("udp", t.localHoleAddr)
conn, errL = net.ListenUDP("udp", t.la) if errL != nil {
if errL != nil { return nil, fmt.Errorf("%s listen error:%s", underlayProtocol, errL)
return nil, fmt.Errorf("%s listen error:%s", underlayProtocol, errL) }
} }
} GNetwork.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, ReadMsgTimeout)
t.pn.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, ReadMsgTimeout) gLog.Printf(LvDEBUG, "%s dial to %s", underlayProtocol, t.remoteHoleAddr.String())
gLog.Printf(LvDEBUG, "%s dial to %s", underlayProtocol, t.ra.String()) if t.config.UnderlayProtocol == "kcp" {
if t.config.UnderlayProtocol == "kcp" { ul, errL = dialKCP(conn, t.remoteHoleAddr, TunnelIdleTimeout)
ul, errL = dialKCP(conn, t.ra, TunnelIdleTimeout) } else {
} else { ul, errL = dialQuic(conn, t.remoteHoleAddr, TunnelIdleTimeout)
ul, errL = dialQuic(conn, t.ra, TunnelIdleTimeout) }
}
if errL != nil {
if errL != nil { return nil, fmt.Errorf("%s dial to %s error:%s", underlayProtocol, t.remoteHoleAddr.String(), errL)
return nil, fmt.Errorf("%s dial to %s error:%s", underlayProtocol, t.ra.String(), errL) }
} handshakeBegin := time.Now()
handshakeBegin := time.Now() ul.WriteBytes(MsgP2P, MsgTunnelHandshake, []byte("OpenP2P,hello"))
ul.WriteBytes(MsgP2P, MsgTunnelHandshake, []byte("OpenP2P,hello")) _, buff, err := ul.ReadBuffer() // TODO: kcp need timeout
_, buff, err := ul.ReadBuffer() // TODO: kcp need timeout if err != nil {
if err != nil { ul.Close()
ul.Close() return nil, fmt.Errorf("read MsgTunnelHandshake error:%s", err)
return nil, fmt.Errorf("read MsgTunnelHandshake error:%s", err) }
} if buff != nil {
if buff != nil { gLog.Println(LvDEBUG, string(buff))
gLog.Println(LvDEBUG, string(buff)) }
}
gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin))
gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin)) gLog.Printf(LvINFO, "%s connection ok", underlayProtocol)
gLog.Printf(LvINFO, "%s connection ok", underlayProtocol) t.linkModeWeb = LinkModeUDPPunch
t.linkModeWeb = LinkModeUDPPunch return ul, nil
return ul, nil }
}
func (t *P2PTunnel) connectUnderlayTCP() (c underlay, err error) {
func (t *P2PTunnel) connectUnderlayTCP() (c underlay, err error) { gLog.Printf(LvDEBUG, "connectUnderlayTCP %s start ", t.config.LogPeerNode())
gLog.Printf(LvDEBUG, "connectUnderlayTCP %s start ", t.config.PeerNode) defer gLog.Printf(LvDEBUG, "connectUnderlayTCP %s end ", t.config.LogPeerNode())
defer gLog.Printf(LvDEBUG, "connectUnderlayTCP %s end ", t.config.PeerNode) var ul *underlayTCP
var ul *underlayTCP peerIP := t.config.peerIP
peerIP := t.config.peerIP if t.config.linkMode == LinkModeIntranet {
if t.config.linkMode == LinkModeIntranet { peerIP = t.config.peerLanIP
peerIP = t.config.peerLanIP }
} // server side
// server side if t.config.isUnderlayServer == 1 {
if t.config.isUnderlayServer == 1 { ul, err = listenTCP(peerIP, t.config.peerConeNatPort, t.coneLocalPort, t.config.linkMode, t)
ul, err = listenTCP(peerIP, t.config.peerConeNatPort, t.coneLocalPort, t.config.linkMode, t) if err != nil {
if err != nil { return nil, fmt.Errorf("listen TCP error:%s", err)
return nil, fmt.Errorf("listen TCP error:%s", err) }
} gLog.Println(LvINFO, "TCP connection ok")
gLog.Println(LvINFO, "TCP connection ok") t.linkModeWeb = LinkModeIPv4
t.linkModeWeb = LinkModeIPv4 if t.config.linkMode == LinkModeIntranet {
if t.config.linkMode == LinkModeIntranet { t.linkModeWeb = LinkModeIntranet
t.linkModeWeb = LinkModeIntranet }
} return ul, nil
return ul, nil }
}
// client side
// client side if t.config.linkMode == LinkModeTCP4 {
if t.config.linkMode == LinkModeTCP4 { GNetwork.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, ReadMsgTimeout)
t.pn.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, ReadMsgTimeout) } else { //tcp punch should sleep for punch the same time
} else { //tcp punch should sleep for punch the same time if compareVersion(t.config.peerVersion, SyncServerTimeVersion) < 0 {
if compareVersion(t.config.peerVersion, SyncServerTimeVersion) < 0 { gLog.Printf(LvDEBUG, "peer version %s less than %s", t.config.peerVersion, SyncServerTimeVersion)
gLog.Printf(LvDEBUG, "peer version %s less than %s", t.config.peerVersion, SyncServerTimeVersion) } else {
} else { ts := time.Duration(int64(t.punchTs) + GNetwork.dt + GNetwork.ddtma*int64(time.Since(GNetwork.hbTime)+PunchTsDelay)/int64(NetworkHeartbeatTime) - time.Now().UnixNano())
ts := time.Duration(int64(t.punchTs) + t.pn.dt + t.pn.ddtma*int64(time.Since(t.pn.hbTime)+PunchTsDelay)/int64(NetworkHeartbeatTime) - time.Now().UnixNano()) if ts > PunchTsDelay || ts < 0 {
if ts > PunchTsDelay || ts < 0 { ts = PunchTsDelay
ts = PunchTsDelay }
} gLog.Printf(LvDEBUG, "sleep %d ms", ts/time.Millisecond)
gLog.Printf(LvDEBUG, "sleep %d ms", ts/time.Millisecond) time.Sleep(ts)
time.Sleep(ts) }
} }
} ul, err = dialTCP(peerIP, t.config.peerConeNatPort, t.coneLocalPort, t.config.linkMode)
ul, err = dialTCP(peerIP, t.config.peerConeNatPort, t.coneLocalPort, t.config.linkMode) if err != nil {
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", t.config.peerIP, t.config.peerConeNatPort, err) }
} handshakeBegin := time.Now()
handshakeBegin := time.Now() tidBuff := new(bytes.Buffer)
tidBuff := new(bytes.Buffer) binary.Write(tidBuff, binary.LittleEndian, t.id)
binary.Write(tidBuff, binary.LittleEndian, t.id) ul.WriteBytes(MsgP2P, MsgTunnelHandshake, tidBuff.Bytes()) // tunnelID
ul.WriteBytes(MsgP2P, MsgTunnelHandshake, tidBuff.Bytes()) // tunnelID _, buff, err := ul.ReadBuffer()
_, buff, err := ul.ReadBuffer() if err != nil {
if err != nil { return nil, fmt.Errorf("read MsgTunnelHandshake error:%s", err)
return nil, fmt.Errorf("read MsgTunnelHandshake error:%s", err) }
} if buff != nil {
if buff != nil { gLog.Println(LvDEBUG, "hello ", string(buff))
gLog.Println(LvDEBUG, "hello ", string(buff)) }
}
gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin))
gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin)) gLog.Println(LvINFO, "TCP connection ok")
gLog.Println(LvINFO, "TCP connection ok") t.linkModeWeb = LinkModeIPv4
t.linkModeWeb = LinkModeIPv4 if t.config.linkMode == LinkModeIntranet {
if t.config.linkMode == LinkModeIntranet { t.linkModeWeb = LinkModeIntranet
t.linkModeWeb = LinkModeIntranet }
} return ul, nil
return ul, nil }
}
func (t *P2PTunnel) connectUnderlayTCPSymmetric() (c underlay, err error) {
func (t *P2PTunnel) connectUnderlayTCPSymmetric() (c underlay, err error) { gLog.Printf(LvDEBUG, "connectUnderlayTCPSymmetric %s start ", t.config.LogPeerNode())
gLog.Printf(LvDEBUG, "connectUnderlayTCPSymmetric %s start ", t.config.PeerNode) defer gLog.Printf(LvDEBUG, "connectUnderlayTCPSymmetric %s end ", t.config.LogPeerNode())
defer gLog.Printf(LvDEBUG, "connectUnderlayTCPSymmetric %s end ", t.config.PeerNode) ts := time.Duration(int64(t.punchTs) + GNetwork.dt + GNetwork.ddtma*int64(time.Since(GNetwork.hbTime)+PunchTsDelay)/int64(NetworkHeartbeatTime) - time.Now().UnixNano())
ts := time.Duration(int64(t.punchTs) + t.pn.dt + t.pn.ddtma*int64(time.Since(t.pn.hbTime)+PunchTsDelay)/int64(NetworkHeartbeatTime) - time.Now().UnixNano()) if ts > PunchTsDelay || ts < 0 {
if ts > PunchTsDelay || ts < 0 { ts = PunchTsDelay
ts = PunchTsDelay }
} gLog.Printf(LvDEBUG, "sleep %d ms", ts/time.Millisecond)
gLog.Printf(LvDEBUG, "sleep %d ms", ts/time.Millisecond) time.Sleep(ts)
time.Sleep(ts) startTime := time.Now()
startTime := time.Now() t.linkModeWeb = LinkModeTCPPunch
t.linkModeWeb = LinkModeTCPPunch gotCh := make(chan *underlayTCP, 1)
gotCh := make(chan *underlayTCP, 1) var wg sync.WaitGroup
var wg sync.WaitGroup var success atomic.Int32
var success atomic.Int32 if t.config.peerNatType == NATSymmetric { // c2s
if t.config.peerNatType == NATSymmetric { // c2s randPorts := rand.Perm(65532)
randPorts := rand.Perm(65532) for i := 0; i < SymmetricHandshakeNum; i++ {
for i := 0; i < SymmetricHandshakeNum; i++ { wg.Add(1)
wg.Add(1) go func(port int) {
go func(port int) { defer wg.Done()
defer wg.Done() ul, err := dialTCP(t.config.peerIP, port, t.coneLocalPort, LinkModeTCPPunch)
ul, err := dialTCP(t.config.peerIP, port, t.coneLocalPort, LinkModeTCPPunch) if err != nil {
if err != nil { return
return }
} if !success.CompareAndSwap(0, 1) {
if !success.CompareAndSwap(0, 1) { ul.Close() // only cone side close
ul.Close() // only cone side close return
return }
} err = ul.WriteMessage(MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
err = ul.WriteMessage(MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id}) if err != nil {
if err != nil { ul.Close()
ul.Close() return
return }
} _, buff, err := ul.ReadBuffer()
_, buff, err := ul.ReadBuffer() if err != nil {
if err != nil { gLog.Println(LvDEBUG, "c2s ul.ReadBuffer error:", err)
gLog.Printf(LvERROR, "utcp.ReadBuffer error:", err) return
return }
} req := P2PHandshakeReq{}
req := P2PHandshakeReq{} if err = json.Unmarshal(buff, &req); err != nil {
if err = json.Unmarshal(buff, &req); err != nil { return
return }
} if req.ID != t.id {
if req.ID != t.id { return
return }
} gLog.Printf(LvINFO, "handshakeS2C TCP ok. cost %dms", time.Since(startTime)/time.Millisecond)
gLog.Printf(LvINFO, "handshakeS2C TCP ok. cost %dms", time.Since(startTime)/time.Millisecond)
gotCh <- ul
gotCh <- ul close(gotCh)
close(gotCh) }(randPorts[i] + 2)
}(randPorts[i] + 2) }
}
} else { // s2c
} else { // s2c for i := 0; i < SymmetricHandshakeNum; i++ {
for i := 0; i < SymmetricHandshakeNum; i++ { wg.Add(1)
wg.Add(1) go func() {
go func() { defer wg.Done()
defer wg.Done() ul, err := dialTCP(t.config.peerIP, t.config.peerConeNatPort, 0, LinkModeTCPPunch)
ul, err := dialTCP(t.config.peerIP, t.config.peerConeNatPort, 0, LinkModeTCPPunch) if err != nil {
if err != nil { return
return }
}
_, buff, err := ul.ReadBuffer()
_, buff, err := ul.ReadBuffer() if err != nil {
if err != nil { gLog.Println(LvDEBUG, "s2c ul.ReadBuffer error:", err)
gLog.Printf(LvERROR, "utcp.ReadBuffer error:", err) return
return }
} req := P2PHandshakeReq{}
req := P2PHandshakeReq{} if err = json.Unmarshal(buff, &req); err != nil {
if err = json.Unmarshal(buff, &req); err != nil { return
return }
} if req.ID != t.id {
if req.ID != t.id { return
return }
} err = ul.WriteMessage(MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
err = ul.WriteMessage(MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id}) if err != nil {
if err != nil { ul.Close()
ul.Close() return
return }
} if success.CompareAndSwap(0, 1) {
if success.CompareAndSwap(0, 1) { gotCh <- ul
gotCh <- ul close(gotCh)
close(gotCh) }
} }()
}() }
} }
} select {
select { case <-time.After(HandshakeTimeout):
case <-time.After(HandshakeTimeout): return nil, fmt.Errorf("wait tcp handshake timeout")
return nil, fmt.Errorf("wait tcp handshake timeout") case ul := <-gotCh:
case ul := <-gotCh: return ul, nil
return ul, nil }
} }
}
func (t *P2PTunnel) connectUnderlayTCP6() (c underlay, err error) {
func (t *P2PTunnel) connectUnderlayTCP6() (c underlay, err error) { gLog.Printf(LvDEBUG, "connectUnderlayTCP6 %s start ", t.config.LogPeerNode())
gLog.Printf(LvDEBUG, "connectUnderlayTCP6 %s start ", t.config.PeerNode) defer gLog.Printf(LvDEBUG, "connectUnderlayTCP6 %s end ", t.config.LogPeerNode())
defer gLog.Printf(LvDEBUG, "connectUnderlayTCP6 %s end ", t.config.PeerNode) var ul *underlayTCP6
var ul *underlayTCP6 if t.config.isUnderlayServer == 1 {
if t.config.isUnderlayServer == 1 { GNetwork.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
t.pn.push(t.config.PeerNode, MsgPushUnderlayConnect, nil) ul, err = listenTCP6(t.coneNatPort, UnderlayConnectTimeout)
ul, err = listenTCP6(t.coneNatPort, UnderlayConnectTimeout) if err != nil {
if err != nil { return nil, fmt.Errorf("listen TCP6 error:%s", err)
return nil, fmt.Errorf("listen TCP6 error:%s", err) }
} _, buff, err := ul.ReadBuffer()
_, buff, err := ul.ReadBuffer() if err != nil {
if err != nil { return nil, fmt.Errorf("read start msg error:%s", err)
return nil, fmt.Errorf("read start msg error:%s", err) }
} if buff != nil {
if buff != nil { gLog.Println(LvDEBUG, string(buff))
gLog.Println(LvDEBUG, string(buff)) }
} ul.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, []byte("OpenP2P,hello2"))
ul.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, []byte("OpenP2P,hello2")) gLog.Println(LvDEBUG, "TCP6 connection ok")
gLog.Println(LvDEBUG, "TCP6 connection ok") t.linkModeWeb = LinkModeIPv6
t.linkModeWeb = LinkModeIPv6 return ul, nil
return ul, nil }
}
//else
//else GNetwork.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, ReadMsgTimeout)
t.pn.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, ReadMsgTimeout) gLog.Println(LvDEBUG, "TCP6 dial to ", t.config.peerIPv6)
gLog.Println(LvDEBUG, "TCP6 dial to ", t.config.peerIPv6) ul, err = dialTCP6(t.config.peerIPv6, t.config.peerConeNatPort)
ul, err = dialTCP6(t.config.peerIPv6, t.config.peerConeNatPort) if err != nil || ul == nil {
if err != nil { return nil, fmt.Errorf("TCP6 dial to %s:%d error:%s", t.config.peerIPv6, t.config.peerConeNatPort, err)
return nil, fmt.Errorf("TCP6 dial to %s:%d error:%s", t.config.peerIPv6, t.config.peerConeNatPort, err) }
} handshakeBegin := time.Now()
handshakeBegin := time.Now() ul.WriteBytes(MsgP2P, MsgTunnelHandshake, []byte("OpenP2P,hello"))
ul.WriteBytes(MsgP2P, MsgTunnelHandshake, []byte("OpenP2P,hello")) _, buff, errR := ul.ReadBuffer()
_, buff, err := ul.ReadBuffer() if errR != nil {
if err != nil { return nil, fmt.Errorf("read MsgTunnelHandshake error:%s", errR)
return nil, fmt.Errorf("read MsgTunnelHandshake error:%s", err) }
} if buff != nil {
if buff != nil { gLog.Println(LvDEBUG, string(buff))
gLog.Println(LvDEBUG, string(buff)) }
}
gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin))
gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin)) gLog.Println(LvINFO, "TCP6 connection ok")
gLog.Println(LvINFO, "TCP6 connection ok") t.linkModeWeb = LinkModeIPv6
t.linkModeWeb = LinkModeIPv6 return ul, nil
return ul, nil }
}
func (t *P2PTunnel) readLoop() {
func (t *P2PTunnel) readLoop() { decryptData := make([]byte, ReadBuffLen+PaddingSize) // 16 bytes for padding
decryptData := make([]byte, ReadBuffLen+PaddingSize) // 16 bytes for padding gLog.Printf(LvDEBUG, "%d tunnel readloop start", t.id)
gLog.Printf(LvDEBUG, "%d tunnel readloop start", t.id) for t.isRuning() {
for t.isRuning() { t.conn.SetReadDeadline(time.Now().Add(TunnelHeartbeatTime * 2))
t.conn.SetReadDeadline(time.Now().Add(TunnelHeartbeatTime * 2)) head, body, err := t.conn.ReadBuffer()
head, body, err := t.conn.ReadBuffer() if err != nil {
if err != nil { if t.isRuning() {
if t.isRuning() { gLog.Printf(LvERROR, "%d tunnel read error:%s", t.id, err)
gLog.Printf(LvERROR, "%d tunnel read error:%s", t.id, err) }
} break
break }
} if head.MainType != MsgP2P {
if head.MainType != MsgP2P { gLog.Printf(LvWARN, "%d head.MainType != MsgP2P", t.id)
gLog.Printf(LvWARN, "%d head.MainType != MsgP2P", t.id) continue
continue }
} // TODO: replace some case implement to functions
// TODO: replace some case implement to functions switch head.SubType {
switch head.SubType { case MsgTunnelHeartbeat:
case MsgTunnelHeartbeat: t.hbMtx.Lock()
t.hbMtx.Lock() t.hbTime = time.Now()
t.hbTime = time.Now() t.hbMtx.Unlock()
t.hbMtx.Unlock() t.conn.WriteBytes(MsgP2P, MsgTunnelHeartbeatAck, nil)
t.conn.WriteBytes(MsgP2P, MsgTunnelHeartbeatAck, nil) gLog.Printf(LvDev, "%d read tunnel heartbeat", t.id)
gLog.Printf(LvDev, "%d read tunnel heartbeat", t.id) case MsgTunnelHeartbeatAck:
case MsgTunnelHeartbeatAck: t.hbMtx.Lock()
t.hbMtx.Lock() t.hbTime = time.Now()
t.hbTime = time.Now() t.hbMtx.Unlock()
t.hbMtx.Unlock() gLog.Printf(LvDev, "%d read tunnel heartbeat ack", t.id)
gLog.Printf(LvDev, "%d read tunnel heartbeat ack", t.id) case MsgOverlayData:
case MsgOverlayData: if len(body) < overlayHeaderSize {
if len(body) < overlayHeaderSize { gLog.Printf(LvWARN, "%d len(body) < overlayHeaderSize", t.id)
gLog.Printf(LvWARN, "%d len(body) < overlayHeaderSize", t.id) continue
continue }
} overlayID := binary.LittleEndian.Uint64(body[:8])
overlayID := binary.LittleEndian.Uint64(body[:8]) gLog.Printf(LvDev, "%d tunnel read overlay data %d bodylen=%d", t.id, overlayID, head.DataLen)
gLog.Printf(LvDev, "%d tunnel read overlay data %d bodylen=%d", t.id, overlayID, head.DataLen) s, ok := t.overlayConns.Load(overlayID)
s, ok := t.overlayConns.Load(overlayID) if !ok {
if !ok { // debug level, when overlay connection closed, always has some packet not found tunnel
// 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.Printf(LvDEBUG, "%d tunnel not found overlay connection %d", t.id, overlayID) continue
continue }
} overlayConn, ok := s.(*overlayConn)
overlayConn, ok := s.(*overlayConn) if !ok {
if !ok { continue
continue }
} payload := body[overlayHeaderSize:]
payload := body[overlayHeaderSize:] var err error
var err error if overlayConn.appKey != 0 {
if overlayConn.appKey != 0 { payload, _ = decryptBytes(overlayConn.appKeyBytes, decryptData, body[overlayHeaderSize:], int(head.DataLen-uint32(overlayHeaderSize)))
payload, _ = decryptBytes(overlayConn.appKeyBytes, decryptData, body[overlayHeaderSize:], int(head.DataLen-uint32(overlayHeaderSize))) }
} _, err = overlayConn.Write(payload)
_, err = overlayConn.Write(payload) if err != nil {
if err != nil { gLog.Println(LvERROR, "overlay write error:", err)
gLog.Println(LvERROR, "overlay write error:", err) }
} case MsgNodeData:
case MsgNodeData: t.handleNodeData(head, body, false)
t.handleNodeData(head, body, false) case MsgRelayNodeData:
case MsgRelayNodeData: t.handleNodeData(head, body, true)
t.handleNodeData(head, body, true) case MsgRelayData:
case MsgRelayData: if len(body) < 8 {
if len(body) < 8 { continue
continue }
} tunnelID := binary.LittleEndian.Uint64(body[:8])
tunnelID := binary.LittleEndian.Uint64(body[:8]) gLog.Printf(LvDev, "relay data to %d, len=%d", tunnelID, head.DataLen-RelayHeaderSize)
gLog.Printf(LvDev, "relay data to %d, len=%d", tunnelID, head.DataLen-RelayHeaderSize) if err := GNetwork.relay(tunnelID, body[RelayHeaderSize:]); err != nil {
if err := t.pn.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.Printf(LvERROR, "%s:%d relay to %d len=%d error:%s", t.config.PeerNode, t.id, tunnelID, len(body), ErrRelayTunnelNotFound) }
} case MsgRelayHeartbeat:
case MsgRelayHeartbeat: req := RelayHeartbeat{}
req := RelayHeartbeat{} if err := json.Unmarshal(body, &req); err != nil {
if err := json.Unmarshal(body, &req); err != nil { gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err) continue
continue }
} // TODO: debug relay heartbeat
// TODO: debug relay heartbeat gLog.Printf(LvDEBUG, "read MsgRelayHeartbeat from rtid:%d,appid:%d", req.RelayTunnelID, req.AppID)
gLog.Printf(LvDEBUG, "read MsgRelayHeartbeat from rtid:%d,appid:%d", req.RelayTunnelID, req.AppID) // update app hbtime
// update app hbtime GNetwork.updateAppHeartbeat(req.AppID)
t.pn.updateAppHeartbeat(req.AppID) req.From = gConf.Network.Node
req.From = gConf.Network.Node t.WriteMessage(req.RelayTunnelID, MsgP2P, MsgRelayHeartbeatAck, &req)
t.WriteMessage(req.RelayTunnelID, MsgP2P, MsgRelayHeartbeatAck, &req) case MsgRelayHeartbeatAck:
case MsgRelayHeartbeatAck: req := RelayHeartbeat{}
req := RelayHeartbeat{} err := json.Unmarshal(body, &req)
err := json.Unmarshal(body, &req) if err != nil {
if err != nil { gLog.Printf(LvERROR, "wrong RelayHeartbeat:%s", err)
gLog.Printf(LvERROR, "wrong RelayHeartbeat:%s", err) continue
continue }
} // TODO: debug relay heartbeat
// TODO: debug relay heartbeat gLog.Printf(LvDEBUG, "read MsgRelayHeartbeatAck to appid:%d", req.AppID)
gLog.Printf(LvDEBUG, "read MsgRelayHeartbeatAck to appid:%d", req.AppID) GNetwork.updateAppHeartbeat(req.AppID)
t.pn.updateAppHeartbeat(req.AppID) case MsgOverlayConnectReq:
case MsgOverlayConnectReq: req := OverlayConnectReq{}
req := OverlayConnectReq{} if err := json.Unmarshal(body, &req); err != nil {
if err := json.Unmarshal(body, &req); err != nil { gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err) continue
continue }
} // app connect only accept token(not relay totp token), avoid someone using the share relay node's token
// app connect only accept token(not relay totp token), avoid someone using the share relay node's token if req.Token != gConf.Network.Token {
if req.Token != gConf.Network.Token { gLog.Println(LvERROR, "Access Denied:", req.Token)
gLog.Println(LvERROR, "Access Denied:", req.Token) continue
continue }
}
overlayID := req.ID
overlayID := req.ID gLog.Printf(LvDEBUG, "App:%d overlayID:%d connect %s:%d", req.AppID, overlayID, req.DstIP, req.DstPort)
gLog.Printf(LvDEBUG, "App:%d overlayID:%d connect %s:%d", req.AppID, overlayID, req.DstIP, req.DstPort) oConn := overlayConn{
oConn := overlayConn{ tunnel: t,
tunnel: t, id: overlayID,
id: overlayID, isClient: false,
isClient: false, rtid: req.RelayTunnelID,
rtid: req.RelayTunnelID, appID: req.AppID,
appID: req.AppID, appKey: GetKey(req.AppID),
appKey: GetKey(req.AppID), running: true,
running: true, }
} if req.Protocol == "udp" {
if req.Protocol == "udp" { oConn.connUDP, err = net.DialUDP("udp", nil, &net.UDPAddr{IP: net.ParseIP(req.DstIP), Port: req.DstPort})
oConn.connUDP, err = net.DialUDP("udp", nil, &net.UDPAddr{IP: net.ParseIP(req.DstIP), Port: req.DstPort}) } else {
} else { oConn.connTCP, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", req.DstIP, req.DstPort), ReadMsgTimeout)
oConn.connTCP, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", req.DstIP, req.DstPort), ReadMsgTimeout)
}
} if err != nil {
if err != nil { gLog.Println(LvERROR, err)
gLog.Println(LvERROR, err) continue
continue }
}
// calc key bytes for encrypt
// calc key bytes for encrypt if oConn.appKey != 0 {
if oConn.appKey != 0 { encryptKey := make([]byte, AESKeySize)
encryptKey := make([]byte, AESKeySize) binary.LittleEndian.PutUint64(encryptKey, oConn.appKey)
binary.LittleEndian.PutUint64(encryptKey, oConn.appKey) binary.LittleEndian.PutUint64(encryptKey[8:], oConn.appKey)
binary.LittleEndian.PutUint64(encryptKey[8:], oConn.appKey) oConn.appKeyBytes = encryptKey
oConn.appKeyBytes = encryptKey }
}
t.overlayConns.Store(oConn.id, &oConn)
t.overlayConns.Store(oConn.id, &oConn) go oConn.run()
go oConn.run() case MsgOverlayDisconnectReq:
case MsgOverlayDisconnectReq: req := OverlayDisconnectReq{}
req := OverlayDisconnectReq{} if err := json.Unmarshal(body, &req); err != nil {
if err := json.Unmarshal(body, &req); err != nil { gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err) continue
continue }
} overlayID := req.ID
overlayID := req.ID gLog.Printf(LvDEBUG, "%d disconnect overlay connection %d", t.id, overlayID)
gLog.Printf(LvDEBUG, "%d disconnect overlay connection %d", t.id, overlayID) i, ok := t.overlayConns.Load(overlayID)
i, ok := t.overlayConns.Load(overlayID) if ok {
if ok { oConn := i.(*overlayConn)
oConn := i.(*overlayConn) oConn.Close()
oConn.Close() }
} default:
default: }
} }
} t.close()
t.close() gLog.Printf(LvDEBUG, "%d tunnel readloop end", t.id)
gLog.Printf(LvDEBUG, "%d tunnel readloop end", t.id) }
}
func (t *P2PTunnel) writeLoop() {
func (t *P2PTunnel) writeLoop() { t.hbMtx.Lock()
t.hbMtx.Lock() t.hbTime = time.Now() // init
t.hbTime = time.Now() // init t.hbMtx.Unlock()
t.hbMtx.Unlock() tc := time.NewTicker(TunnelHeartbeatTime)
tc := time.NewTicker(TunnelHeartbeatTime) defer tc.Stop()
defer tc.Stop() gLog.Printf(LvDEBUG, "%s:%d tunnel writeLoop start", t.config.LogPeerNode(), t.id)
gLog.Printf(LvDEBUG, "%s:%d tunnel writeLoop start", t.config.PeerNode, t.id) defer gLog.Printf(LvDEBUG, "%s:%d tunnel writeLoop end", t.config.LogPeerNode(), t.id)
defer gLog.Printf(LvDEBUG, "%s:%d tunnel writeLoop end", t.config.PeerNode, t.id) for t.isRuning() {
for t.isRuning() { select {
select { case buff := <-t.writeDataSmall:
case buff := <-t.writeDataSmall: t.conn.WriteBuffer(buff)
t.conn.WriteBuffer(buff) // gLog.Printf(LvDEBUG, "write icmp %d", time.Now().Unix())
// gLog.Printf(LvDEBUG, "write icmp %d", time.Now().Unix()) default:
default: select {
select { case buff := <-t.writeDataSmall:
case buff := <-t.writeDataSmall: t.conn.WriteBuffer(buff)
t.conn.WriteBuffer(buff) // gLog.Printf(LvDEBUG, "write icmp %d", time.Now().Unix())
// gLog.Printf(LvDEBUG, "write icmp %d", time.Now().Unix()) case buff := <-t.writeData:
case buff := <-t.writeData: t.conn.WriteBuffer(buff)
t.conn.WriteBuffer(buff) case <-tc.C:
case <-tc.C: // tunnel send
// tunnel send err := t.conn.WriteBytes(MsgP2P, MsgTunnelHeartbeat, nil)
err := t.conn.WriteBytes(MsgP2P, MsgTunnelHeartbeat, nil) if err != nil {
if err != nil { gLog.Printf(LvERROR, "%d write tunnel heartbeat error %s", t.id, err)
gLog.Printf(LvERROR, "%d write tunnel heartbeat error %s", t.id, err) t.close()
t.close() return
return }
} gLog.Printf(LvDev, "%d write tunnel heartbeat ok", t.id)
gLog.Printf(LvDev, "%d write tunnel heartbeat ok", t.id) }
} }
} }
} }
}
func (t *P2PTunnel) listen() error {
func (t *P2PTunnel) listen() error { // notify client to connect
// notify client to connect rsp := PushConnectRsp{
rsp := PushConnectRsp{ Error: 0,
Error: 0, Detail: "connect ok",
Detail: "connect ok", To: t.config.PeerNode,
To: t.config.PeerNode, From: gConf.Network.Node,
From: gConf.Network.Node, NatType: gConf.Network.natType,
NatType: gConf.Network.natType, HasIPv4: gConf.Network.hasIPv4,
HasIPv4: gConf.Network.hasIPv4, // IPv6: gConf.Network.IPv6,
// IPv6: gConf.Network.IPv6, HasUPNPorNATPMP: gConf.Network.hasUPNPorNATPMP,
HasUPNPorNATPMP: gConf.Network.hasUPNPorNATPMP, FromIP: gConf.Network.publicIP,
FromIP: gConf.Network.publicIP, ConeNatPort: t.coneNatPort,
ConeNatPort: t.coneNatPort, ID: t.id,
ID: t.id, PunchTs: uint64(time.Now().UnixNano() + int64(PunchTsDelay) - GNetwork.dt),
PunchTs: uint64(time.Now().UnixNano() + int64(PunchTsDelay) - t.pn.dt), Version: OpenP2PVersion,
Version: OpenP2PVersion, }
} t.punchTs = rsp.PunchTs
t.punchTs = rsp.PunchTs // only private node set ipv6
// only private node set ipv6 if t.config.fromToken == gConf.Network.Token {
if t.config.fromToken == gConf.Network.Token { rsp.IPv6 = gConf.IPv6()
rsp.IPv6 = gConf.IPv6() }
}
GNetwork.push(t.config.PeerNode, MsgPushConnectRsp, rsp)
t.pn.push(t.config.PeerNode, MsgPushConnectRsp, rsp) gLog.Printf(LvDEBUG, "p2ptunnel wait for connecting")
gLog.Printf(LvDEBUG, "p2ptunnel wait for connecting") t.tunnelServer = true
t.tunnelServer = true return t.start()
return t.start() }
}
func (t *P2PTunnel) closeOverlayConns(appID uint64) {
func (t *P2PTunnel) closeOverlayConns(appID uint64) { t.overlayConns.Range(func(_, i interface{}) bool {
t.overlayConns.Range(func(_, i interface{}) bool { oConn := i.(*overlayConn)
oConn := i.(*overlayConn) if oConn.appID == appID {
if oConn.appID == appID { oConn.Close()
oConn.Close() }
} return true
return true })
}) }
}
func (t *P2PTunnel) handleNodeData(head *openP2PHeader, body []byte, isRelay bool) {
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.Printf(LvDev, "%d tunnel read node data bodylen=%d, relay=%t", t.id, head.DataLen, isRelay) ch := GNetwork.nodeData
ch := t.pn.nodeData // if body[9] == 1 { // TODO: deal relay
// if body[9] == 1 { // TODO: deal relay // ch = GNetwork.nodeDataSmall
// ch = t.pn.nodeDataSmall // gLog.Printf(LvDEBUG, "read icmp %d", time.Now().Unix())
// gLog.Printf(LvDEBUG, "read icmp %d", time.Now().Unix()) // }
// } if isRelay {
if isRelay { fromPeerID := binary.LittleEndian.Uint64(body[:8])
fromPeerID := binary.LittleEndian.Uint64(body[:8]) ch <- &NodeData{fromPeerID, body[8:]} // TODO: cache peerNodeID; encrypt/decrypt
ch <- &NodeData{fromPeerID, body[8:]} // TODO: cache peerNodeID; encrypt/decrypt } else {
} else { ch <- &NodeData{NodeNameToID(t.config.PeerNode), body} // TODO: cache peerNodeID; encrypt/decrypt
ch <- &NodeData{NodeNameToID(t.config.PeerNode), body} // TODO: cache peerNodeID; encrypt/decrypt }
} }
}
func (t *P2PTunnel) asyncWriteNodeData(mainType, subType uint16, data []byte) {
func (t *P2PTunnel) asyncWriteNodeData(mainType, subType uint16, data []byte) { writeBytes := append(encodeHeader(mainType, subType, uint32(len(data))), data...)
writeBytes := append(encodeHeader(mainType, subType, uint32(len(data))), data...) // if len(data) < 192 {
// if len(data) < 192 { if data[9] == 1 { // icmp
if data[9] == 1 { // icmp select {
select { case t.writeDataSmall <- writeBytes:
case t.writeDataSmall <- writeBytes: // gLog.Printf(LvWARN, "%s:%d t.writeDataSmall write %d", t.config.PeerNode, t.id, len(t.writeDataSmall))
// gLog.Printf(LvWARN, "%s:%d t.writeDataSmall write %d", t.config.PeerNode, t.id, len(t.writeDataSmall)) default:
default: gLog.Printf(LvWARN, "%s:%d t.writeDataSmall is full, drop it", t.config.LogPeerNode(), t.id)
gLog.Printf(LvWARN, "%s:%d t.writeDataSmall is full, drop it", t.config.PeerNode, t.id) }
} } else {
} else { select {
select { case t.writeData <- writeBytes:
case t.writeData <- writeBytes: default:
default: gLog.Printf(LvWARN, "%s:%d t.writeData is full, drop it", t.config.LogPeerNode(), t.id)
gLog.Printf(LvWARN, "%s:%d t.writeData is full, drop it", t.config.PeerNode, t.id) }
} }
}
}
}
func (t *P2PTunnel) WriteMessage(rtid uint64, mainType uint16, subType uint16, req interface{}) error {
func (t *P2PTunnel) WriteMessage(rtid uint64, mainType uint16, subType uint16, req interface{}) error { if rtid == 0 {
if rtid == 0 { return t.conn.WriteMessage(mainType, subType, &req)
return t.conn.WriteMessage(mainType, subType, &req) }
} relayHead := new(bytes.Buffer)
relayHead := new(bytes.Buffer) binary.Write(relayHead, binary.LittleEndian, rtid)
binary.Write(relayHead, binary.LittleEndian, rtid) msg, _ := newMessage(mainType, subType, &req)
msg, _ := newMessage(mainType, subType, &req) msgWithHead := append(relayHead.Bytes(), msg...)
msgWithHead := append(relayHead.Bytes(), msg...) return t.conn.WriteBytes(mainType, MsgRelayData, msgWithHead)
return t.conn.WriteBytes(mainType, MsgRelayData, msgWithHead)
}
}
+87
View File
@@ -0,0 +1,87 @@
package openp2p
import (
"fmt"
"net"
"os"
"time"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
)
// 定义ICMP回显请求和应答的结构
type ICMPMessage struct {
Type uint8
Code uint8
Checksum uint16
Ident uint16
Seq uint16
Data []byte
}
// Ping sends an ICMP Echo request to the specified host and returns the response time.
func Ping(host string) (time.Duration, error) {
// Resolve the IP address of the host
ipAddr, err := net.ResolveIPAddr("ip4", host)
if err != nil {
return 0, fmt.Errorf("failed to resolve host: %v", err)
}
// Create an ICMP listener
conn, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
if err != nil {
return 0, fmt.Errorf("failed to create ICMP connection: %v", err)
}
defer conn.Close()
// Create an ICMP Echo request message
message := icmp.Message{
Type: ipv4.ICMPTypeEcho,
Code: 0,
Body: &icmp.Echo{
ID: os.Getpid() & 0xffff,
Seq: 1,
Data: []byte("HELLO-R-U-THERE"),
},
}
// Marshal the message into binary form
messageBytes, err := message.Marshal(nil)
if err != nil {
return 0, fmt.Errorf("failed to marshal ICMP message: %v", err)
}
// Send the ICMP Echo request
start := time.Now()
if _, err := conn.WriteTo(messageBytes, ipAddr); err != nil {
return 0, fmt.Errorf("failed to send ICMP request: %v", err)
}
// Set a deadline for the response
err = conn.SetReadDeadline(time.Now().Add(3 * time.Second))
if err != nil {
return 0, fmt.Errorf("failed to set read deadline: %v", err)
}
// Read the ICMP response
response := make([]byte, 1500)
n, _, err := conn.ReadFrom(response)
if err != nil {
return 0, fmt.Errorf("failed to read ICMP response: %v", err)
}
// Parse the ICMP response message
parsedMessage, err := icmp.ParseMessage(ipv4.ICMPTypeEchoReply.Protocol(), response[:n])
if err != nil {
return 0, fmt.Errorf("failed to parse ICMP response: %v", err)
}
// Check if the response is an Echo reply
if parsedMessage.Type == ipv4.ICMPTypeEchoReply {
duration := time.Since(start)
return duration, nil
} else {
return 0, fmt.Errorf("unexpected ICMP message: %+v", parsedMessage)
}
}
+16 -3
View File
@@ -10,7 +10,7 @@ import (
"time" "time"
) )
const OpenP2PVersion = "3.18.4" const OpenP2PVersion = "3.21.12"
const ProductName string = "openp2p" const ProductName string = "openp2p"
const LeastSupportVersion = "3.0.0" const LeastSupportVersion = "3.0.0"
const SyncServerTimeVersion = "3.9.0" const SyncServerTimeVersion = "3.9.0"
@@ -108,6 +108,7 @@ const (
MsgPushReportGoroutine = 16 MsgPushReportGoroutine = 16
MsgPushReportMemApps = 17 MsgPushReportMemApps = 17
MsgPushServerSideSaveMemApp = 18 MsgPushServerSideSaveMemApp = 18
MsgPushCheckRemoteService = 19
) )
// MsgP2P sub type message // MsgP2P sub type message
@@ -143,6 +144,7 @@ const (
MsgReportApps MsgReportApps
MsgReportLog MsgReportLog
MsgReportMemApps MsgReportMemApps
MsgReportResponse
) )
const ( const (
@@ -165,7 +167,7 @@ const (
MaxRetry = 10 MaxRetry = 10
Cone2ConeTCPPunchMaxRetry = 1 Cone2ConeTCPPunchMaxRetry = 1
Cone2ConeUDPPunchMaxRetry = 1 Cone2ConeUDPPunchMaxRetry = 1
PublicIPEchoTimeout = time.Second * 1 PublicIPEchoTimeout = time.Second * 3
NatTestTimeout = time.Second * 5 NatTestTimeout = time.Second * 5
UDPReadTimeout = time.Second * 5 UDPReadTimeout = time.Second * 5
ClientAPITimeout = time.Second * 10 ClientAPITimeout = time.Second * 10
@@ -215,6 +217,12 @@ const (
MsgSDWANInfoRsp MsgSDWANInfoRsp
) )
// MsgNATDetect
const (
MsgNAT = iota
MsgPublicIP
)
func newMessage(mainType uint16, subType uint16, packet interface{}) ([]byte, error) { func newMessage(mainType uint16, subType uint16, packet interface{}) ([]byte, error) {
data, err := json.Marshal(packet) data, err := json.Marshal(packet)
if err != nil { if err != nil {
@@ -493,7 +501,7 @@ type SDWANInfo struct {
ForceRelay int32 `json:"forceRelay,omitempty"` ForceRelay int32 `json:"forceRelay,omitempty"`
PunchPriority int32 `json:"punchPriority,omitempty"` PunchPriority int32 `json:"punchPriority,omitempty"`
Enable int32 `json:"enable,omitempty"` Enable int32 `json:"enable,omitempty"`
Nodes []SDWANNode Nodes []*SDWANNode
} }
const ( const (
@@ -510,6 +518,11 @@ type ServerSideSaveMemApp struct {
AppID uint64 `json:"appID,omitempty"` AppID uint64 `json:"appID,omitempty"`
} }
type CheckRemoteService struct {
Host string `json:"host,omitempty"`
Port uint32 `json:"port,omitempty"`
}
const rootCA = `-----BEGIN CERTIFICATE----- const rootCA = `-----BEGIN CERTIFICATE-----
MIIDhTCCAm0CFHm0cd8dnGCbUW/OcS56jf0gvRk7MA0GCSqGSIb3DQEBCwUAMH4x MIIDhTCCAm0CFHm0cd8dnGCbUW/OcS56jf0gvRk7MA0GCSqGSIb3DQEBCwUAMH4x
CzAJBgNVBAYTAkNOMQswCQYDVQQIDAJHRDETMBEGA1UECgwKb3BlbnAycC5jbjET CzAJBgNVBAYTAkNOMQswCQYDVQQIDAJHRDETMBEGA1UECgwKb3BlbnAycC5jbjET
+323 -292
View File
@@ -1,292 +1,323 @@
package openp2p package openp2p
import ( import (
"encoding/binary" "encoding/binary"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net" "net"
"runtime" "runtime"
"strings" "strings"
"sync" "sync"
"time" "time"
) )
type PacketHeader struct { type PacketHeader struct {
version int version int
// src uint32 // src uint32
// prot uint8 // prot uint8
protocol byte protocol byte
dst uint32 dst uint32
port uint16 port uint16
} }
func parseHeader(b []byte, h *PacketHeader) error { func parseHeader(b []byte, h *PacketHeader) error {
if len(b) < 20 { if len(b) < 20 {
return fmt.Errorf("small packet") return fmt.Errorf("small packet")
} }
h.version = int(b[0] >> 4) h.version = int(b[0] >> 4)
h.protocol = byte(b[9]) h.protocol = byte(b[9])
if h.version == 4 { if h.version == 4 {
h.dst = binary.BigEndian.Uint32(b[16:20]) h.dst = binary.BigEndian.Uint32(b[16:20])
} else if h.version != 6 { } else if h.version != 6 {
return fmt.Errorf("unknown version in ip header:%d", h.version) return fmt.Errorf("unknown version in ip header:%d", h.version)
} }
if h.protocol == 6 || h.protocol == 17 { // TCP or UDP if h.protocol == 6 || h.protocol == 17 { // TCP or UDP
h.port = binary.BigEndian.Uint16(b[22:24]) h.port = binary.BigEndian.Uint16(b[22:24])
} }
return nil return nil
} }
type sdwanNode struct { type sdwanNode struct {
name string name string
id uint64 id uint64
} }
type p2pSDWAN struct { type p2pSDWAN struct {
nodeName string nodeName string
tun *optun tun *optun
sysRoute sync.Map // ip:sdwanNode sysRoute sync.Map // ip:sdwanNode
subnet *net.IPNet subnet *net.IPNet
gateway net.IP gateway net.IP
virtualIP *net.IPNet virtualIP *net.IPNet
internalRoute *IPTree internalRoute *IPTree
} }
func (s *p2pSDWAN) init(name string) error { func (s *p2pSDWAN) reset() {
if gConf.getSDWAN().Gateway == "" { gLog.Println(LvINFO, "reset sdwan when network disconnected")
gLog.Println(LvDEBUG, "not in sdwan clear all ") // clear sysroute
} delRoutesByGateway(s.gateway.String())
if s.internalRoute == nil { // clear internel route
s.internalRoute = NewIPTree("") s.internalRoute = NewIPTree("")
} // clear p2papp
for _, node := range gConf.getAddNodes() {
s.nodeName = name gConf.delete(AppConfig{SrcPort: 0, PeerNode: node.Name})
s.gateway, s.subnet, _ = net.ParseCIDR(gConf.getSDWAN().Gateway) }
for _, node := range gConf.getDelNodes() {
gLog.Println(LvDEBUG, "deal deleted node: ", node.Name) gConf.resetSDWAN()
delRoute(node.IP, s.gateway.String()) }
s.internalRoute.Del(node.IP, node.IP) func (s *p2pSDWAN) init(name string) error {
ipNum, _ := inetAtoN(node.IP) if gConf.getSDWAN().Gateway == "" {
s.sysRoute.Delete(ipNum) gLog.Println(LvDEBUG, "sdwan init: not in sdwan clear all ")
gConf.delete(AppConfig{SrcPort: 0, PeerNode: node.Name}) }
GNetwork.DeleteApp(AppConfig{SrcPort: 0, PeerNode: node.Name}) if s.internalRoute == nil {
arr := strings.Split(node.Resource, ",") s.internalRoute = NewIPTree("")
for _, r := range arr { }
_, ipnet, err := net.ParseCIDR(r)
if err != nil { s.nodeName = name
// fmt.Println("Error parsing CIDR:", err) if gw, sn, err := net.ParseCIDR(gConf.getSDWAN().Gateway); err == nil { // preserve old gateway
continue s.gateway = gw
} s.subnet = sn
if ipnet.Contains(net.ParseIP(gConf.Network.localIP)) { // local ip and resource in the same lan }
continue
} for _, node := range gConf.getDelNodes() {
minIP := ipnet.IP gLog.Println(LvDEBUG, "sdwan init: deal deleted node: ", node.Name)
maxIP := make(net.IP, len(minIP)) gLog.Printf(LvDEBUG, "sdwan init: delRoute: %s, %s ", node.IP, s.gateway.String())
copy(maxIP, minIP) delRoute(node.IP, s.gateway.String())
for i := range minIP { s.internalRoute.Del(node.IP, node.IP)
maxIP[i] = minIP[i] | ^ipnet.Mask[i] ipNum, _ := inetAtoN(node.IP)
} s.sysRoute.Delete(ipNum)
s.internalRoute.Del(minIP.String(), maxIP.String()) gConf.delete(AppConfig{SrcPort: 0, PeerNode: node.Name})
delRoute(ipnet.String(), s.gateway.String()) GNetwork.DeleteApp(AppConfig{SrcPort: 0, PeerNode: node.Name})
} arr := strings.Split(node.Resource, ",")
} for _, r := range arr {
for _, node := range gConf.getAddNodes() { _, ipnet, err := net.ParseCIDR(r)
gLog.Println(LvDEBUG, "deal add node: ", node.Name) if err != nil {
ipNet := &net.IPNet{ // fmt.Println("Error parsing CIDR:", err)
IP: net.ParseIP(node.IP), continue
Mask: s.subnet.Mask, }
} if ipnet.Contains(net.ParseIP(gConf.Network.localIP)) { // local ip and resource in the same lan
if node.Name == s.nodeName { continue
s.virtualIP = ipNet }
gLog.Println(LvINFO, "start tun ", ipNet.String()) minIP := ipnet.IP
err := s.StartTun() maxIP := make(net.IP, len(minIP))
if err != nil { copy(maxIP, minIP)
gLog.Println(LvERROR, "start tun error:", err) for i := range minIP {
return err maxIP[i] = minIP[i] | ^ipnet.Mask[i]
} }
gLog.Println(LvINFO, "start tun ok") s.internalRoute.Del(minIP.String(), maxIP.String())
allowTunForward() delRoute(ipnet.String(), s.gateway.String())
addRoute(s.subnet.String(), s.gateway.String(), s.tun.tunName) gLog.Printf(LvDEBUG, "sdwan init: resource delRoute: %s, %s ", ipnet.String(), s.gateway.String())
// 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 }
initSNATRule(s.subnet.String()) // for network resource for _, node := range gConf.getAddNodes() {
continue gLog.Println(LvDEBUG, "sdwan init: deal add node: ", node.Name)
} ipNet := &net.IPNet{
ip, err := inetAtoN(ipNet.String()) IP: net.ParseIP(node.IP),
if err != nil { Mask: s.subnet.Mask,
return err }
} if node.Name == s.nodeName {
s.sysRoute.Store(ip, &sdwanNode{name: node.Name, id: NodeNameToID(node.Name)}) s.virtualIP = ipNet
s.internalRoute.AddIntIP(ip, ip, &sdwanNode{name: node.Name, id: NodeNameToID(node.Name)}) gLog.Println(LvINFO, "sdwan init: start tun ", ipNet.String())
} err := s.StartTun()
for _, node := range gConf.getAddNodes() { if err != nil {
if node.Name == s.nodeName { // not deal resource itself gLog.Println(LvERROR, "sdwan init: start tun error:", err)
continue return err
} }
if len(node.Resource) > 0 { gLog.Println(LvINFO, "sdwan init: start tun ok")
gLog.Printf(LvINFO, "deal add node: %s resource: %s", node.Name, node.Resource) allowTunForward()
arr := strings.Split(node.Resource, ",") gLog.Printf(LvDEBUG, "sdwan init: addRoute %s %s %s", s.subnet.String(), s.gateway.String(), s.tun.tunName)
for _, r := range arr { addRoute(s.subnet.String(), s.gateway.String(), s.tun.tunName)
// add internal route // addRoute("255.255.255.255/32", s.gateway.String(), s.tun.tunName) // for broadcast
_, ipnet, err := net.ParseCIDR(r) // addRoute("224.0.0.0/4", s.gateway.String(), s.tun.tunName) // for multicast
if err != nil { initSNATRule(s.subnet.String()) // for network resource
fmt.Println("Error parsing CIDR:", err) continue
continue }
} ip, err := inetAtoN(ipNet.String())
if ipnet.Contains(net.ParseIP(gConf.Network.localIP)) { // local ip and resource in the same lan if err != nil {
continue return err
} }
minIP := ipnet.IP s.sysRoute.Store(ip, &sdwanNode{name: node.Name, id: NodeNameToID(node.Name)})
maxIP := make(net.IP, len(minIP)) s.internalRoute.AddIntIP(ip, ip, &sdwanNode{name: node.Name, id: NodeNameToID(node.Name)})
copy(maxIP, minIP) }
for i := range minIP { for _, node := range gConf.getAddNodes() {
maxIP[i] = minIP[i] | ^ipnet.Mask[i] if node.Name == s.nodeName { // not deal resource itself
} continue
s.internalRoute.Add(minIP.String(), maxIP.String(), &sdwanNode{name: node.Name, id: NodeNameToID(node.Name)}) }
// add sys route if len(node.Resource) > 0 {
addRoute(ipnet.String(), s.gateway.String(), s.tun.tunName) gLog.Printf(LvINFO, "sdwan init: deal add node: %s resource: %s", node.Name, node.Resource)
} arr := strings.Split(node.Resource, ",")
} for _, r := range arr {
} // add internal route
gConf.retryAllMemApp() _, ipnet, err := net.ParseCIDR(r)
gLog.Printf(LvINFO, "sdwan init ok") if err != nil {
return nil fmt.Println("sdwan init: Error parsing CIDR:", err)
} continue
}
func (s *p2pSDWAN) run() { if ipnet.Contains(net.ParseIP(gConf.Network.localIP)) { // local ip and resource in the same lan
s.sysRoute.Range(func(key, value interface{}) bool { gLog.Printf(LvDEBUG, "sdwan init: local ip %s in this resource %s, ignore", gConf.Network.localIP, ipnet.IP.String())
node := value.(*sdwanNode) continue
GNetwork.ConnectNode(node.name) }
return true // 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())
if _, err := Ping(ipnet.IP.String()); err == nil {
func (s *p2pSDWAN) readNodeLoop() { gLog.Printf(LvDEBUG, "sdwan init: ping %s ok, ignore this resource", ipnet.IP.String())
gLog.Printf(LvDEBUG, "sdwan readNodeLoop start") continue
defer gLog.Printf(LvDEBUG, "sdwan readNodeLoop end") }
writeBuff := make([][]byte, 1) gLog.Printf(LvDEBUG, "sdwan init: ping %s failed", ipnet.IP.String())
for { }
nd := GNetwork.ReadNode(time.Second * 10) // TODO: read multi packet minIP := ipnet.IP
if nd == nil { maxIP := make(net.IP, len(minIP))
gLog.Printf(LvDev, "waiting for node data") copy(maxIP, minIP)
continue for i := range minIP {
} maxIP[i] = minIP[i] | ^ipnet.Mask[i]
head := PacketHeader{} }
parseHeader(nd.Data, &head) s.internalRoute.Add(minIP.String(), maxIP.String(), &sdwanNode{name: node.Name, id: NodeNameToID(node.Name)})
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)) // add sys route
if PIHeaderSize == 0 { gLog.Printf(LvDEBUG, "sdwan init: addRoute %s %s %s", ipnet.String(), s.gateway.String(), s.tun.tunName)
writeBuff[0] = nd.Data addRoute(ipnet.String(), s.gateway.String(), s.tun.tunName)
} else { }
writeBuff[0] = make([]byte, PIHeaderSize+len(nd.Data)) }
copy(writeBuff[0][PIHeaderSize:], nd.Data) }
} gConf.retryAllMemApp()
gLog.Printf(LvINFO, "sdwan init ok")
len, err := s.tun.Write(writeBuff, PIHeaderSize) return nil
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)
} func (s *p2pSDWAN) run() {
} s.sysRoute.Range(func(key, value interface{}) bool {
} node := value.(*sdwanNode)
GNetwork.ConnectNode(node.name)
func isBroadcastOrMulticast(ipUint32 uint32, subnet *net.IPNet) bool { return true
// return ipUint32 == 0xffffffff || (byte(ipUint32) == 0xff) || (ipUint32>>28 == 0xe) })
return ipUint32 == 0xffffffff || (ipUint32>>28 == 0xe) // 225.255.255.255/32, 224.0.0.0/4 }
}
func (s *p2pSDWAN) readNodeLoop() {
func (s *p2pSDWAN) routeTunPacket(p []byte, head *PacketHeader) { gLog.Printf(LvDEBUG, "sdwan readNodeLoop start")
var node *sdwanNode defer gLog.Printf(LvDEBUG, "sdwan readNodeLoop end")
// v, ok := s.routes.Load(ih.dst) writeBuff := make([][]byte, 1)
v, ok := s.internalRoute.Load(head.dst) for {
if !ok || v == nil { nd := GNetwork.ReadNode(time.Second * 10) // TODO: read multi packet
if isBroadcastOrMulticast(head.dst, s.subnet) { if nd == nil {
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.Printf(LvDev, "waiting for node data")
GNetwork.WriteBroadcast(p) continue
} }
return head := PacketHeader{}
} else { parseHeader(nd.Data, &head)
node = v.(*sdwanNode) 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))
} if PIHeaderSize == 0 {
writeBuff[0] = nd.Data
err := GNetwork.WriteNode(node.id, p) } else {
if err != nil { writeBuff[0] = make([]byte, PIHeaderSize+len(nd.Data))
gLog.Printf(LvDev, "write packet to %s fail: %s", node.name, err) copy(writeBuff[0][PIHeaderSize:], nd.Data)
} }
}
len, err := s.tun.Write(writeBuff, PIHeaderSize)
func (s *p2pSDWAN) readTunLoop() { if err != nil {
gLog.Printf(LvDEBUG, "sdwan readTunLoop start") 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)
defer gLog.Printf(LvDEBUG, "sdwan readTunLoop end") }
readBuff := make([][]byte, ReadTunBuffNum) }
for i := 0; i < ReadTunBuffNum; i++ { }
readBuff[i] = make([]byte, ReadTunBuffSize+PIHeaderSize)
} func isBroadcastOrMulticast(ipUint32 uint32, subnet *net.IPNet) bool {
readBuffSize := make([]int, ReadTunBuffNum) // return ipUint32 == 0xffffffff || (byte(ipUint32) == 0xff) || (ipUint32>>28 == 0xe)
ih := PacketHeader{} return ipUint32 == 0xffffffff || (ipUint32>>28 == 0xe) // 225.255.255.255/32, 224.0.0.0/4
for { }
n, err := s.tun.Read(readBuff, readBuffSize, PIHeaderSize)
if err != nil { func (s *p2pSDWAN) routeTunPacket(p []byte, head *PacketHeader) {
gLog.Printf(LvERROR, "read tun fail: ", err) var node *sdwanNode
return // v, ok := s.routes.Load(ih.dst)
} v, ok := s.internalRoute.Load(head.dst)
for i := 0; i < n; i++ { if !ok || v == nil {
if readBuffSize[i] > ReadTunBuffSize { if isBroadcastOrMulticast(head.dst, s.subnet) {
gLog.Printf(LvERROR, "read tun overflow: len=", readBuffSize[i]) gLog.Printf(LvDev, "multicast ip=%s", net.IP{byte(head.dst >> 24), byte(head.dst >> 16), byte(head.dst >> 8), byte(head.dst)}.String())
continue GNetwork.WriteBroadcast(p)
} }
parseHeader(readBuff[i][PIHeaderSize:readBuffSize[i]+PIHeaderSize], &ih) return
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]) } else {
s.routeTunPacket(readBuff[i][PIHeaderSize:readBuffSize[i]+PIHeaderSize], &ih) node = v.(*sdwanNode)
} }
}
} err := GNetwork.WriteNode(node.id, p)
if err != nil {
func (s *p2pSDWAN) StartTun() error { gLog.Printf(LvDev, "write packet to %s fail: %s", node.name, err)
sdwan := gConf.getSDWAN() }
if s.tun == nil { }
tun := &optun{}
err := tun.Start(s.virtualIP.String(), &sdwan) func (s *p2pSDWAN) readTunLoop() {
if err != nil { gLog.Printf(LvDEBUG, "sdwan readTunLoop start")
gLog.Println(LvERROR, "open tun fail:", err) defer gLog.Printf(LvDEBUG, "sdwan readTunLoop end")
return err readBuff := make([][]byte, ReadTunBuffNum)
} for i := 0; i < ReadTunBuffNum; i++ {
s.tun = tun readBuff[i] = make([]byte, ReadTunBuffSize+PIHeaderSize)
go s.readTunLoop() }
go s.readNodeLoop() // multi-thread read will cause packets out of order, resulting in slower speeds readBuffSize := make([]int, ReadTunBuffNum)
} ih := PacketHeader{}
err := setTunAddr(s.tun.tunName, s.virtualIP.String(), sdwan.Gateway, s.tun.dev) for {
if err != nil { n, err := s.tun.Read(readBuff, readBuffSize, PIHeaderSize)
gLog.Printf(LvERROR, "setTunAddr error:%s,%s,%s,%s", err, s.tun.tunName, s.virtualIP.String(), sdwan.Gateway) if err != nil {
return err gLog.Printf(LvERROR, "read tun fail: ", err)
} return
return nil }
} for i := 0; i < n; i++ {
if readBuffSize[i] > ReadTunBuffSize {
func handleSDWAN(subType uint16, msg []byte) error { gLog.Printf(LvERROR, "read tun overflow: len=", readBuffSize[i])
gLog.Printf(LvDEBUG, "handle sdwan msg type:%d", subType) continue
var err error }
switch subType { parseHeader(readBuff[i][PIHeaderSize:readBuffSize[i]+PIHeaderSize], &ih)
case MsgSDWANInfoRsp: 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])
rsp := SDWANInfo{} s.routeTunPacket(readBuff[i][PIHeaderSize:readBuffSize[i]+PIHeaderSize], &ih)
if err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp); err != nil { }
return ErrMsgFormat }
} }
gLog.Println(LvINFO, "sdwan init:", prettyJson(rsp))
if runtime.GOOS == "android" { func (s *p2pSDWAN) StartTun() error {
AndroidSDWANConfig <- msg[openP2PHeaderSize:] sdwan := gConf.getSDWAN()
} if s.tun == nil {
// GNetwork.sdwan.detail = &rsp tun := &optun{}
gConf.setSDWAN(rsp) err := tun.Start(s.virtualIP.String(), &sdwan)
err = GNetwork.sdwan.init(gConf.Network.Node) if err != nil {
if err != nil { gLog.Println(LvERROR, "open tun fail:", err)
gLog.Println(LvERROR, "sdwan init fail: ", err) return err
if GNetwork.sdwan.tun != nil { }
GNetwork.sdwan.tun.Stop() s.tun = tun
GNetwork.sdwan.tun = nil go s.readTunLoop()
return err 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)
go GNetwork.sdwan.run() if err != nil {
default: gLog.Printf(LvERROR, "setTunAddr error:%s,%s,%s,%s", err, s.tun.tunName, s.virtualIP.String(), sdwan.Gateway)
} return err
return err }
} return nil
}
func handleSDWAN(subType uint16, msg []byte) error {
gLog.Printf(LvDEBUG, "handle sdwan msg type:%d", subType)
var err error
switch subType {
case MsgSDWANInfoRsp:
rsp := SDWANInfo{}
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:]
}
// GNetwork.sdwan.detail = &rsp
gConf.setSDWAN(rsp)
err = GNetwork.sdwan.init(gConf.Network.Node)
if err != nil {
gLog.Println(LvERROR, "sdwan init fail: ", err)
if GNetwork.sdwan.tun != nil {
GNetwork.sdwan.tun.Stop()
GNetwork.sdwan.tun = nil
return err
}
}
go GNetwork.sdwan.run()
default:
}
return err
}
+1 -1
View File
@@ -27,7 +27,7 @@ func DefaultReadBuffer(ul underlay) (*openP2PHeader, []byte, error) {
return nil, nil, err return nil, nil, err
} }
head, err := decodeHeader(headBuf) head, err := decodeHeader(headBuf)
if err != nil { if err != nil || head.MainType > 16 {
return nil, nil, err return nil, nil, err
} }
dataBuf := make([]byte, head.DataLen) dataBuf := make([]byte, head.DataLen)
+2 -2
View File
@@ -51,7 +51,7 @@ func listenTCP(host string, port int, localPort int, mode string, t *P2PTunnel)
if compareVersion(t.config.peerVersion, SyncServerTimeVersion) < 0 { if compareVersion(t.config.peerVersion, SyncServerTimeVersion) < 0 {
gLog.Printf(LvDEBUG, "peer version %s less than %s", t.config.peerVersion, SyncServerTimeVersion) gLog.Printf(LvDEBUG, "peer version %s less than %s", t.config.peerVersion, SyncServerTimeVersion)
} else { } else {
ts := time.Duration(int64(t.punchTs) + t.pn.dt - time.Now().UnixNano()) ts := time.Duration(int64(t.punchTs) + GNetwork.dt - time.Now().UnixNano())
gLog.Printf(LvDEBUG, "sleep %d ms", ts/time.Millisecond) gLog.Printf(LvDEBUG, "sleep %d ms", ts/time.Millisecond)
time.Sleep(ts) time.Sleep(ts)
} }
@@ -72,7 +72,7 @@ func listenTCP(host string, port int, localPort int, mode string, t *P2PTunnel)
utcp.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, buff) utcp.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, buff)
return utcp, nil return utcp, nil
} }
t.pn.push(t.config.PeerNode, MsgPushUnderlayConnect, nil) GNetwork.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
tid := t.id tid := t.id
if compareVersion(t.config.peerVersion, PublicIPVersion) < 0 { // old version if compareVersion(t.config.peerVersion, PublicIPVersion) < 0 { // old version
ipBytes := net.ParseIP(t.config.peerIP).To4() ipBytes := net.ParseIP(t.config.peerIP).To4()
+239 -239
View File
@@ -1,239 +1,239 @@
package openp2p package openp2p
import ( import (
"archive/tar" "archive/tar"
"archive/zip" "archive/zip"
"compress/gzip" "compress/gzip"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"time" "time"
) )
func update(host string, port int) error { func update(host string, port int) error {
gLog.Println(LvINFO, "update start") gLog.Println(LvINFO, "update start")
defer gLog.Println(LvINFO, "update end") defer gLog.Println(LvINFO, "update end")
caCertPool, err := x509.SystemCertPool() caCertPool, err := x509.SystemCertPool()
if err != nil { if err != nil {
gLog.Println(LvERROR, "Failed to load system root CAs:", err) gLog.Println(LvERROR, "Failed to load system root CAs:", err)
} else { } else {
caCertPool = x509.NewCertPool() caCertPool = x509.NewCertPool()
} }
caCertPool.AppendCertsFromPEM([]byte(rootCA)) caCertPool.AppendCertsFromPEM([]byte(rootCA))
caCertPool.AppendCertsFromPEM([]byte(ISRGRootX1)) caCertPool.AppendCertsFromPEM([]byte(ISRGRootX1))
c := http.Client{ c := http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
TLSClientConfig: &tls.Config{RootCAs: caCertPool, TLSClientConfig: &tls.Config{RootCAs: caCertPool,
InsecureSkipVerify: false}, InsecureSkipVerify: false},
}, },
Timeout: time.Second * 30, Timeout: time.Second * 30,
} }
goos := runtime.GOOS goos := runtime.GOOS
goarch := runtime.GOARCH 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))) 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 { if err != nil {
gLog.Println(LvERROR, "update:query update list failed:", err) gLog.Println(LvERROR, "update:query update list failed:", err)
return err return err
} }
defer rsp.Body.Close() defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK { if rsp.StatusCode != http.StatusOK {
gLog.Println(LvERROR, "get update info error:", rsp.Status) gLog.Println(LvERROR, "get update info error:", rsp.Status)
return err return err
} }
rspBuf, err := ioutil.ReadAll(rsp.Body) rspBuf, err := ioutil.ReadAll(rsp.Body)
if err != nil { if err != nil {
gLog.Println(LvERROR, "update:read update list failed:", err) gLog.Println(LvERROR, "update:read update list failed:", err)
return err return err
} }
updateInfo := UpdateInfo{} updateInfo := UpdateInfo{}
if err = json.Unmarshal(rspBuf, &updateInfo); err != nil { if err = json.Unmarshal(rspBuf, &updateInfo); err != nil {
gLog.Println(LvERROR, rspBuf, " update info decode error:", err) gLog.Println(LvERROR, rspBuf, " update info decode error:", err)
return err return err
} }
if updateInfo.Error != 0 { if updateInfo.Error != 0 {
gLog.Println(LvERROR, "update error:", updateInfo.Error, updateInfo.ErrorDetail) gLog.Println(LvERROR, "update error:", updateInfo.Error, updateInfo.ErrorDetail)
return err return err
} }
err = updateFile(updateInfo.Url, "", "openp2p") err = updateFile(updateInfo.Url, "", "openp2p")
if err != nil { if err != nil {
gLog.Println(LvERROR, "update: download failed:", err) gLog.Println(LvERROR, "update: download failed:", err)
return err return err
} }
return nil return nil
} }
func downloadFile(url string, checksum string, dstFile string) 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) output, err := os.OpenFile(dstFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0776)
if err != nil { if err != nil {
gLog.Printf(LvERROR, "OpenFile %s error:%s", dstFile, err) gLog.Printf(LvERROR, "OpenFile %s error:%s", dstFile, err)
return err return err
} }
caCertPool, err := x509.SystemCertPool() caCertPool, err := x509.SystemCertPool()
if err != nil { if err != nil {
gLog.Println(LvERROR, "Failed to load system root CAs:", err) gLog.Println(LvERROR, "Failed to load system root CAs:", err)
} else { } else {
caCertPool = x509.NewCertPool() caCertPool = x509.NewCertPool()
} }
caCertPool.AppendCertsFromPEM([]byte(rootCA)) caCertPool.AppendCertsFromPEM([]byte(rootCA))
caCertPool.AppendCertsFromPEM([]byte(ISRGRootX1)) caCertPool.AppendCertsFromPEM([]byte(ISRGRootX1))
tr := &http.Transport{ tr := &http.Transport{
TLSClientConfig: &tls.Config{ TLSClientConfig: &tls.Config{
RootCAs: caCertPool, RootCAs: caCertPool,
InsecureSkipVerify: false}, InsecureSkipVerify: false},
} }
client := &http.Client{Transport: tr} client := &http.Client{Transport: tr}
response, err := client.Get(url) response, err := client.Get(url)
if err != nil { if err != nil {
gLog.Printf(LvERROR, "download url %s error:%s", url, err) gLog.Printf(LvERROR, "download url %s error:%s", url, err)
output.Close() output.Close()
return err return err
} }
defer response.Body.Close() defer response.Body.Close()
n, err := io.Copy(output, response.Body) n, err := io.Copy(output, response.Body)
if err != nil { if err != nil {
gLog.Printf(LvERROR, "io.Copy error:%s", err) gLog.Printf(LvERROR, "io.Copy error:%s", err)
output.Close() output.Close()
return err return err
} }
output.Sync() output.Sync()
output.Close() output.Close()
gLog.Println(LvINFO, "download ", url, " ok") gLog.Println(LvINFO, "download ", url, " ok")
gLog.Printf(LvINFO, "size: %d bytes", n) gLog.Printf(LvINFO, "size: %d bytes", n)
return nil return nil
} }
func updateFile(url string, checksum string, dst string) error { func updateFile(url string, checksum string, dst string) error {
gLog.Println(LvINFO, "download ", url) gLog.Println(LvINFO, "download ", url)
tmpFile := filepath.Dir(os.Args[0]) + "/openp2p.tmp" tmpFile := filepath.Dir(os.Args[0]) + "/openp2p.tmp"
err := downloadFile(url, checksum, tmpFile) err := downloadFile(url, checksum, tmpFile)
if err != nil { if err != nil {
return err return err
} }
backupFile := os.Args[0] + "0" 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 err = os.Rename(os.Args[0], backupFile) // the old daemon process was using the 0 file, so it will prevent override it
if err != nil { if err != nil {
gLog.Printf(LvINFO, " rename %s error:%s, retry 1", os.Args[0], err) gLog.Printf(LvINFO, " rename %s error:%s, retry 1", os.Args[0], err)
backupFile = os.Args[0] + "1" backupFile = os.Args[0] + "1"
err = os.Rename(os.Args[0], backupFile) err = os.Rename(os.Args[0], backupFile)
if err != nil { if err != nil {
gLog.Printf(LvINFO, " rename %s error:%s", os.Args[0], err) gLog.Printf(LvINFO, " rename %s error:%s", os.Args[0], err)
} }
} }
// extract // extract
gLog.Println(LvINFO, "extract files") gLog.Println(LvINFO, "extract files")
err = extract(filepath.Dir(os.Args[0]), tmpFile) err = extract(filepath.Dir(os.Args[0]), tmpFile)
if err != nil { if err != nil {
gLog.Printf(LvERROR, "extract error:%s. revert rename", err) gLog.Printf(LvERROR, "extract error:%s. revert rename", err)
os.Rename(backupFile, os.Args[0]) os.Rename(backupFile, os.Args[0])
return err return err
} }
os.Remove(tmpFile) os.Remove(tmpFile)
return nil return nil
} }
func extract(dst, src string) (err error) { func extract(dst, src string) (err error) {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
return unzip(dst, src) return unzip(dst, src)
} else { } else {
return extractTgz(dst, src) return extractTgz(dst, src)
} }
} }
func unzip(dst, src string) (err error) { func unzip(dst, src string) (err error) {
archive, err := zip.OpenReader(src) archive, err := zip.OpenReader(src)
if err != nil { if err != nil {
return err return err
} }
defer archive.Close() defer archive.Close()
for _, f := range archive.File { for _, f := range archive.File {
filePath := filepath.Join(dst, f.Name) filePath := filepath.Join(dst, f.Name)
fmt.Println("unzipping file ", filePath) fmt.Println("unzipping file ", filePath)
if f.FileInfo().IsDir() { if f.FileInfo().IsDir() {
fmt.Println("creating directory...") fmt.Println("creating directory...")
os.MkdirAll(filePath, os.ModePerm) os.MkdirAll(filePath, os.ModePerm)
continue continue
} }
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
return err return err
} }
dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil { if err != nil {
return err return err
} }
fileInArchive, err := f.Open() fileInArchive, err := f.Open()
if err != nil { if err != nil {
return err return err
} }
if _, err := io.Copy(dstFile, fileInArchive); err != nil { if _, err := io.Copy(dstFile, fileInArchive); err != nil {
return err return err
} }
dstFile.Close() dstFile.Close()
fileInArchive.Close() fileInArchive.Close()
} }
return nil return nil
} }
func extractTgz(dst, src string) error { func extractTgz(dst, src string) error {
gzipStream, err := os.Open(src) gzipStream, err := os.Open(src)
if err != nil { if err != nil {
return err return err
} }
uncompressedStream, err := gzip.NewReader(gzipStream) uncompressedStream, err := gzip.NewReader(gzipStream)
if err != nil { if err != nil {
return err return err
} }
tarReader := tar.NewReader(uncompressedStream) tarReader := tar.NewReader(uncompressedStream)
for { for {
header, err := tarReader.Next() header, err := tarReader.Next()
if err == io.EOF { if err == io.EOF {
break break
} }
if err != nil { if err != nil {
return err return err
} }
switch header.Typeflag { switch header.Typeflag {
case tar.TypeDir: case tar.TypeDir:
if err := os.Mkdir(header.Name, 0755); err != nil { if err := os.Mkdir(header.Name, 0755); err != nil {
return err return err
} }
case tar.TypeReg: case tar.TypeReg:
filePath := filepath.Join(dst, header.Name) filePath := filepath.Join(dst, header.Name)
outFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(header.Mode)) outFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(header.Mode))
if err != nil { if err != nil {
return err return err
} }
defer outFile.Close() defer outFile.Close()
if _, err := io.Copy(outFile, tarReader); err != nil { if _, err := io.Copy(outFile, tarReader); err != nil {
return err return err
} }
default: default:
return err return err
} }
} }
return nil return nil
} }
func cleanTempFiles() { func cleanTempFiles() {
tmpFile := os.Args[0] + "0" tmpFile := os.Args[0] + "0"
if _, err := os.Stat(tmpFile); err == nil { if _, err := os.Stat(tmpFile); err == nil {
if err := os.Remove(tmpFile); err != nil { if err := os.Remove(tmpFile); err != nil {
gLog.Printf(LvDEBUG, " remove %s error:%s", tmpFile, err) gLog.Printf(LvDEBUG, " remove %s error:%s", tmpFile, err)
} }
} }
tmpFile = os.Args[0] + "1" tmpFile = os.Args[0] + "1"
if _, err := os.Stat(tmpFile); err == nil { if _, err := os.Stat(tmpFile); err == nil {
if err := os.Remove(tmpFile); err != nil { if err := os.Remove(tmpFile); err != nil {
gLog.Printf(LvDEBUG, " remove %s error:%s", tmpFile, err) gLog.Printf(LvDEBUG, " remove %s error:%s", tmpFile, err)
} }
} }
} }
+1 -1
View File
@@ -47,7 +47,7 @@ func (vl *v4Listener) handleConnection(c net.Conn) {
utcp.SetReadDeadline(time.Now().Add(UnderlayTCPConnectTimeout)) utcp.SetReadDeadline(time.Now().Add(UnderlayTCPConnectTimeout))
_, buff, err := utcp.ReadBuffer() _, buff, err := utcp.ReadBuffer()
if err != nil { if err != nil {
gLog.Printf(LvERROR, "utcp.ReadBuffer error:", err) gLog.Println(LvERROR, "utcp.ReadBuffer error:", err)
} }
utcp.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, buff) utcp.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, buff)
var tid uint64 var tid uint64
+11 -12
View File
@@ -8,17 +8,16 @@ require (
github.com/openp2p-cn/go-reuseport v0.3.2 github.com/openp2p-cn/go-reuseport v0.3.2
github.com/openp2p-cn/service v1.0.0 github.com/openp2p-cn/service v1.0.0
github.com/openp2p-cn/totp v0.0.0-20230421034602-0f3320ffb25e github.com/openp2p-cn/totp v0.0.0-20230421034602-0f3320ffb25e
github.com/openp2p-cn/wireguard-go v0.0.20240223
github.com/quic-go/quic-go v0.34.0 github.com/quic-go/quic-go v0.34.0
github.com/vishvananda/netlink v1.1.0 github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54
github.com/xtaci/kcp-go/v5 v5.5.17 github.com/xtaci/kcp-go/v5 v5.5.17
golang.org/x/sys v0.21.0 golang.org/x/sys v0.26.0
golang.zx2c4.com/wireguard/windows v0.5.3 golang.zx2c4.com/wireguard/windows v0.5.3
) )
require ( require (
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/mock v1.6.0 // indirect github.com/golang/mock v1.7.0-rc.1 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/kardianos/service v1.2.2 // indirect github.com/kardianos/service v1.2.2 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect
@@ -30,13 +29,13 @@ require (
github.com/templexxx/cpu v0.1.0 // indirect github.com/templexxx/cpu v0.1.0 // indirect
github.com/templexxx/xorsimd v0.4.2 // indirect github.com/templexxx/xorsimd v0.4.2 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
golang.org/x/crypto v0.24.0 // indirect golang.org/x/crypto v0.28.0 // indirect
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect
golang.org/x/mod v0.18.0 // indirect golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.26.0 // indirect golang.org/x/net v0.30.0 // indirect
golang.org/x/tools v0.22.0 // indirect golang.org/x/tools v0.26.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect google.golang.org/protobuf v1.33.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect gvisor.dev/gvisor v0.0.0-20241128011400-745828301c93 // indirect
) )
+18 -14
View File
@@ -1,14 +1,18 @@
package main package main
import ( // On Windows env
op "openp2p/core" // cd lib
) // go build -o openp2p.dll -buildmode=c-shared openp2p.go
import "C" // caller example see example/dll
import (
func main() { op "openp2p/core"
} )
import "C"
//export RunCmd
func RunCmd(cmd *C.char) { func main() {
op.RunCmd(C.GoString(cmd)) }
}
//export RunCmd
func RunCmd(cmd *C.char) {
op.RunCmd(C.GoString(cmd))
}