This commit is contained in:
TenderIronh
2024-07-26 22:27:24 +08:00
parent 2dea3a718d
commit 9c3d557f5d
18 changed files with 4041 additions and 4041 deletions
+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
``` ```
+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()
} }
+115 -115
View File
@@ -1,115 +1,115 @@
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") args = append(args, "-nv")
for { for {
// start worker // start worker
tmpDump := filepath.Join("log", "dump.log.tmp") tmpDump := filepath.Join("log", "dump.log.tmp")
dumpFile := filepath.Join("log", "dump.log") dumpFile := filepath.Join("log", "dump.log")
f, err := os.Create(filepath.Join(tmpDump)) f, err := os.Create(filepath.Join(tmpDump))
if err != nil { if err != nil {
gLog.Printf(LvERROR, "start worker error:%s", err) gLog.Printf(LvERROR, "start worker error:%s", err)
return return
} }
gLog.Println(LvINFO, "start worker process, args:", args) 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}} execSpec := &os.ProcAttr{Env: append(os.Environ(), "GOTRACEBACK=crash"), Files: []*os.File{os.Stdin, os.Stdout, f}}
p, err := os.StartProcess(binPath, args, execSpec) p, err := os.StartProcess(binPath, args, execSpec)
if err != nil { if err != nil {
gLog.Printf(LvERROR, "start worker error:%s", err) gLog.Printf(LvERROR, "start worker error:%s", err)
return return
} }
d.proc = p d.proc = p
_, _ = p.Wait() _, _ = p.Wait()
f.Close() f.Close()
time.Sleep(time.Second) time.Sleep(time.Second)
err = os.Rename(tmpDump, dumpFile) err = os.Rename(tmpDump, dumpFile)
if err != nil { if err != nil {
gLog.Printf(LvERROR, "rename dump error:%s", err) gLog.Printf(LvERROR, "rename dump error:%s", err)
} }
if !d.running { if !d.running {
return return
} }
gLog.Printf(LvERROR, "worker stop, restart it after 10s") gLog.Printf(LvERROR, "worker stop, restart it after 10s")
time.Sleep(time.Second * 10) time.Sleep(time.Second * 10)
} }
} }
func (d *daemon) Control(ctrlComm string, exeAbsPath string, args []string) error { func (d *daemon) Control(ctrlComm string, exeAbsPath string, args []string) error {
svcConfig := &service.Config{ svcConfig := &service.Config{
Name: ProductName, Name: ProductName,
DisplayName: ProductName, DisplayName: ProductName,
Description: ProductName, Description: ProductName,
Executable: exeAbsPath, Executable: exeAbsPath,
Arguments: args, Arguments: args,
} }
s, e := service.New(d, svcConfig) s, e := service.New(d, svcConfig)
if e != nil { if e != nil {
return e return e
} }
e = service.Control(s, ctrlComm) e = service.Control(s, ctrlComm)
if e != nil { if e != nil {
return e return e
} }
return nil return nil
} }
+482 -482
View File
@@ -1,482 +1,482 @@
package openp2p package openp2p
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net" "net"
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"runtime" "runtime"
"time" "time"
"github.com/openp2p-cn/totp" "github.com/openp2p-cn/totp"
) )
func handlePush(subType uint16, msg []byte) error { func handlePush(subType uint16, msg []byte) error {
pushHead := PushHeader{} pushHead := PushHeader{}
err := binary.Read(bytes.NewReader(msg[openP2PHeaderSize:openP2PHeaderSize+PushHeaderSize]), binary.LittleEndian, &pushHead) err := binary.Read(bytes.NewReader(msg[openP2PHeaderSize:openP2PHeaderSize+PushHeaderSize]), binary.LittleEndian, &pushHead)
if err != nil { if err != nil {
return err return err
} }
gLog.Printf(LvDEBUG, "handle push msg type:%d, push header:%+v", subType, pushHead) gLog.Printf(LvDEBUG, "handle push msg type:%d, push header:%+v", subType, pushHead)
switch subType { switch subType {
case MsgPushConnectReq: case MsgPushConnectReq:
err = handleConnectReq(msg) err = handleConnectReq(msg)
case MsgPushRsp: case MsgPushRsp:
rsp := PushRsp{} rsp := PushRsp{}
if err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp); err != nil { if err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp); err != nil {
gLog.Printf(LvERROR, "wrong pushRsp:%s", err) gLog.Printf(LvERROR, "wrong pushRsp:%s", err)
return err return err
} }
if rsp.Error == 0 { if rsp.Error == 0 {
gLog.Printf(LvDEBUG, "push ok, detail:%s", rsp.Detail) gLog.Printf(LvDEBUG, "push ok, detail:%s", rsp.Detail)
} else { } else {
gLog.Printf(LvERROR, "push error:%d, detail:%s", rsp.Error, rsp.Detail) gLog.Printf(LvERROR, "push error:%d, detail:%s", rsp.Error, rsp.Detail)
} }
case MsgPushAddRelayTunnelReq: case MsgPushAddRelayTunnelReq:
req := AddRelayTunnelReq{} req := AddRelayTunnelReq{}
if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil { if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil {
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err) gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
return err return err
} }
config := AppConfig{} config := AppConfig{}
config.PeerNode = req.RelayName config.PeerNode = req.RelayName
config.peerToken = req.RelayToken config.peerToken = req.RelayToken
config.relayMode = req.RelayMode config.relayMode = req.RelayMode
go func(r AddRelayTunnelReq) { go func(r AddRelayTunnelReq) {
t, errDt := GNetwork.addDirectTunnel(config, 0) t, errDt := GNetwork.addDirectTunnel(config, 0)
if errDt == nil { if errDt == nil {
// notify peer relay ready // notify peer relay ready
msg := TunnelMsg{ID: t.id} msg := TunnelMsg{ID: t.id}
GNetwork.push(r.From, MsgPushAddRelayTunnelRsp, msg) GNetwork.push(r.From, MsgPushAddRelayTunnelRsp, msg)
appConfig := config appConfig := config
appConfig.PeerNode = req.From appConfig.PeerNode = req.From
} else { } else {
gLog.Printf(LvERROR, "addDirectTunnel error:%s", errDt) gLog.Printf(LvERROR, "addDirectTunnel error:%s", errDt)
GNetwork.push(r.From, MsgPushAddRelayTunnelRsp, "error") // compatible with old version client, trigger unmarshal error GNetwork.push(r.From, MsgPushAddRelayTunnelRsp, "error") // compatible with old version client, trigger unmarshal error
} }
}(req) }(req)
case MsgPushServerSideSaveMemApp: case MsgPushServerSideSaveMemApp:
req := ServerSideSaveMemApp{} req := ServerSideSaveMemApp{}
if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil { if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil {
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err) gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
return err return err
} }
gLog.Println(LvDEBUG, "handle MsgPushServerSideSaveMemApp:", prettyJson(req)) gLog.Println(LvDEBUG, "handle MsgPushServerSideSaveMemApp:", prettyJson(req))
var existTunnel *P2PTunnel var existTunnel *P2PTunnel
i, ok := GNetwork.allTunnels.Load(req.TunnelID) i, ok := GNetwork.allTunnels.Load(req.TunnelID)
if !ok { if !ok {
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
i, ok = GNetwork.allTunnels.Load(req.TunnelID) // retry sometimes will receive MsgPushServerSideSaveMemApp but p2ptunnel not store yet. i, ok = GNetwork.allTunnels.Load(req.TunnelID) // retry sometimes will receive MsgPushServerSideSaveMemApp but p2ptunnel not store yet.
if !ok { if !ok {
gLog.Println(LvERROR, "handle MsgPushServerSideSaveMemApp error:", ErrMemAppTunnelNotFound) gLog.Println(LvERROR, "handle MsgPushServerSideSaveMemApp error:", ErrMemAppTunnelNotFound)
return ErrMemAppTunnelNotFound return ErrMemAppTunnelNotFound
} }
} }
existTunnel = i.(*P2PTunnel) existTunnel = i.(*P2PTunnel)
peerID := NodeNameToID(req.From) peerID := NodeNameToID(req.From)
existApp, appok := GNetwork.apps.Load(peerID) existApp, appok := GNetwork.apps.Load(peerID)
if appok { if appok {
app := existApp.(*p2pApp) app := existApp.(*p2pApp)
app.config.AppName = fmt.Sprintf("%d", peerID) app.config.AppName = fmt.Sprintf("%d", peerID)
app.id = req.AppID app.id = req.AppID
app.setRelayTunnelID(req.RelayTunnelID) app.setRelayTunnelID(req.RelayTunnelID)
app.relayMode = req.RelayMode app.relayMode = req.RelayMode
app.hbTimeRelay = time.Now() app.hbTimeRelay = time.Now()
if req.RelayTunnelID == 0 { if req.RelayTunnelID == 0 {
app.setDirectTunnel(existTunnel) app.setDirectTunnel(existTunnel)
} else { } else {
app.setRelayTunnel(existTunnel) app.setRelayTunnel(existTunnel)
} }
gLog.Println(LvDEBUG, "find existing memapp, update it") gLog.Println(LvDEBUG, "find existing memapp, update it")
} else { } else {
appConfig := existTunnel.config appConfig := existTunnel.config
appConfig.SrcPort = 0 appConfig.SrcPort = 0
appConfig.Protocol = "" appConfig.Protocol = ""
appConfig.AppName = fmt.Sprintf("%d", peerID) appConfig.AppName = fmt.Sprintf("%d", peerID)
appConfig.PeerNode = req.From appConfig.PeerNode = req.From
app := p2pApp{ app := p2pApp{
id: req.AppID, id: req.AppID,
config: appConfig, config: appConfig,
relayMode: req.RelayMode, relayMode: req.RelayMode,
running: true, running: true,
hbTimeRelay: time.Now(), hbTimeRelay: time.Now(),
} }
if req.RelayTunnelID == 0 { if req.RelayTunnelID == 0 {
app.setDirectTunnel(existTunnel) app.setDirectTunnel(existTunnel)
} else { } else {
app.setRelayTunnel(existTunnel) app.setRelayTunnel(existTunnel)
app.setRelayTunnelID(req.RelayTunnelID) app.setRelayTunnelID(req.RelayTunnelID)
} }
if req.RelayTunnelID != 0 { if req.RelayTunnelID != 0 {
app.relayNode = req.Node app.relayNode = req.Node
} }
GNetwork.apps.Store(NodeNameToID(req.From), &app) GNetwork.apps.Store(NodeNameToID(req.From), &app)
} }
return nil return nil
case MsgPushAPPKey: case MsgPushAPPKey:
req := APPKeySync{} req := APPKeySync{}
if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil { if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil {
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err) gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
return err return err
} }
SaveKey(req.AppID, req.AppKey) SaveKey(req.AppID, req.AppKey)
case MsgPushUpdate: case MsgPushUpdate:
gLog.Println(LvINFO, "MsgPushUpdate") gLog.Println(LvINFO, "MsgPushUpdate")
err := update(gConf.Network.ServerHost, gConf.Network.ServerPort) err := update(gConf.Network.ServerHost, gConf.Network.ServerPort)
if err == nil { if err == nil {
os.Exit(0) os.Exit(0)
} }
return err return err
case MsgPushRestart: case MsgPushRestart:
gLog.Println(LvINFO, "MsgPushRestart") gLog.Println(LvINFO, "MsgPushRestart")
os.Exit(0) os.Exit(0)
return err return err
case MsgPushReportApps: case MsgPushReportApps:
err = handleReportApps() err = handleReportApps()
case MsgPushReportMemApps: case MsgPushReportMemApps:
err = handleReportMemApps() err = handleReportMemApps()
case MsgPushReportLog: case MsgPushReportLog:
err = handleLog(msg) err = handleLog(msg)
case MsgPushReportGoroutine: case MsgPushReportGoroutine:
err = handleReportGoroutine() err = handleReportGoroutine()
case MsgPushCheckRemoteService: case MsgPushCheckRemoteService:
err = handleCheckRemoteService(msg) err = handleCheckRemoteService(msg)
case MsgPushEditApp: case MsgPushEditApp:
err = handleEditApp(msg) err = handleEditApp(msg)
case MsgPushEditNode: case MsgPushEditNode:
gLog.Println(LvINFO, "MsgPushEditNode") gLog.Println(LvINFO, "MsgPushEditNode")
req := EditNode{} req := EditNode{}
if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil { if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil {
gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:])) gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:]))
return err return err
} }
gConf.setNode(req.NewName) gConf.setNode(req.NewName)
gConf.setShareBandwidth(req.Bandwidth) gConf.setShareBandwidth(req.Bandwidth)
os.Exit(0) os.Exit(0)
case MsgPushSwitchApp: case MsgPushSwitchApp:
gLog.Println(LvINFO, "MsgPushSwitchApp") gLog.Println(LvINFO, "MsgPushSwitchApp")
app := AppInfo{} app := AppInfo{}
if err = json.Unmarshal(msg[openP2PHeaderSize:], &app); err != nil { if err = json.Unmarshal(msg[openP2PHeaderSize:], &app); err != nil {
gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(app), err, string(msg[openP2PHeaderSize:])) gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(app), err, string(msg[openP2PHeaderSize:]))
return err return err
} }
config := AppConfig{Enabled: app.Enabled, SrcPort: app.SrcPort, Protocol: app.Protocol} config := AppConfig{Enabled: app.Enabled, SrcPort: app.SrcPort, Protocol: app.Protocol}
gLog.Println(LvINFO, app.AppName, " switch to ", app.Enabled) gLog.Println(LvINFO, app.AppName, " switch to ", app.Enabled)
gConf.switchApp(config, app.Enabled) gConf.switchApp(config, app.Enabled)
if app.Enabled == 0 { if app.Enabled == 0 {
// disable APP // disable APP
GNetwork.DeleteApp(config) GNetwork.DeleteApp(config)
} }
case MsgPushDstNodeOnline: case MsgPushDstNodeOnline:
gLog.Println(LvINFO, "MsgPushDstNodeOnline") gLog.Println(LvINFO, "MsgPushDstNodeOnline")
req := PushDstNodeOnline{} req := PushDstNodeOnline{}
if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil { if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil {
gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:])) gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:]))
return err return err
} }
gLog.Println(LvINFO, "retry peerNode ", req.Node) gLog.Println(LvINFO, "retry peerNode ", req.Node)
gConf.retryApp(req.Node) gConf.retryApp(req.Node)
default: default:
i, ok := GNetwork.msgMap.Load(pushHead.From) i, ok := GNetwork.msgMap.Load(pushHead.From)
if !ok { if !ok {
return ErrMsgChannelNotFound return ErrMsgChannelNotFound
} }
ch := i.(chan msgCtx) ch := i.(chan msgCtx)
ch <- msgCtx{data: msg, ts: time.Now()} ch <- msgCtx{data: msg, ts: time.Now()}
} }
return err return err
} }
func handleEditApp(msg []byte) (err error) { func handleEditApp(msg []byte) (err error) {
gLog.Println(LvINFO, "MsgPushEditApp") gLog.Println(LvINFO, "MsgPushEditApp")
newApp := AppInfo{} newApp := AppInfo{}
if err = json.Unmarshal(msg[openP2PHeaderSize:], &newApp); err != nil { if err = json.Unmarshal(msg[openP2PHeaderSize:], &newApp); err != nil {
gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(newApp), err, string(msg[openP2PHeaderSize:])) gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(newApp), err, string(msg[openP2PHeaderSize:]))
return err return err
} }
oldConf := AppConfig{Enabled: 1} oldConf := AppConfig{Enabled: 1}
// protocol0+srcPort0 exist, delApp // protocol0+srcPort0 exist, delApp
oldConf.AppName = newApp.AppName oldConf.AppName = newApp.AppName
oldConf.Protocol = newApp.Protocol0 oldConf.Protocol = newApp.Protocol0
oldConf.Whitelist = newApp.Whitelist oldConf.Whitelist = newApp.Whitelist
oldConf.SrcPort = newApp.SrcPort0 oldConf.SrcPort = newApp.SrcPort0
oldConf.PeerNode = newApp.PeerNode oldConf.PeerNode = newApp.PeerNode
oldConf.DstHost = newApp.DstHost oldConf.DstHost = newApp.DstHost
oldConf.DstPort = newApp.DstPort oldConf.DstPort = newApp.DstPort
if newApp.Protocol0 != "" && newApp.SrcPort0 != 0 { // not edit if newApp.Protocol0 != "" && newApp.SrcPort0 != 0 { // not edit
gConf.delete(oldConf) gConf.delete(oldConf)
} }
// AddApp // AddApp
newConf := oldConf newConf := oldConf
newConf.Protocol = newApp.Protocol newConf.Protocol = newApp.Protocol
newConf.SrcPort = newApp.SrcPort newConf.SrcPort = newApp.SrcPort
newConf.RelayNode = newApp.SpecRelayNode newConf.RelayNode = newApp.SpecRelayNode
newConf.PunchPriority = newApp.PunchPriority newConf.PunchPriority = newApp.PunchPriority
gConf.add(newConf, false) gConf.add(newConf, false)
if newApp.Protocol0 != "" && newApp.SrcPort0 != 0 { // not edit if newApp.Protocol0 != "" && newApp.SrcPort0 != 0 { // not edit
GNetwork.DeleteApp(oldConf) // DeleteApp may cost some times, execute at the end GNetwork.DeleteApp(oldConf) // DeleteApp may cost some times, execute at the end
} }
return nil return nil
} }
func handleConnectReq(msg []byte) (err error) { func handleConnectReq(msg []byte) (err error) {
req := PushConnectReq{} req := PushConnectReq{}
if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil { if err = json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req); err != nil {
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err) gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(req), err)
return err return err
} }
gLog.Printf(LvDEBUG, "%s is connecting...", req.From) gLog.Printf(LvDEBUG, "%s is connecting...", req.From)
gLog.Println(LvDEBUG, "push connect response to ", req.From) gLog.Println(LvDEBUG, "push connect response to ", req.From)
if compareVersion(req.Version, LeastSupportVersion) < 0 { if compareVersion(req.Version, LeastSupportVersion) < 0 {
gLog.Println(LvERROR, ErrVersionNotCompatible.Error(), ":", req.From) gLog.Println(LvERROR, ErrVersionNotCompatible.Error(), ":", req.From)
rsp := PushConnectRsp{ rsp := PushConnectRsp{
Error: 10, Error: 10,
Detail: ErrVersionNotCompatible.Error(), Detail: ErrVersionNotCompatible.Error(),
To: req.From, To: req.From,
From: gConf.Network.Node, From: gConf.Network.Node,
} }
GNetwork.push(req.From, MsgPushConnectRsp, rsp) GNetwork.push(req.From, MsgPushConnectRsp, rsp)
return ErrVersionNotCompatible return ErrVersionNotCompatible
} }
// verify totp token or token // verify totp token or token
t := totp.TOTP{Step: totp.RelayTOTPStep} t := totp.TOTP{Step: totp.RelayTOTPStep}
if t.Verify(req.Token, gConf.Network.Token, time.Now().Unix()-GNetwork.dt/int64(time.Second)) { // localTs may behind, auto adjust ts if t.Verify(req.Token, gConf.Network.Token, time.Now().Unix()-GNetwork.dt/int64(time.Second)) { // localTs may behind, auto adjust ts
gLog.Printf(LvINFO, "Access Granted") gLog.Printf(LvINFO, "Access Granted")
config := AppConfig{} config := AppConfig{}
config.peerNatType = req.NatType config.peerNatType = req.NatType
config.peerConeNatPort = req.ConeNatPort config.peerConeNatPort = req.ConeNatPort
config.peerIP = req.FromIP config.peerIP = req.FromIP
config.PeerNode = req.From config.PeerNode = req.From
config.peerVersion = req.Version config.peerVersion = req.Version
config.fromToken = req.Token config.fromToken = req.Token
config.peerIPv6 = req.IPv6 config.peerIPv6 = req.IPv6
config.hasIPv4 = req.HasIPv4 config.hasIPv4 = req.HasIPv4
config.hasUPNPorNATPMP = req.HasUPNPorNATPMP config.hasUPNPorNATPMP = req.HasUPNPorNATPMP
config.linkMode = req.LinkMode config.linkMode = req.LinkMode
config.isUnderlayServer = req.IsUnderlayServer config.isUnderlayServer = req.IsUnderlayServer
config.UnderlayProtocol = req.UnderlayProtocol config.UnderlayProtocol = req.UnderlayProtocol
// share relay node will limit bandwidth // share relay node will limit bandwidth
if req.Token != gConf.Network.Token { if req.Token != gConf.Network.Token {
gLog.Printf(LvINFO, "set share bandwidth %d mbps", gConf.Network.ShareBandwidth) gLog.Printf(LvINFO, "set share bandwidth %d mbps", gConf.Network.ShareBandwidth)
config.shareBandwidth = gConf.Network.ShareBandwidth config.shareBandwidth = gConf.Network.ShareBandwidth
} }
// go GNetwork.AddTunnel(config, req.ID) // go GNetwork.AddTunnel(config, req.ID)
go func() { go func() {
GNetwork.addDirectTunnel(config, req.ID) GNetwork.addDirectTunnel(config, req.ID)
}() }()
return nil return nil
} }
gLog.Println(LvERROR, "Access Denied:", req.From) gLog.Println(LvERROR, "Access Denied:", req.From)
rsp := PushConnectRsp{ rsp := PushConnectRsp{
Error: 1, Error: 1,
Detail: fmt.Sprintf("connect to %s error: Access Denied", gConf.Network.Node), Detail: fmt.Sprintf("connect to %s error: Access Denied", gConf.Network.Node),
To: req.From, To: req.From,
From: gConf.Network.Node, From: gConf.Network.Node,
} }
return GNetwork.push(req.From, MsgPushConnectRsp, rsp) return GNetwork.push(req.From, MsgPushConnectRsp, rsp)
} }
func handleReportApps() (err error) { func handleReportApps() (err error) {
gLog.Println(LvINFO, "MsgPushReportApps") gLog.Println(LvINFO, "MsgPushReportApps")
req := ReportApps{} req := ReportApps{}
gConf.mtx.Lock() gConf.mtx.Lock()
defer gConf.mtx.Unlock() defer gConf.mtx.Unlock()
for _, config := range gConf.Apps { for _, config := range gConf.Apps {
appActive := 0 appActive := 0
relayNode := "" relayNode := ""
specRelayNode := "" specRelayNode := ""
relayMode := "" relayMode := ""
linkMode := LinkModeUDPPunch linkMode := LinkModeUDPPunch
var connectTime string var connectTime string
var retryTime string var retryTime string
var app *p2pApp var app *p2pApp
i, ok := GNetwork.apps.Load(config.ID()) i, ok := GNetwork.apps.Load(config.ID())
if ok { if ok {
app = i.(*p2pApp) app = i.(*p2pApp)
if app.isActive() { if app.isActive() {
appActive = 1 appActive = 1
} }
if app.config.SrcPort == 0 { // memapp if app.config.SrcPort == 0 { // memapp
continue continue
} }
specRelayNode = app.config.RelayNode specRelayNode = app.config.RelayNode
if !app.isDirect() { // TODO: should always report relay node for app edit if !app.isDirect() { // TODO: should always report relay node for app edit
relayNode = app.relayNode relayNode = app.relayNode
relayMode = app.relayMode relayMode = app.relayMode
} }
if app.Tunnel() != nil { if app.Tunnel() != nil {
linkMode = app.Tunnel().linkModeWeb linkMode = app.Tunnel().linkModeWeb
} }
retryTime = app.RetryTime().Local().Format("2006-01-02T15:04:05-0700") retryTime = app.RetryTime().Local().Format("2006-01-02T15:04:05-0700")
connectTime = app.ConnectTime().Local().Format("2006-01-02T15:04:05-0700") connectTime = app.ConnectTime().Local().Format("2006-01-02T15:04:05-0700")
} }
appInfo := AppInfo{ appInfo := AppInfo{
AppName: config.AppName, AppName: config.AppName,
Error: config.errMsg, Error: config.errMsg,
Protocol: config.Protocol, Protocol: config.Protocol,
PunchPriority: config.PunchPriority, PunchPriority: config.PunchPriority,
Whitelist: config.Whitelist, Whitelist: config.Whitelist,
SrcPort: config.SrcPort, SrcPort: config.SrcPort,
RelayNode: relayNode, RelayNode: relayNode,
SpecRelayNode: specRelayNode, SpecRelayNode: specRelayNode,
RelayMode: relayMode, RelayMode: relayMode,
LinkMode: linkMode, LinkMode: linkMode,
PeerNode: config.PeerNode, PeerNode: config.PeerNode,
DstHost: config.DstHost, DstHost: config.DstHost,
DstPort: config.DstPort, DstPort: config.DstPort,
PeerUser: config.PeerUser, PeerUser: config.PeerUser,
PeerIP: config.peerIP, PeerIP: config.peerIP,
PeerNatType: config.peerNatType, PeerNatType: config.peerNatType,
RetryTime: retryTime, RetryTime: retryTime,
ConnectTime: connectTime, ConnectTime: connectTime,
IsActive: appActive, IsActive: appActive,
Enabled: config.Enabled, Enabled: config.Enabled,
} }
req.Apps = append(req.Apps, appInfo) req.Apps = append(req.Apps, appInfo)
} }
return GNetwork.write(MsgReport, MsgReportApps, &req) return GNetwork.write(MsgReport, MsgReportApps, &req)
} }
func handleReportMemApps() (err error) { func handleReportMemApps() (err error) {
gLog.Println(LvINFO, "handleReportMemApps") gLog.Println(LvINFO, "handleReportMemApps")
req := ReportApps{} req := ReportApps{}
gConf.mtx.Lock() gConf.mtx.Lock()
defer gConf.mtx.Unlock() defer gConf.mtx.Unlock()
GNetwork.sdwan.sysRoute.Range(func(key, value interface{}) bool { GNetwork.sdwan.sysRoute.Range(func(key, value interface{}) bool {
node := value.(*sdwanNode) node := value.(*sdwanNode)
appActive := 0 appActive := 0
relayMode := "" relayMode := ""
var connectTime string var connectTime string
var retryTime string var retryTime string
i, ok := GNetwork.apps.Load(node.id) i, ok := GNetwork.apps.Load(node.id)
var app *p2pApp var app *p2pApp
if ok { if ok {
app = i.(*p2pApp) app = i.(*p2pApp)
if app.isActive() { if app.isActive() {
appActive = 1 appActive = 1
} }
if !app.isDirect() { if !app.isDirect() {
relayMode = app.relayMode relayMode = app.relayMode
} }
retryTime = app.RetryTime().Local().Format("2006-01-02T15:04:05-0700") retryTime = app.RetryTime().Local().Format("2006-01-02T15:04:05-0700")
connectTime = app.ConnectTime().Local().Format("2006-01-02T15:04:05-0700") connectTime = app.ConnectTime().Local().Format("2006-01-02T15:04:05-0700")
} }
appInfo := AppInfo{ appInfo := AppInfo{
RelayMode: relayMode, RelayMode: relayMode,
PeerNode: node.name, PeerNode: node.name,
IsActive: appActive, IsActive: appActive,
Enabled: 1, Enabled: 1,
} }
if app != nil { if app != nil {
appInfo.AppName = app.config.AppName appInfo.AppName = app.config.AppName
appInfo.Error = app.config.errMsg appInfo.Error = app.config.errMsg
appInfo.Protocol = app.config.Protocol appInfo.Protocol = app.config.Protocol
appInfo.Whitelist = app.config.Whitelist appInfo.Whitelist = app.config.Whitelist
appInfo.SrcPort = app.config.SrcPort appInfo.SrcPort = app.config.SrcPort
if !app.isDirect() { if !app.isDirect() {
appInfo.RelayNode = app.relayNode appInfo.RelayNode = app.relayNode
} }
if app.Tunnel() != nil { if app.Tunnel() != nil {
appInfo.LinkMode = app.Tunnel().linkModeWeb appInfo.LinkMode = app.Tunnel().linkModeWeb
} }
appInfo.DstHost = app.config.DstHost appInfo.DstHost = app.config.DstHost
appInfo.DstPort = app.config.DstPort appInfo.DstPort = app.config.DstPort
appInfo.PeerUser = app.config.PeerUser appInfo.PeerUser = app.config.PeerUser
appInfo.PeerIP = app.config.peerIP appInfo.PeerIP = app.config.peerIP
appInfo.PeerNatType = app.config.peerNatType appInfo.PeerNatType = app.config.peerNatType
appInfo.RetryTime = retryTime appInfo.RetryTime = retryTime
appInfo.ConnectTime = connectTime appInfo.ConnectTime = connectTime
} }
req.Apps = append(req.Apps, appInfo) req.Apps = append(req.Apps, appInfo)
return true return true
}) })
gLog.Println(LvDEBUG, "handleReportMemApps res:", prettyJson(req)) gLog.Println(LvDEBUG, "handleReportMemApps res:", prettyJson(req))
return GNetwork.write(MsgReport, MsgReportMemApps, &req) return GNetwork.write(MsgReport, MsgReportMemApps, &req)
} }
func handleLog(msg []byte) (err error) { func handleLog(msg []byte) (err error) {
gLog.Println(LvDEBUG, "MsgPushReportLog") gLog.Println(LvDEBUG, "MsgPushReportLog")
const defaultLen = 1024 * 128 const defaultLen = 1024 * 128
const maxLen = 1024 * 1024 const maxLen = 1024 * 1024
req := ReportLogReq{} req := ReportLogReq{}
if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil { if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil {
gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:])) gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:]))
return err return err
} }
if req.FileName == "" { if req.FileName == "" {
req.FileName = "openp2p.log" req.FileName = "openp2p.log"
} else { } else {
req.FileName = sanitizeFileName(req.FileName) req.FileName = sanitizeFileName(req.FileName)
} }
f, err := os.Open(filepath.Join("log", req.FileName)) f, err := os.Open(filepath.Join("log", req.FileName))
if err != nil { if err != nil {
gLog.Println(LvERROR, "read log file error:", err) gLog.Println(LvERROR, "read log file error:", err)
return err return err
} }
fi, err := f.Stat() fi, err := f.Stat()
if err != nil { if err != nil {
return err return err
} }
if req.Offset > fi.Size() { if req.Offset > fi.Size() {
req.Offset = fi.Size() - defaultLen req.Offset = fi.Size() - defaultLen
} }
// verify input parameters // verify input parameters
if req.Offset < 0 { if req.Offset < 0 {
req.Offset = 0 req.Offset = 0
} }
if req.Len <= 0 || req.Len > maxLen { if req.Len <= 0 || req.Len > maxLen {
req.Len = defaultLen req.Len = defaultLen
} }
f.Seek(req.Offset, 0) f.Seek(req.Offset, 0)
buff := make([]byte, req.Len) buff := make([]byte, req.Len)
readLength, err := f.Read(buff) readLength, err := f.Read(buff)
f.Close() f.Close()
if err != nil { if err != nil {
gLog.Println(LvERROR, "read log content error:", err) gLog.Println(LvERROR, "read log content error:", err)
return err return err
} }
rsp := ReportLogRsp{} rsp := ReportLogRsp{}
rsp.Content = string(buff[:readLength]) rsp.Content = string(buff[:readLength])
rsp.FileName = req.FileName rsp.FileName = req.FileName
rsp.Total = fi.Size() rsp.Total = fi.Size()
rsp.Len = req.Len rsp.Len = req.Len
return GNetwork.write(MsgReport, MsgPushReportLog, &rsp) return GNetwork.write(MsgReport, MsgPushReportLog, &rsp)
} }
func handleReportGoroutine() (err error) { func handleReportGoroutine() (err error) {
gLog.Println(LvDEBUG, "handleReportGoroutine") gLog.Println(LvDEBUG, "handleReportGoroutine")
buf := make([]byte, 1024*128) buf := make([]byte, 1024*128)
stackLen := runtime.Stack(buf, true) stackLen := runtime.Stack(buf, true)
return GNetwork.write(MsgReport, MsgPushReportLog, string(buf[:stackLen])) return GNetwork.write(MsgReport, MsgPushReportLog, string(buf[:stackLen]))
} }
func handleCheckRemoteService(msg []byte) (err error) { func handleCheckRemoteService(msg []byte) (err error) {
gLog.Println(LvDEBUG, "handleCheckRemoteService") gLog.Println(LvDEBUG, "handleCheckRemoteService")
req := CheckRemoteService{} req := CheckRemoteService{}
if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil { if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil {
gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:])) gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:]))
return err return err
} }
rsp := PushRsp{Error: 0} rsp := PushRsp{Error: 0}
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", req.Host, req.Port), time.Second*3) conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", req.Host, req.Port), time.Second*3)
if err != nil { if err != nil {
rsp.Error = 1 rsp.Error = 1
rsp.Detail = ErrRemoteServiceUnable.Error() rsp.Detail = ErrRemoteServiceUnable.Error()
} else { } else {
conn.Close() conn.Close()
} }
return GNetwork.write(MsgReport, MsgReportResponse, rsp) return GNetwork.write(MsgReport, MsgReportResponse, rsp)
} }
+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
} }
} }
+189 -189
View File
@@ -1,189 +1,189 @@
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, 0, 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() { go func() {
// close outside for breaking the ReadFromUDP // close outside for breaking the ReadFromUDP
// wait 30s for echo testing // wait 30s for echo testing
buf := make([]byte, 1600) buf := make([]byte, 1600)
echoConn.SetReadDeadline(time.Now().Add(time.Second * 30)) echoConn.SetReadDeadline(time.Now().Add(time.Second * 30))
n, addr, err := echoConn.ReadFromUDP(buf) n, addr, err := echoConn.ReadFromUDP(buf)
if err != nil { if err != nil {
return return
} }
echoConn.WriteToUDP(buf[0:n], addr) echoConn.WriteToUDP(buf[0:n], addr)
gLog.Println(LvDEBUG, "echo server end") gLog.Println(LvDEBUG, "echo server end")
}() }()
// testing for public ip // testing for public ip
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
if i == 1 { if i == 1 {
// test upnp or nat-pmp // test upnp or nat-pmp
gLog.Println(LvDEBUG, "upnp test start") gLog.Println(LvDEBUG, "upnp test start")
nat, err := Discover() nat, err := Discover()
if err != nil || nat == nil { if err != nil || nat == nil {
gLog.Println(LvDEBUG, "could not perform UPNP discover:", err) gLog.Println(LvDEBUG, "could not perform UPNP discover:", err)
break break
} }
ext, err := nat.GetExternalAddress() ext, err := nat.GetExternalAddress()
if err != nil { if err != nil {
gLog.Println(LvDEBUG, "could not perform UPNP external address:", err) gLog.Println(LvDEBUG, "could not perform UPNP external address:", err)
break break
} }
gLog.Println(LvINFO, "PublicIP:", ext) gLog.Println(LvINFO, "PublicIP:", ext)
externalPort, err := nat.AddPortMapping("udp", echoPort, echoPort, "openp2p", 30) // 30 seconds fot upnp testing externalPort, err := nat.AddPortMapping("udp", echoPort, echoPort, "openp2p", 30) // 30 seconds fot upnp testing
if err != nil { if err != nil {
gLog.Println(LvDEBUG, "could not add udp UPNP port mapping", externalPort) gLog.Println(LvDEBUG, "could not add udp UPNP port mapping", externalPort)
break break
} else { } else {
nat.AddPortMapping("tcp", echoPort, echoPort, "openp2p", 604800) // 7 days for tcp connection nat.AddPortMapping("tcp", echoPort, echoPort, "openp2p", 604800) // 7 days for tcp connection
} }
} }
gLog.Printf(LvDEBUG, "public ip test start %s:%d", publicIP, echoPort) gLog.Printf(LvDEBUG, "public ip test start %s:%d", publicIP, echoPort)
conn, err := net.ListenUDP("udp", nil) conn, err := net.ListenUDP("udp", nil)
if err != nil { if err != nil {
break break
} }
defer conn.Close() defer conn.Close()
dst, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", publicIP, echoPort)) dst, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", publicIP, echoPort))
if err != nil { if err != nil {
break break
} }
conn.WriteTo([]byte("echo"), dst) conn.WriteTo([]byte("echo"), dst)
buf := make([]byte, 1600) buf := make([]byte, 1600)
// wait for echo testing // wait for echo testing
conn.SetReadDeadline(time.Now().Add(PublicIPEchoTimeout)) conn.SetReadDeadline(time.Now().Add(PublicIPEchoTimeout))
_, _, err = conn.ReadFromUDP(buf) _, _, err = conn.ReadFromUDP(buf)
if err == nil { if err == nil {
if i == 1 { if i == 1 {
gLog.Println(LvDEBUG, "UPNP or NAT-PMP:YES") gLog.Println(LvDEBUG, "UPNP or NAT-PMP:YES")
hasUPNPorNATPMP = 1 hasUPNPorNATPMP = 1
} else { } else {
gLog.Println(LvDEBUG, "public ip:YES") gLog.Println(LvDEBUG, "public ip:YES")
hasPublicIP = 1 hasPublicIP = 1
} }
break break
} }
} }
return 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)
} }
+133 -133
View File
@@ -1,133 +1,133 @@
//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 return err
} }
fmt.Printf("Delete route ok: %s %s %s\n", fields[0], fields[1], gateway) fmt.Printf("Delete route ok: %s %s %s\n", fields[0], fields[1], gateway)
} }
} }
return nil return nil
} }
+142 -142
View File
@@ -1,142 +1,142 @@
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) fmt.Println("Delete route error:", err)
} }
fmt.Printf("Delete route ok: %s %s %s\n", fields[0], fields[1], gateway) fmt.Printf("Delete route ok: %s %s %s\n", fields[0], fields[1], gateway)
} }
} }
return nil return nil
} }
+976 -976
View File
@@ -1,976 +1,976 @@
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 pn.online = false
pn.wgReconnect.Wait() // wait read/autorunapp goroutine end pn.wgReconnect.Wait() // wait read/autorunapp goroutine end
delay := ClientAPITimeout + time.Duration(rand.Int()%pn.loginMaxDelaySeconds)*time.Second delay := ClientAPITimeout + time.Duration(rand.Int()%pn.loginMaxDelaySeconds)*time.Second
time.Sleep(delay) time.Sleep(delay)
err := pn.init() err := pn.init()
if err != nil { if err != nil {
gLog.Println(LvERROR, "P2PNetwork init error:", err) gLog.Println(LvERROR, "P2PNetwork init error:", err)
} }
gConf.retryAllApp() gConf.retryAllApp()
case t := <-pn.tunnelCloseCh: case t := <-pn.tunnelCloseCh:
gLog.Printf(LvDEBUG, "got tunnelCloseCh %s", t.config.LogPeerNode()) gLog.Printf(LvDEBUG, "got tunnelCloseCh %s", t.config.LogPeerNode())
pn.apps.Range(func(id, i interface{}) bool { pn.apps.Range(func(id, i interface{}) bool {
app := i.(*p2pApp) app := i.(*p2pApp)
if app.DirectTunnel() == t { if app.DirectTunnel() == t {
app.setDirectTunnel(nil) app.setDirectTunnel(nil)
} }
if app.RelayTunnel() == t { if app.RelayTunnel() == t {
app.setRelayTunnel(nil) app.setRelayTunnel(nil)
} }
return true return true
}) })
} }
} }
} }
func (pn *P2PNetwork) NotifyTunnelClose(t *P2PTunnel) bool { func (pn *P2PNetwork) NotifyTunnelClose(t *P2PTunnel) bool {
select { select {
case pn.tunnelCloseCh <- t: case pn.tunnelCloseCh <- t:
return true return true
default: default:
} }
return false return false
} }
func (pn *P2PNetwork) Connect(timeout int) bool { func (pn *P2PNetwork) Connect(timeout int) bool {
// waiting for heartbeat // waiting for heartbeat
for i := 0; i < (timeout / 1000); i++ { for i := 0; i < (timeout / 1000); i++ {
if pn.hbTime.After(time.Now().Add(-NetworkHeartbeatTime)) { if pn.hbTime.After(time.Now().Add(-NetworkHeartbeatTime)) {
return true return true
} }
time.Sleep(time.Second) time.Sleep(time.Second)
} }
return false return false
} }
func (pn *P2PNetwork) runAll() { func (pn *P2PNetwork) runAll() {
gConf.mtx.Lock() // lock for copy gConf.Apps and the modification of config(it's pointer) gConf.mtx.Lock() // lock for copy gConf.Apps and the modification of config(it's pointer)
defer gConf.mtx.Unlock() defer gConf.mtx.Unlock()
allApps := gConf.Apps // read a copy, other thread will modify the gConf.Apps allApps := gConf.Apps // read a copy, other thread will modify the gConf.Apps
for _, config := range allApps { for _, config := range allApps {
if config.AppName == "" { if config.AppName == "" {
config.AppName = fmt.Sprintf("%d", config.ID()) config.AppName = fmt.Sprintf("%d", config.ID())
} }
if config.Enabled == 0 { if config.Enabled == 0 {
continue continue
} }
if _, ok := pn.apps.Load(config.ID()); ok { if _, ok := pn.apps.Load(config.ID()); ok {
continue continue
} }
config.peerToken = gConf.Network.Token config.peerToken = gConf.Network.Token
gConf.mtx.Unlock() // AddApp will take a period of time, let outside modify gConf gConf.mtx.Unlock() // AddApp will take a period of time, let outside modify gConf
pn.AddApp(*config) pn.AddApp(*config)
gConf.mtx.Lock() gConf.mtx.Lock()
} }
} }
func (pn *P2PNetwork) autorunApp() { func (pn *P2PNetwork) autorunApp() {
gLog.Println(LvINFO, "autorunApp start") gLog.Println(LvINFO, "autorunApp start")
pn.wgReconnect.Add(1) pn.wgReconnect.Add(1)
defer pn.wgReconnect.Done() defer pn.wgReconnect.Done()
for pn.running && pn.online { for pn.running && pn.online {
time.Sleep(time.Second) time.Sleep(time.Second)
pn.runAll() pn.runAll()
} }
gLog.Println(LvINFO, "autorunApp end") gLog.Println(LvINFO, "autorunApp end")
} }
func (pn *P2PNetwork) addRelayTunnel(config AppConfig) (*P2PTunnel, uint64, string, error) { func (pn *P2PNetwork) addRelayTunnel(config AppConfig) (*P2PTunnel, uint64, string, error) {
gLog.Printf(LvINFO, "addRelayTunnel to %s start", config.LogPeerNode()) gLog.Printf(LvINFO, "addRelayTunnel to %s start", config.LogPeerNode())
defer gLog.Printf(LvINFO, "addRelayTunnel to %s end", config.LogPeerNode()) defer gLog.Printf(LvINFO, "addRelayTunnel to %s end", config.LogPeerNode())
relayConfig := AppConfig{ relayConfig := AppConfig{
PeerNode: config.RelayNode, PeerNode: config.RelayNode,
peerToken: config.peerToken, peerToken: config.peerToken,
relayMode: "private"} relayMode: "private"}
if relayConfig.PeerNode == "" { if relayConfig.PeerNode == "" {
// find existing relay tunnel // find existing relay tunnel
pn.apps.Range(func(id, i interface{}) bool { pn.apps.Range(func(id, i interface{}) bool {
app := i.(*p2pApp) app := i.(*p2pApp)
if app.config.PeerNode != config.PeerNode { if app.config.PeerNode != config.PeerNode {
return true return true
} }
if app.RelayTunnel() == nil { if app.RelayTunnel() == nil {
return true return true
} }
relayConfig.PeerNode = app.RelayTunnel().config.PeerNode relayConfig.PeerNode = app.RelayTunnel().config.PeerNode
gLog.Printf(LvDEBUG, "found existing relay tunnel %s", relayConfig.LogPeerNode()) gLog.Printf(LvDEBUG, "found existing relay tunnel %s", relayConfig.LogPeerNode())
return false return false
}) })
if relayConfig.PeerNode == "" { // request relay node if relayConfig.PeerNode == "" { // request relay node
pn.reqGatewayMtx.Lock() pn.reqGatewayMtx.Lock()
pn.write(MsgRelay, MsgRelayNodeReq, &RelayNodeReq{config.PeerNode}) pn.write(MsgRelay, MsgRelayNodeReq, &RelayNodeReq{config.PeerNode})
head, body := pn.read("", MsgRelay, MsgRelayNodeRsp, ClientAPITimeout) head, body := pn.read("", MsgRelay, MsgRelayNodeRsp, ClientAPITimeout)
pn.reqGatewayMtx.Unlock() pn.reqGatewayMtx.Unlock()
if head == nil { if head == nil {
return nil, 0, "", errors.New("read MsgRelayNodeRsp error") return nil, 0, "", errors.New("read MsgRelayNodeRsp error")
} }
rsp := RelayNodeRsp{} rsp := RelayNodeRsp{}
if err := json.Unmarshal(body, &rsp); err != nil { if err := json.Unmarshal(body, &rsp); err != nil {
return nil, 0, "", errors.New("unmarshal MsgRelayNodeRsp error") return nil, 0, "", errors.New("unmarshal MsgRelayNodeRsp error")
} }
if rsp.RelayName == "" || rsp.RelayToken == 0 { if rsp.RelayName == "" || rsp.RelayToken == 0 {
gLog.Printf(LvERROR, "MsgRelayNodeReq error") gLog.Printf(LvERROR, "MsgRelayNodeReq error")
return nil, 0, "", errors.New("MsgRelayNodeReq error") return nil, 0, "", errors.New("MsgRelayNodeReq error")
} }
gLog.Printf(LvDEBUG, "got relay node:%s", relayConfig.LogPeerNode()) gLog.Printf(LvDEBUG, "got relay node:%s", relayConfig.LogPeerNode())
relayConfig.PeerNode = rsp.RelayName relayConfig.PeerNode = rsp.RelayName
relayConfig.peerToken = rsp.RelayToken relayConfig.peerToken = rsp.RelayToken
relayConfig.relayMode = rsp.Mode relayConfig.relayMode = rsp.Mode
} }
} }
/// ///
t, err := pn.addDirectTunnel(relayConfig, 0) t, err := pn.addDirectTunnel(relayConfig, 0)
if err != nil { if err != nil {
gLog.Println(LvERROR, "direct connect error:", err) gLog.Println(LvERROR, "direct connect error:", err)
return nil, 0, "", ErrConnectRelayNode // relay offline will stop retry return nil, 0, "", ErrConnectRelayNode // relay offline will stop retry
} }
// notify peer addRelayTunnel // notify peer addRelayTunnel
req := AddRelayTunnelReq{ req := AddRelayTunnelReq{
From: gConf.Network.Node, From: gConf.Network.Node,
RelayName: relayConfig.PeerNode, RelayName: relayConfig.PeerNode,
RelayToken: relayConfig.peerToken, RelayToken: relayConfig.peerToken,
RelayMode: relayConfig.relayMode, RelayMode: relayConfig.relayMode,
RelayTunnelID: t.id, RelayTunnelID: t.id,
} }
gLog.Printf(LvDEBUG, "push %s the relay node(%s)", config.LogPeerNode(), relayConfig.LogPeerNode()) gLog.Printf(LvDEBUG, "push %s the relay node(%s)", config.LogPeerNode(), relayConfig.LogPeerNode())
pn.push(config.PeerNode, MsgPushAddRelayTunnelReq, &req) pn.push(config.PeerNode, MsgPushAddRelayTunnelReq, &req)
// wait relay ready // wait relay ready
head, body := pn.read(config.PeerNode, MsgPush, MsgPushAddRelayTunnelRsp, PeerAddRelayTimeount) head, body := pn.read(config.PeerNode, MsgPush, MsgPushAddRelayTunnelRsp, PeerAddRelayTimeount)
if head == nil { if head == nil {
gLog.Printf(LvERROR, "read MsgPushAddRelayTunnelRsp error") gLog.Printf(LvERROR, "read MsgPushAddRelayTunnelRsp error")
return nil, 0, "", errors.New("read MsgPushAddRelayTunnelRsp error") return nil, 0, "", errors.New("read MsgPushAddRelayTunnelRsp error")
} }
rspID := TunnelMsg{} rspID := TunnelMsg{}
if err = json.Unmarshal(body, &rspID); err != nil { if err = json.Unmarshal(body, &rspID); err != nil {
gLog.Println(LvDEBUG, ErrPeerConnectRelay) gLog.Println(LvDEBUG, ErrPeerConnectRelay)
return nil, 0, "", ErrPeerConnectRelay return nil, 0, "", ErrPeerConnectRelay
} }
return t, rspID.ID, relayConfig.relayMode, err return t, rspID.ID, relayConfig.relayMode, err
} }
// use *AppConfig to save status // use *AppConfig to save status
func (pn *P2PNetwork) AddApp(config AppConfig) error { func (pn *P2PNetwork) AddApp(config AppConfig) error {
gLog.Printf(LvINFO, "addApp %s to %s:%s:%d start", config.AppName, config.LogPeerNode(), config.DstHost, config.DstPort) gLog.Printf(LvINFO, "addApp %s to %s:%s:%d start", config.AppName, config.LogPeerNode(), config.DstHost, config.DstPort)
defer gLog.Printf(LvINFO, "addApp %s to %s:%s:%d end", config.AppName, config.LogPeerNode(), config.DstHost, config.DstPort) defer gLog.Printf(LvINFO, "addApp %s to %s:%s:%d end", config.AppName, config.LogPeerNode(), config.DstHost, config.DstPort)
if !pn.online { if !pn.online {
return errors.New("P2PNetwork offline") return errors.New("P2PNetwork offline")
} }
if _, ok := pn.msgMap.Load(NodeNameToID(config.PeerNode)); !ok { if _, ok := pn.msgMap.Load(NodeNameToID(config.PeerNode)); !ok {
pn.msgMap.Store(NodeNameToID(config.PeerNode), make(chan msgCtx, 50)) pn.msgMap.Store(NodeNameToID(config.PeerNode), make(chan msgCtx, 50))
} }
// check if app already exist? // check if app already exist?
if _, ok := pn.apps.Load(config.ID()); ok { if _, ok := pn.apps.Load(config.ID()); ok {
return errors.New("P2PApp already exist") return errors.New("P2PApp already exist")
} }
app := p2pApp{ app := p2pApp{
// tunnel: t, // tunnel: t,
id: rand.Uint64(), id: rand.Uint64(),
key: rand.Uint64(), key: rand.Uint64(),
config: config, config: config,
iptree: NewIPTree(config.Whitelist), iptree: NewIPTree(config.Whitelist),
running: true, running: true,
hbTimeRelay: time.Now(), hbTimeRelay: time.Now(),
} }
if _, ok := pn.msgMap.Load(NodeNameToID(config.PeerNode)); !ok { if _, ok := pn.msgMap.Load(NodeNameToID(config.PeerNode)); !ok {
pn.msgMap.Store(NodeNameToID(config.PeerNode), make(chan msgCtx, 50)) pn.msgMap.Store(NodeNameToID(config.PeerNode), make(chan msgCtx, 50))
} }
pn.apps.Store(config.ID(), &app) pn.apps.Store(config.ID(), &app)
gLog.Printf(LvDEBUG, "Store app %d", config.ID()) gLog.Printf(LvDEBUG, "Store app %d", config.ID())
go app.checkP2PTunnel() go app.checkP2PTunnel()
return nil return nil
} }
func (pn *P2PNetwork) DeleteApp(config AppConfig) { func (pn *P2PNetwork) DeleteApp(config AppConfig) {
gLog.Printf(LvINFO, "DeleteApp %s to %s:%s:%d start", config.AppName, config.LogPeerNode(), config.DstHost, config.DstPort) gLog.Printf(LvINFO, "DeleteApp %s to %s:%s:%d start", config.AppName, config.LogPeerNode(), config.DstHost, config.DstPort)
defer gLog.Printf(LvINFO, "DeleteApp %s to %s:%s:%d end", config.AppName, config.LogPeerNode(), config.DstHost, config.DstPort) defer gLog.Printf(LvINFO, "DeleteApp %s to %s:%s:%d end", config.AppName, config.LogPeerNode(), config.DstHost, config.DstPort)
// close the apps of this config // close the apps of this config
i, ok := pn.apps.Load(config.ID()) i, ok := pn.apps.Load(config.ID())
if ok { if ok {
app := i.(*p2pApp) app := i.(*p2pApp)
gLog.Printf(LvINFO, "app %s exist, delete it", app.config.AppName) gLog.Printf(LvINFO, "app %s exist, delete it", app.config.AppName)
app.close() app.close()
pn.apps.Delete(config.ID()) pn.apps.Delete(config.ID())
} }
} }
func (pn *P2PNetwork) findTunnel(peerNode string) (t *P2PTunnel) { func (pn *P2PNetwork) findTunnel(peerNode string) (t *P2PTunnel) {
t = nil t = nil
// find existing tunnel to peer // find existing tunnel to peer
pn.allTunnels.Range(func(id, i interface{}) bool { pn.allTunnels.Range(func(id, i interface{}) bool {
tmpt := i.(*P2PTunnel) tmpt := i.(*P2PTunnel)
if tmpt.config.PeerNode == peerNode { if tmpt.config.PeerNode == peerNode {
gLog.Println(LvINFO, "tunnel already exist ", peerNode) gLog.Println(LvINFO, "tunnel already exist ", peerNode)
isActive := tmpt.checkActive() isActive := tmpt.checkActive()
// inactive, close it // inactive, close it
if !isActive { if !isActive {
gLog.Println(LvINFO, "but it's not active, close it ", peerNode) gLog.Println(LvINFO, "but it's not active, close it ", peerNode)
tmpt.close() tmpt.close()
} else { } else {
t = tmpt t = tmpt
} }
return false return false
} }
return true return true
}) })
return t return t
} }
func (pn *P2PNetwork) addDirectTunnel(config AppConfig, tid uint64) (t *P2PTunnel, err error) { 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.LogPeerNode(), config.DstHost, config.DstPort, tid) gLog.Printf(LvDEBUG, "addDirectTunnel %s%d to %s:%s:%d tid:%d start", config.Protocol, config.SrcPort, config.LogPeerNode(), config.DstHost, config.DstPort, tid)
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) 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)
isClient := false isClient := false
// client side tid=0, assign random uint64 // client side tid=0, assign random uint64
if tid == 0 { if tid == 0 {
tid = rand.Uint64() tid = rand.Uint64()
isClient = true isClient = true
} }
if _, ok := pn.msgMap.Load(NodeNameToID(config.PeerNode)); !ok { if _, ok := pn.msgMap.Load(NodeNameToID(config.PeerNode)); !ok {
pn.msgMap.Store(NodeNameToID(config.PeerNode), make(chan msgCtx, 50)) pn.msgMap.Store(NodeNameToID(config.PeerNode), make(chan msgCtx, 50))
} }
// server side // server side
if !isClient { if !isClient {
t, err = pn.newTunnel(config, tid, isClient) t, err = pn.newTunnel(config, tid, isClient)
return t, err // always return return t, err // always return
} }
// client side // client side
// peer info // peer info
initErr := pn.requestPeerInfo(&config) initErr := pn.requestPeerInfo(&config)
if initErr != nil { if initErr != nil {
gLog.Printf(LvERROR, "%s init error:%s", config.LogPeerNode(), initErr) gLog.Printf(LvERROR, "%s init error:%s", config.LogPeerNode(), initErr)
return nil, initErr return nil, 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,", 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,",
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) 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)
// try Intranet // try Intranet
if config.peerIP == gConf.Network.publicIP && compareVersion(config.peerVersion, SupportIntranetVersion) >= 0 { // old version client has no peerLanIP if config.peerIP == gConf.Network.publicIP && compareVersion(config.peerVersion, SupportIntranetVersion) >= 0 { // old version client has no peerLanIP
gLog.Println(LvINFO, "try Intranet") gLog.Println(LvINFO, "try Intranet")
config.linkMode = LinkModeIntranet config.linkMode = LinkModeIntranet
config.isUnderlayServer = 0 config.isUnderlayServer = 0
if t, err = pn.newTunnel(config, tid, isClient); err == nil { if t, err = pn.newTunnel(config, tid, isClient); err == nil {
return t, nil return t, nil
} }
} }
// try TCP6 // try TCP6
if IsIPv6(config.peerIPv6) && IsIPv6(gConf.IPv6()) { if IsIPv6(config.peerIPv6) && IsIPv6(gConf.IPv6()) {
gLog.Println(LvINFO, "try TCP6") gLog.Println(LvINFO, "try TCP6")
config.linkMode = LinkModeTCP6 config.linkMode = LinkModeTCP6
config.isUnderlayServer = 0 config.isUnderlayServer = 0
if t, err = pn.newTunnel(config, tid, isClient); err == nil { if t, err = pn.newTunnel(config, tid, isClient); err == nil {
return t, nil return t, nil
} }
} }
// try UDP6? maybe no // try UDP6? maybe no
// try TCP4 // try TCP4
if config.hasIPv4 == 1 || gConf.Network.hasIPv4 == 1 || config.hasUPNPorNATPMP == 1 || gConf.Network.hasUPNPorNATPMP == 1 { if config.hasIPv4 == 1 || gConf.Network.hasIPv4 == 1 || config.hasUPNPorNATPMP == 1 || gConf.Network.hasUPNPorNATPMP == 1 {
gLog.Println(LvINFO, "try TCP4") gLog.Println(LvINFO, "try TCP4")
config.linkMode = LinkModeTCP4 config.linkMode = LinkModeTCP4
if gConf.Network.hasIPv4 == 1 || gConf.Network.hasUPNPorNATPMP == 1 { if gConf.Network.hasIPv4 == 1 || gConf.Network.hasUPNPorNATPMP == 1 {
config.isUnderlayServer = 1 config.isUnderlayServer = 1
} else { } else {
config.isUnderlayServer = 0 config.isUnderlayServer = 0
} }
if t, err = pn.newTunnel(config, tid, isClient); err == nil { if t, err = pn.newTunnel(config, tid, isClient); err == nil {
return t, nil return t, nil
} }
} }
// try UDP4? maybe no // try UDP4? maybe no
var primaryPunchFunc func() (*P2PTunnel, error) var primaryPunchFunc func() (*P2PTunnel, error)
var secondaryPunchFunc func() (*P2PTunnel, error) var secondaryPunchFunc func() (*P2PTunnel, error)
funcUDP := func() (t *P2PTunnel, err error) { funcUDP := func() (t *P2PTunnel, err error) {
if config.PunchPriority&PunchPriorityUDPDisable != 0 { if config.PunchPriority&PunchPriorityUDPDisable != 0 {
return return
} }
// try UDPPunch // try UDPPunch
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 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 { if config.peerNatType == NATCone || gConf.Network.natType == NATCone {
gLog.Println(LvINFO, "try UDP4 Punch") gLog.Println(LvINFO, "try UDP4 Punch")
config.linkMode = LinkModeUDPPunch config.linkMode = LinkModeUDPPunch
config.isUnderlayServer = 0 config.isUnderlayServer = 0
if t, err = pn.newTunnel(config, tid, isClient); err == nil { if t, err = pn.newTunnel(config, tid, isClient); err == nil {
return t, nil return t, nil
} }
} }
if !(config.peerNatType == NATCone && gConf.Network.natType == NATCone) { // not cone2cone, no more try if !(config.peerNatType == NATCone && gConf.Network.natType == NATCone) { // not cone2cone, no more try
break break
} }
} }
return return
} }
funcTCP := func() (t *P2PTunnel, err error) { funcTCP := func() (t *P2PTunnel, err error) {
if config.PunchPriority&PunchPriorityTCPDisable != 0 { if config.PunchPriority&PunchPriorityTCPDisable != 0 {
return return
} }
// try TCPPunch // try TCPPunch
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 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 { if config.peerNatType == NATCone || gConf.Network.natType == NATCone {
gLog.Println(LvINFO, "try TCP4 Punch") gLog.Println(LvINFO, "try TCP4 Punch")
config.linkMode = LinkModeTCPPunch config.linkMode = LinkModeTCPPunch
config.isUnderlayServer = 0 config.isUnderlayServer = 0
if t, err = pn.newTunnel(config, tid, isClient); err == nil { if t, err = pn.newTunnel(config, tid, isClient); err == nil {
gLog.Println(LvINFO, "TCP4 Punch ok") gLog.Println(LvINFO, "TCP4 Punch ok")
return t, nil return t, nil
} }
} }
} }
return return
} }
if config.PunchPriority&PunchPriorityTCPFirst != 0 { if config.PunchPriority&PunchPriorityTCPFirst != 0 {
primaryPunchFunc = funcTCP primaryPunchFunc = funcTCP
secondaryPunchFunc = funcUDP secondaryPunchFunc = funcUDP
} else { } else {
primaryPunchFunc = funcTCP primaryPunchFunc = funcTCP
secondaryPunchFunc = funcUDP secondaryPunchFunc = funcUDP
} }
if t, err = primaryPunchFunc(); t != nil && err == nil { if t, err = primaryPunchFunc(); t != nil && err == nil {
return t, err return t, err
} }
if t, err = secondaryPunchFunc(); t != nil && err == nil { if t, err = secondaryPunchFunc(); t != nil && err == nil {
return t, err return t, err
} }
// TODO: s2s won't return err // TODO: s2s won't return err
return nil, err return nil, err
} }
func (pn *P2PNetwork) newTunnel(config AppConfig, tid uint64, isClient bool) (t *P2PTunnel, err error) { func (pn *P2PNetwork) newTunnel(config AppConfig, tid uint64, isClient bool) (t *P2PTunnel, err error) {
if isClient { // only client side find existing tunnel if isClient { // only client side find existing tunnel
if existTunnel := pn.findTunnel(config.PeerNode); existTunnel != nil { if existTunnel := pn.findTunnel(config.PeerNode); existTunnel != nil {
return existTunnel, nil return existTunnel, nil
} }
} }
t = &P2PTunnel{pn: pn, t = &P2PTunnel{pn: pn,
config: config, config: config,
id: tid, id: tid,
writeData: make(chan []byte, WriteDataChanSize), writeData: make(chan []byte, WriteDataChanSize),
writeDataSmall: make(chan []byte, WriteDataChanSize/30), writeDataSmall: make(chan []byte, WriteDataChanSize/30),
} }
t.initPort() t.initPort()
if isClient { if isClient {
if err = t.connect(); err != nil { if err = t.connect(); err != nil {
gLog.Println(LvERROR, "p2pTunnel connect error:", err) gLog.Println(LvERROR, "p2pTunnel connect error:", err)
return return
} }
} else { } else {
if err = t.listen(); err != nil { if err = t.listen(); err != nil {
gLog.Println(LvERROR, "p2pTunnel listen error:", err) gLog.Println(LvERROR, "p2pTunnel listen error:", err)
return return
} }
} }
// store it when success // store it when success
gLog.Printf(LvDEBUG, "store tunnel %d", tid) gLog.Printf(LvDEBUG, "store tunnel %d", tid)
pn.allTunnels.Store(tid, t) pn.allTunnels.Store(tid, t)
return return
} }
func (pn *P2PNetwork) init() error { func (pn *P2PNetwork) init() error {
gLog.Println(LvINFO, "P2PNetwork init start") gLog.Println(LvINFO, "P2PNetwork init start")
defer gLog.Println(LvINFO, "P2PNetwork init end") defer gLog.Println(LvINFO, "P2PNetwork init end")
pn.wgReconnect.Add(1) pn.wgReconnect.Add(1)
defer pn.wgReconnect.Done() defer pn.wgReconnect.Done()
var err error var err error
for { for {
// detect nat type // detect nat type
gConf.Network.publicIP, gConf.Network.natType, err = getNATType(gConf.Network.ServerHost, gConf.Network.UDPPort1, gConf.Network.UDPPort2) gConf.Network.publicIP, gConf.Network.natType, err = getNATType(gConf.Network.ServerHost, gConf.Network.UDPPort1, gConf.Network.UDPPort2)
if err != nil { if err != nil {
gLog.Println(LvDEBUG, "detect NAT type error:", err) gLog.Println(LvDEBUG, "detect NAT type error:", err)
break break
} }
if gConf.Network.hasIPv4 == 0 && gConf.Network.hasUPNPorNATPMP == 0 { // if already has ipv4 or upnp no need test again 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) gConf.Network.hasIPv4, gConf.Network.hasUPNPorNATPMP = publicIPTest(gConf.Network.publicIP, gConf.Network.TCPPort)
} }
// for testcase // for testcase
if strings.Contains(gConf.Network.Node, "openp2pS2STest") { if strings.Contains(gConf.Network.Node, "openp2pS2STest") {
gConf.Network.natType = NATSymmetric gConf.Network.natType = NATSymmetric
gConf.Network.hasIPv4 = 0 gConf.Network.hasIPv4 = 0
gConf.Network.hasUPNPorNATPMP = 0 gConf.Network.hasUPNPorNATPMP = 0
gLog.Println(LvINFO, "openp2pS2STest debug") gLog.Println(LvINFO, "openp2pS2STest debug")
} }
if strings.Contains(gConf.Network.Node, "openp2pC2CTest") { if strings.Contains(gConf.Network.Node, "openp2pC2CTest") {
gConf.Network.natType = NATCone gConf.Network.natType = NATCone
gConf.Network.hasIPv4 = 0 gConf.Network.hasIPv4 = 0
gConf.Network.hasUPNPorNATPMP = 0 gConf.Network.hasUPNPorNATPMP = 0
gLog.Println(LvINFO, "openp2pC2CTest debug") gLog.Println(LvINFO, "openp2pC2CTest debug")
} }
if gConf.Network.hasIPv4 == 1 || gConf.Network.hasUPNPorNATPMP == 1 { if gConf.Network.hasIPv4 == 1 || gConf.Network.hasUPNPorNATPMP == 1 {
onceV4Listener.Do(func() { onceV4Listener.Do(func() {
v4l = &v4Listener{port: gConf.Network.TCPPort} v4l = &v4Listener{port: gConf.Network.TCPPort}
go v4l.start() 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) 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) gatewayURL := fmt.Sprintf("%s:%d", gConf.Network.ServerHost, gConf.Network.ServerPort)
uri := "/api/v1/login" uri := "/api/v1/login"
caCertPool, errCert := x509.SystemCertPool() caCertPool, errCert := x509.SystemCertPool()
if errCert != nil { if errCert != nil {
gLog.Println(LvERROR, "Failed to load system root CAs:", errCert) gLog.Println(LvERROR, "Failed to load system root CAs:", errCert)
} else { } else {
caCertPool = x509.NewCertPool() caCertPool = x509.NewCertPool()
} }
caCertPool.AppendCertsFromPEM([]byte(rootCA)) caCertPool.AppendCertsFromPEM([]byte(rootCA))
caCertPool.AppendCertsFromPEM([]byte(ISRGRootX1)) caCertPool.AppendCertsFromPEM([]byte(ISRGRootX1))
config := tls.Config{ config := tls.Config{
RootCAs: caCertPool, RootCAs: caCertPool,
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 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.TLSClientConfig = &config websocket.DefaultDialer.TLSClientConfig = &config
websocket.DefaultDialer.HandshakeTimeout = ClientAPITimeout websocket.DefaultDialer.HandshakeTimeout = ClientAPITimeout
u := url.URL{Scheme: "wss", Host: gatewayURL, Path: uri} u := url.URL{Scheme: "wss", Host: gatewayURL, Path: uri}
q := u.Query() q := u.Query()
q.Add("node", gConf.Network.Node) q.Add("node", gConf.Network.Node)
q.Add("token", fmt.Sprintf("%d", gConf.Network.Token)) q.Add("token", fmt.Sprintf("%d", gConf.Network.Token))
q.Add("version", OpenP2PVersion) q.Add("version", OpenP2PVersion)
q.Add("nattype", fmt.Sprintf("%d", gConf.Network.natType)) q.Add("nattype", fmt.Sprintf("%d", gConf.Network.natType))
q.Add("sharebandwidth", fmt.Sprintf("%d", gConf.Network.ShareBandwidth)) q.Add("sharebandwidth", fmt.Sprintf("%d", gConf.Network.ShareBandwidth))
u.RawQuery = q.Encode() u.RawQuery = q.Encode()
var ws *websocket.Conn var ws *websocket.Conn
ws, _, err = websocket.DefaultDialer.Dial(u.String(), nil) ws, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil { if err != nil {
gLog.Println(LvERROR, "Dial error:", err) gLog.Println(LvERROR, "Dial error:", err)
break break
} }
pn.running = true pn.running = true
pn.online = true pn.online = true
pn.conn = ws pn.conn = ws
localAddr := strings.Split(ws.LocalAddr().String(), ":") localAddr := strings.Split(ws.LocalAddr().String(), ":")
if len(localAddr) == 2 { if len(localAddr) == 2 {
gConf.Network.localIP = localAddr[0] gConf.Network.localIP = localAddr[0]
} else { } else {
err = errors.New("get local ip failed") err = errors.New("get local ip failed")
break break
} }
go pn.readLoop() go pn.readLoop()
gConf.Network.mac = getmac(gConf.Network.localIP) gConf.Network.mac = getmac(gConf.Network.localIP)
gConf.Network.os = getOsName() gConf.Network.os = getOsName()
go func() { go func() {
req := ReportBasic{ req := ReportBasic{
Mac: gConf.Network.mac, Mac: gConf.Network.mac,
LanIP: gConf.Network.localIP, LanIP: gConf.Network.localIP,
OS: gConf.Network.os, OS: gConf.Network.os,
HasIPv4: gConf.Network.hasIPv4, HasIPv4: gConf.Network.hasIPv4,
HasUPNPorNATPMP: gConf.Network.hasUPNPorNATPMP, HasUPNPorNATPMP: gConf.Network.hasUPNPorNATPMP,
Version: OpenP2PVersion, Version: OpenP2PVersion,
} }
rsp := netInfo() rsp := netInfo()
gLog.Println(LvDEBUG, "netinfo:", rsp) gLog.Println(LvDEBUG, "netinfo:", rsp)
if rsp != nil && rsp.Country != "" { if rsp != nil && rsp.Country != "" {
if IsIPv6(rsp.IP.String()) { if IsIPv6(rsp.IP.String()) {
gConf.setIPv6(rsp.IP.String()) gConf.setIPv6(rsp.IP.String())
} }
req.NetInfo = *rsp req.NetInfo = *rsp
} else { } else {
pn.refreshIPv6() pn.refreshIPv6()
} }
req.IPv6 = gConf.IPv6() req.IPv6 = gConf.IPv6()
pn.write(MsgReport, MsgReportBasic, &req) pn.write(MsgReport, MsgReportBasic, &req)
}() }()
go pn.autorunApp() go pn.autorunApp()
pn.write(MsgSDWAN, MsgSDWANInfoReq, nil) pn.write(MsgSDWAN, MsgSDWANInfoReq, nil)
gLog.Println(LvDEBUG, "P2PNetwork init ok") gLog.Println(LvDEBUG, "P2PNetwork init ok")
break break
} }
if err != nil { if err != nil {
// init failed, retry // init failed, retry
pn.close() pn.close()
gLog.Println(LvERROR, "P2PNetwork init error:", err) gLog.Println(LvERROR, "P2PNetwork init error:", err)
} }
return err return err
} }
func (pn *P2PNetwork) handleMessage(msg []byte) { func (pn *P2PNetwork) handleMessage(msg []byte) {
head := openP2PHeader{} head := openP2PHeader{}
err := binary.Read(bytes.NewReader(msg[:openP2PHeaderSize]), binary.LittleEndian, &head) err := binary.Read(bytes.NewReader(msg[:openP2PHeaderSize]), binary.LittleEndian, &head)
if err != nil { if err != nil {
gLog.Println(LvERROR, "handleMessage error:", err) gLog.Println(LvERROR, "handleMessage error:", err)
return return
} }
switch head.MainType { switch head.MainType {
case MsgLogin: case MsgLogin:
// gLog.Println(LevelINFO,string(msg)) // gLog.Println(LevelINFO,string(msg))
rsp := LoginRsp{} rsp := LoginRsp{}
if err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp); err != nil { if err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp); err != nil {
gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(rsp), err) gLog.Printf(LvERROR, "wrong %v:%s", reflect.TypeOf(rsp), err)
return return
} }
if rsp.Error != 0 { if rsp.Error != 0 {
gLog.Printf(LvERROR, "login error:%d, detail:%s", rsp.Error, rsp.Detail) gLog.Printf(LvERROR, "login error:%d, detail:%s", rsp.Error, rsp.Detail)
pn.running = false pn.running = false
} else { } else {
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
} }
+806 -806
View File
@@ -1,806 +1,806 @@
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 pn *P2PNetwork
conn underlay conn underlay
hbTime time.Time hbTime time.Time
hbMtx sync.Mutex hbMtx sync.Mutex
config AppConfig config AppConfig
la *net.UDPAddr // local hole address la *net.UDPAddr // local hole address
ra *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.la = &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.LogPeerNode())
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
} }
t.pn.push(t.config.PeerNode, MsgPushConnectReq, req) t.pn.push(t.config.PeerNode, MsgPushConnectReq, req)
head, body := t.pn.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() {
t.pn.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()
} }
t.pn.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.LogPeerNode())
} }
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.ra, 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) + t.pn.dt + t.pn.ddtma*int64(time.Since(t.pn.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.LogPeerNode())
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.LogPeerNode())
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.LogPeerNode())
defer gLog.Printf(LvDEBUG, "connectUnderlayUDP %s end ", t.config.LogPeerNode()) defer gLog.Printf(LvDEBUG, "connectUnderlayUDP %s end ", t.config.LogPeerNode())
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 t.pn.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.la.String(), TunnelIdleTimeout) ul, err = listenKCP(t.la.String(), TunnelIdleTimeout)
} else { } else {
ul, err = listenQuic(t.la.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.la) 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.la) 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)
} }
} }
t.pn.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.ra.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.ra, TunnelIdleTimeout) ul, errL = dialKCP(conn, t.ra, TunnelIdleTimeout)
} else { } else {
ul, errL = dialQuic(conn, t.ra, 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.ra.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.LogPeerNode())
defer gLog.Printf(LvDEBUG, "connectUnderlayTCP %s end ", t.config.LogPeerNode()) defer gLog.Printf(LvDEBUG, "connectUnderlayTCP %s end ", t.config.LogPeerNode())
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 {
t.pn.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) + t.pn.dt + t.pn.ddtma*int64(time.Since(t.pn.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.LogPeerNode())
defer gLog.Printf(LvDEBUG, "connectUnderlayTCPSymmetric %s end ", t.config.LogPeerNode()) defer gLog.Printf(LvDEBUG, "connectUnderlayTCPSymmetric %s end ", t.config.LogPeerNode())
ts := time.Duration(int64(t.punchTs) + t.pn.dt + t.pn.ddtma*int64(time.Since(t.pn.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.Printf(LvERROR, "utcp.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.Printf(LvERROR, "utcp.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.LogPeerNode())
defer gLog.Printf(LvDEBUG, "connectUnderlayTCP6 %s end ", t.config.LogPeerNode()) defer gLog.Printf(LvDEBUG, "connectUnderlayTCP6 %s end ", t.config.LogPeerNode())
var ul *underlayTCP6 var ul *underlayTCP6
if t.config.isUnderlayServer == 1 { if t.config.isUnderlayServer == 1 {
t.pn.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
t.pn.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 { 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, 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, 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 := t.pn.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.LogPeerNode(), 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
t.pn.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)
t.pn.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.LogPeerNode(), 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.LogPeerNode(), 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) - t.pn.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()
} }
t.pn.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 := t.pn.nodeData ch := t.pn.nodeData
// if body[9] == 1 { // TODO: deal relay // if body[9] == 1 { // TODO: deal relay
// ch = t.pn.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.LogPeerNode(), 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.LogPeerNode(), 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)
} }
+292 -292
View File
@@ -1,292 +1,292 @@
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) init(name string) error {
if gConf.getSDWAN().Gateway == "" { if gConf.getSDWAN().Gateway == "" {
gLog.Println(LvDEBUG, "not in sdwan clear all ") gLog.Println(LvDEBUG, "not in sdwan clear all ")
} }
if s.internalRoute == nil { if s.internalRoute == nil {
s.internalRoute = NewIPTree("") s.internalRoute = NewIPTree("")
} }
s.nodeName = name s.nodeName = name
s.gateway, s.subnet, _ = net.ParseCIDR(gConf.getSDWAN().Gateway) s.gateway, s.subnet, _ = net.ParseCIDR(gConf.getSDWAN().Gateway)
for _, node := range gConf.getDelNodes() { for _, node := range gConf.getDelNodes() {
gLog.Println(LvDEBUG, "deal deleted node: ", node.Name) gLog.Println(LvDEBUG, "deal deleted node: ", node.Name)
delRoute(node.IP, s.gateway.String()) delRoute(node.IP, s.gateway.String())
s.internalRoute.Del(node.IP, node.IP) s.internalRoute.Del(node.IP, node.IP)
ipNum, _ := inetAtoN(node.IP) ipNum, _ := inetAtoN(node.IP)
s.sysRoute.Delete(ipNum) s.sysRoute.Delete(ipNum)
gConf.delete(AppConfig{SrcPort: 0, PeerNode: node.Name}) gConf.delete(AppConfig{SrcPort: 0, PeerNode: node.Name})
GNetwork.DeleteApp(AppConfig{SrcPort: 0, PeerNode: node.Name}) GNetwork.DeleteApp(AppConfig{SrcPort: 0, PeerNode: node.Name})
arr := strings.Split(node.Resource, ",") arr := strings.Split(node.Resource, ",")
for _, r := range arr { for _, r := range arr {
_, ipnet, err := net.ParseCIDR(r) _, ipnet, err := net.ParseCIDR(r)
if err != nil { if err != nil {
// fmt.Println("Error parsing CIDR:", err) // fmt.Println("Error parsing CIDR:", err)
continue continue
} }
if ipnet.Contains(net.ParseIP(gConf.Network.localIP)) { // local ip and resource in the same lan if ipnet.Contains(net.ParseIP(gConf.Network.localIP)) { // local ip and resource in the same lan
continue continue
} }
minIP := ipnet.IP minIP := ipnet.IP
maxIP := make(net.IP, len(minIP)) maxIP := make(net.IP, len(minIP))
copy(maxIP, minIP) copy(maxIP, minIP)
for i := range minIP { for i := range minIP {
maxIP[i] = minIP[i] | ^ipnet.Mask[i] maxIP[i] = minIP[i] | ^ipnet.Mask[i]
} }
s.internalRoute.Del(minIP.String(), maxIP.String()) s.internalRoute.Del(minIP.String(), maxIP.String())
delRoute(ipnet.String(), s.gateway.String()) delRoute(ipnet.String(), s.gateway.String())
} }
} }
for _, node := range gConf.getAddNodes() { for _, node := range gConf.getAddNodes() {
gLog.Println(LvDEBUG, "deal add node: ", node.Name) gLog.Println(LvDEBUG, "deal add node: ", node.Name)
ipNet := &net.IPNet{ ipNet := &net.IPNet{
IP: net.ParseIP(node.IP), IP: net.ParseIP(node.IP),
Mask: s.subnet.Mask, Mask: s.subnet.Mask,
} }
if node.Name == s.nodeName { if node.Name == s.nodeName {
s.virtualIP = ipNet s.virtualIP = ipNet
gLog.Println(LvINFO, "start tun ", ipNet.String()) gLog.Println(LvINFO, "start tun ", ipNet.String())
err := s.StartTun() err := s.StartTun()
if err != nil { if err != nil {
gLog.Println(LvERROR, "start tun error:", err) gLog.Println(LvERROR, "start tun error:", err)
return err return err
} }
gLog.Println(LvINFO, "start tun ok") gLog.Println(LvINFO, "start tun ok")
allowTunForward() allowTunForward()
addRoute(s.subnet.String(), s.gateway.String(), s.tun.tunName) addRoute(s.subnet.String(), s.gateway.String(), s.tun.tunName)
// addRoute("255.255.255.255/32", s.gateway.String(), s.tun.tunName) // for broadcast // addRoute("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 // addRoute("224.0.0.0/4", s.gateway.String(), s.tun.tunName) // for multicast
initSNATRule(s.subnet.String()) // for network resource initSNATRule(s.subnet.String()) // for network resource
continue continue
} }
ip, err := inetAtoN(ipNet.String()) ip, err := inetAtoN(ipNet.String())
if err != nil { if err != nil {
return err return err
} }
s.sysRoute.Store(ip, &sdwanNode{name: node.Name, id: NodeNameToID(node.Name)}) s.sysRoute.Store(ip, &sdwanNode{name: node.Name, id: NodeNameToID(node.Name)})
s.internalRoute.AddIntIP(ip, ip, &sdwanNode{name: node.Name, id: NodeNameToID(node.Name)}) s.internalRoute.AddIntIP(ip, ip, &sdwanNode{name: node.Name, id: NodeNameToID(node.Name)})
} }
for _, node := range gConf.getAddNodes() { for _, node := range gConf.getAddNodes() {
if node.Name == s.nodeName { // not deal resource itself if node.Name == s.nodeName { // not deal resource itself
continue continue
} }
if len(node.Resource) > 0 { if len(node.Resource) > 0 {
gLog.Printf(LvINFO, "deal add node: %s resource: %s", node.Name, node.Resource) gLog.Printf(LvINFO, "deal add node: %s resource: %s", node.Name, node.Resource)
arr := strings.Split(node.Resource, ",") arr := strings.Split(node.Resource, ",")
for _, r := range arr { for _, r := range arr {
// add internal route // add internal route
_, ipnet, err := net.ParseCIDR(r) _, ipnet, err := net.ParseCIDR(r)
if err != nil { if err != nil {
fmt.Println("Error parsing CIDR:", err) fmt.Println("Error parsing CIDR:", err)
continue continue
} }
if ipnet.Contains(net.ParseIP(gConf.Network.localIP)) { // local ip and resource in the same lan if ipnet.Contains(net.ParseIP(gConf.Network.localIP)) { // local ip and resource in the same lan
continue continue
} }
minIP := ipnet.IP minIP := ipnet.IP
maxIP := make(net.IP, len(minIP)) maxIP := make(net.IP, len(minIP))
copy(maxIP, minIP) copy(maxIP, minIP)
for i := range minIP { for i := range minIP {
maxIP[i] = minIP[i] | ^ipnet.Mask[i] maxIP[i] = minIP[i] | ^ipnet.Mask[i]
} }
s.internalRoute.Add(minIP.String(), maxIP.String(), &sdwanNode{name: node.Name, id: NodeNameToID(node.Name)}) s.internalRoute.Add(minIP.String(), maxIP.String(), &sdwanNode{name: node.Name, id: NodeNameToID(node.Name)})
// add sys route // add sys route
addRoute(ipnet.String(), s.gateway.String(), s.tun.tunName) addRoute(ipnet.String(), s.gateway.String(), s.tun.tunName)
} }
} }
} }
gConf.retryAllMemApp() gConf.retryAllMemApp()
gLog.Printf(LvINFO, "sdwan init ok") gLog.Printf(LvINFO, "sdwan init ok")
return nil return nil
} }
func (s *p2pSDWAN) run() { func (s *p2pSDWAN) run() {
s.sysRoute.Range(func(key, value interface{}) bool { s.sysRoute.Range(func(key, value interface{}) bool {
node := value.(*sdwanNode) node := value.(*sdwanNode)
GNetwork.ConnectNode(node.name) GNetwork.ConnectNode(node.name)
return true return true
}) })
} }
func (s *p2pSDWAN) readNodeLoop() { func (s *p2pSDWAN) readNodeLoop() {
gLog.Printf(LvDEBUG, "sdwan readNodeLoop start") gLog.Printf(LvDEBUG, "sdwan readNodeLoop start")
defer gLog.Printf(LvDEBUG, "sdwan readNodeLoop end") defer gLog.Printf(LvDEBUG, "sdwan readNodeLoop end")
writeBuff := make([][]byte, 1) writeBuff := make([][]byte, 1)
for { for {
nd := GNetwork.ReadNode(time.Second * 10) // TODO: read multi packet nd := GNetwork.ReadNode(time.Second * 10) // TODO: read multi packet
if nd == nil { if nd == nil {
gLog.Printf(LvDev, "waiting for node data") gLog.Printf(LvDev, "waiting for node data")
continue continue
} }
head := PacketHeader{} head := PacketHeader{}
parseHeader(nd.Data, &head) parseHeader(nd.Data, &head)
gLog.Printf(LvDev, "write tun dst ip=%s,len=%d", net.IP{byte(head.dst >> 24), byte(head.dst >> 16), byte(head.dst >> 8), byte(head.dst)}.String(), len(nd.Data)) 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 { if PIHeaderSize == 0 {
writeBuff[0] = nd.Data writeBuff[0] = nd.Data
} else { } else {
writeBuff[0] = make([]byte, PIHeaderSize+len(nd.Data)) writeBuff[0] = make([]byte, PIHeaderSize+len(nd.Data))
copy(writeBuff[0][PIHeaderSize:], nd.Data) copy(writeBuff[0][PIHeaderSize:], nd.Data)
} }
len, err := s.tun.Write(writeBuff, PIHeaderSize) len, err := s.tun.Write(writeBuff, PIHeaderSize)
if err != 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) 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 isBroadcastOrMulticast(ipUint32 uint32, subnet *net.IPNet) bool { func isBroadcastOrMulticast(ipUint32 uint32, subnet *net.IPNet) bool {
// return ipUint32 == 0xffffffff || (byte(ipUint32) == 0xff) || (ipUint32>>28 == 0xe) // 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 return ipUint32 == 0xffffffff || (ipUint32>>28 == 0xe) // 225.255.255.255/32, 224.0.0.0/4
} }
func (s *p2pSDWAN) routeTunPacket(p []byte, head *PacketHeader) { func (s *p2pSDWAN) routeTunPacket(p []byte, head *PacketHeader) {
var node *sdwanNode var node *sdwanNode
// v, ok := s.routes.Load(ih.dst) // v, ok := s.routes.Load(ih.dst)
v, ok := s.internalRoute.Load(head.dst) v, ok := s.internalRoute.Load(head.dst)
if !ok || v == nil { if !ok || v == nil {
if isBroadcastOrMulticast(head.dst, s.subnet) { if isBroadcastOrMulticast(head.dst, s.subnet) {
gLog.Printf(LvDev, "multicast ip=%s", net.IP{byte(head.dst >> 24), byte(head.dst >> 16), byte(head.dst >> 8), byte(head.dst)}.String()) gLog.Printf(LvDev, "multicast ip=%s", net.IP{byte(head.dst >> 24), byte(head.dst >> 16), byte(head.dst >> 8), byte(head.dst)}.String())
GNetwork.WriteBroadcast(p) GNetwork.WriteBroadcast(p)
} }
return return
} else { } else {
node = v.(*sdwanNode) node = v.(*sdwanNode)
} }
err := GNetwork.WriteNode(node.id, p) err := GNetwork.WriteNode(node.id, p)
if err != nil { if err != nil {
gLog.Printf(LvDev, "write packet to %s fail: %s", node.name, err) gLog.Printf(LvDev, "write packet to %s fail: %s", node.name, err)
} }
} }
func (s *p2pSDWAN) readTunLoop() { func (s *p2pSDWAN) readTunLoop() {
gLog.Printf(LvDEBUG, "sdwan readTunLoop start") gLog.Printf(LvDEBUG, "sdwan readTunLoop start")
defer gLog.Printf(LvDEBUG, "sdwan readTunLoop end") defer gLog.Printf(LvDEBUG, "sdwan readTunLoop end")
readBuff := make([][]byte, ReadTunBuffNum) readBuff := make([][]byte, ReadTunBuffNum)
for i := 0; i < ReadTunBuffNum; i++ { for i := 0; i < ReadTunBuffNum; i++ {
readBuff[i] = make([]byte, ReadTunBuffSize+PIHeaderSize) readBuff[i] = make([]byte, ReadTunBuffSize+PIHeaderSize)
} }
readBuffSize := make([]int, ReadTunBuffNum) readBuffSize := make([]int, ReadTunBuffNum)
ih := PacketHeader{} ih := PacketHeader{}
for { for {
n, err := s.tun.Read(readBuff, readBuffSize, PIHeaderSize) n, err := s.tun.Read(readBuff, readBuffSize, PIHeaderSize)
if err != nil { if err != nil {
gLog.Printf(LvERROR, "read tun fail: ", err) gLog.Printf(LvERROR, "read tun fail: ", err)
return return
} }
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
if readBuffSize[i] > ReadTunBuffSize { if readBuffSize[i] > ReadTunBuffSize {
gLog.Printf(LvERROR, "read tun overflow: len=", readBuffSize[i]) gLog.Printf(LvERROR, "read tun overflow: len=", readBuffSize[i])
continue continue
} }
parseHeader(readBuff[i][PIHeaderSize:readBuffSize[i]+PIHeaderSize], &ih) parseHeader(readBuff[i][PIHeaderSize:readBuffSize[i]+PIHeaderSize], &ih)
gLog.Printf(LvDev, "read tun dst ip=%s,len=%d", net.IP{byte(ih.dst >> 24), byte(ih.dst >> 16), byte(ih.dst >> 8), byte(ih.dst)}.String(), readBuffSize[0]) gLog.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])
s.routeTunPacket(readBuff[i][PIHeaderSize:readBuffSize[i]+PIHeaderSize], &ih) s.routeTunPacket(readBuff[i][PIHeaderSize:readBuffSize[i]+PIHeaderSize], &ih)
} }
} }
} }
func (s *p2pSDWAN) StartTun() error { func (s *p2pSDWAN) StartTun() error {
sdwan := gConf.getSDWAN() sdwan := gConf.getSDWAN()
if s.tun == nil { if s.tun == nil {
tun := &optun{} tun := &optun{}
err := tun.Start(s.virtualIP.String(), &sdwan) err := tun.Start(s.virtualIP.String(), &sdwan)
if err != nil { if err != nil {
gLog.Println(LvERROR, "open tun fail:", err) gLog.Println(LvERROR, "open tun fail:", err)
return err return err
} }
s.tun = tun s.tun = tun
go s.readTunLoop() go s.readTunLoop()
go s.readNodeLoop() // multi-thread read will cause packets out of order, resulting in slower speeds 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) err := setTunAddr(s.tun.tunName, s.virtualIP.String(), sdwan.Gateway, s.tun.dev)
if err != nil { if err != nil {
gLog.Printf(LvERROR, "setTunAddr error:%s,%s,%s,%s", err, s.tun.tunName, s.virtualIP.String(), sdwan.Gateway) gLog.Printf(LvERROR, "setTunAddr error:%s,%s,%s,%s", err, s.tun.tunName, s.virtualIP.String(), sdwan.Gateway)
return err return err
} }
return nil return nil
} }
func handleSDWAN(subType uint16, msg []byte) error { func handleSDWAN(subType uint16, msg []byte) error {
gLog.Printf(LvDEBUG, "handle sdwan msg type:%d", subType) gLog.Printf(LvDEBUG, "handle sdwan msg type:%d", subType)
var err error var err error
switch subType { switch subType {
case MsgSDWANInfoRsp: case MsgSDWANInfoRsp:
rsp := SDWANInfo{} rsp := SDWANInfo{}
if err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp); err != nil { if err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp); err != nil {
return ErrMsgFormat return ErrMsgFormat
} }
gLog.Println(LvINFO, "sdwan init:", prettyJson(rsp)) gLog.Println(LvINFO, "sdwan init:", prettyJson(rsp))
if runtime.GOOS == "android" { if runtime.GOOS == "android" {
AndroidSDWANConfig <- msg[openP2PHeaderSize:] AndroidSDWANConfig <- msg[openP2PHeaderSize:]
} }
// GNetwork.sdwan.detail = &rsp // GNetwork.sdwan.detail = &rsp
gConf.setSDWAN(rsp) gConf.setSDWAN(rsp)
err = GNetwork.sdwan.init(gConf.Network.Node) err = GNetwork.sdwan.init(gConf.Network.Node)
if err != nil { if err != nil {
gLog.Println(LvERROR, "sdwan init fail: ", err) gLog.Println(LvERROR, "sdwan init fail: ", err)
if GNetwork.sdwan.tun != nil { if GNetwork.sdwan.tun != nil {
GNetwork.sdwan.tun.Stop() GNetwork.sdwan.tun.Stop()
GNetwork.sdwan.tun = nil GNetwork.sdwan.tun = nil
return err return err
} }
} }
go GNetwork.sdwan.run() go GNetwork.sdwan.run()
default: default:
} }
return err return err
} }
+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)
} }
} }
} }
+18 -18
View File
@@ -1,18 +1,18 @@
package main package main
// On Windows env // On Windows env
// cd lib // cd lib
// go build -o openp2p.dll -buildmode=c-shared openp2p.go // go build -o openp2p.dll -buildmode=c-shared openp2p.go
// caller example see example/dll // caller example see example/dll
import ( import (
op "openp2p/core" op "openp2p/core"
) )
import "C" import "C"
func main() { func main() {
} }
//export RunCmd //export RunCmd
func RunCmd(cmd *C.char) { func RunCmd(cmd *C.char) {
op.RunCmd(C.GoString(cmd)) op.RunCmd(C.GoString(cmd))
} }