Compare commits

..

10 Commits

Author SHA1 Message Date
TenderIronh
2af77668fe auto adjust node name 2022-09-13 20:47:32 +08:00
TenderIronh
215feb8721 doc 2022-08-08 23:19:47 +08:00
TenderIronh
4a115fb3fa doc remote debug 2022-08-08 23:10:37 +08:00
TenderIronh
0fb3bc4e26 request peer info token wrong 2022-06-15 23:20:13 +08:00
TenderIronh
df23b30d2b doc 2022-05-29 16:05:18 +08:00
TenderIronh
f9b5073e0d fix some bug 2022-05-29 15:48:56 +08:00
TenderIronh
9ea467c7b3 rm rename 2022-05-26 23:43:06 +08:00
TenderIronh
c0bad61eb6 tcp punch 2022-05-26 23:18:22 +08:00
TenderIronh
bb32133038 config template 2022-05-17 15:53:29 +08:00
TenderIronh
532d3667ce support ipv6 2022-05-15 13:08:56 +08:00
32 changed files with 760 additions and 598 deletions

View File

@@ -23,7 +23,7 @@
### 5. 跨平台
因为轻量所以很容易支持各个平台。支持主流的操作系统Windows,Linux,MacOS和主流的cpu架构386、amd64、arm、arm64、mipsle、mipsle64、mips、mips64
### 6. 高效
P2P直连可以让你的设备跑满带宽。不论你的设备在任何网络环境无论NAT1-4Cone或Symmetric都支持。依靠Quic协议优秀的拥塞算法能在糟糕的网络环境获得高带宽低延时。
P2P直连可以让你的设备跑满带宽。不论你的设备在任何网络环境无论NAT1-4Cone或SymmetricUDP或TCP打洞,UPNP,IPv6都支持。依靠Quic协议优秀的拥塞算法能在糟糕的网络环境获得高带宽低延时。
### 7. 二次开发
基于OpenP2P只需数行代码就能让原来只能局域网通信的程序变成任何内网都能通信
@@ -103,15 +103,15 @@ go mod tidy
go build
```
## TODO
## RoadMap
近期计划:
1. 支持IPv6
2. 支持随系统自动启动,安装成系统服务(100%)
3. 提供一些免费服务器给特别差的网络,如广电网络(100%)
4. 建立网站用户可以在网站管理所有P2PApp和设备。查看设备在线状态升级增删查改重启P2PApp等(100%)
1. ~~支持IPv6~~(100%)
2. ~~支持随系统自动启动,安装成系统服务~~(100%)
3. ~~提供一些免费服务器给特别差的网络,如广电网络~~(100%)
4. ~~建立网站用户可以在网站管理所有P2PApp和设备。查看设备在线状态升级增删查改重启P2PApp等~~(100%)
5. 建立公众号用户可在微信公众号管理所有P2PApp和设备
6. 客户端提供WebUI
7. 支持自有服务器高并发连接
7. 支持自有服务器,开源服务器程序
8. 共享节点调度模型优化,对不同的运营商优化
9. 方便二次开发提供API和lib
10. 应用层支持UDP协议实现很简单但UDP应用较少暂不急(100%)
@@ -119,6 +119,7 @@ go build
12. 支持Android系统让旧手机焕发青春变成移动网关
13. 支持Windows网上邻居共享文件
14. 内网直连优化,用处不大,估计就用户测试时用到
15. ~~支持UPNP~~(100%)
远期计划:
1. 利用区块链技术去中心化,让共享设备的用户有收益,从而促进更多用户共享,达到正向闭环。

View File

@@ -25,7 +25,7 @@ The code is open source, the P2P tunnel uses TLS1.3+AES double encryption, and t
Benefit from lightweight, it easily supports most of major OS, like Windows, Linux, MacOS, also most of CPU architecture, like 386、amd64、arm、arm64、mipsle、mipsle64、mips、mips64.
### 6. Efficient
P2P direct connection lets your devices make good use of bandwidth. Your device can be connected in any network environments, even supports NAT1-4 (Cone or Symmetric). Relying on the excellent congestion algorithm of the Quic protocol, high bandwidth and low latency can be obtained in a bad network environment.
P2P direct connection lets your devices make good use of bandwidth. Your device can be connected in any network environments, even supports NAT1-4 (Cone or Symmetric),UDP or TCP punching,UPNP,IPv6. Relying on the excellent congestion algorithm of the Quic protocol, high bandwidth and low latency can be obtained in a bad network environment.
### 7. Integration
Your applicaiton can call OpenP2P with a few code to make any internal networks communicate with each other.
@@ -111,22 +111,23 @@ go mod tidy
go build
```
## TODO
## RoadMap
Short-Term:
1. Support IPv6.
2. Support auto run when system boot, setup system service.(100%)
3. Provide free servers to some low-performance network.(100%)
4. Build website, users can manage all P2PApp and devices via it. View devices' online status, upgrade, restart or CURD P2PApp .(100%)
1. ~~Support IPv6.~~(100%)
2. ~~Support auto run when system boot, setup system service.~~(100%)
3. ~~Provide free servers to some low-performance network.~~(100%)
4. ~~Build website, users can manage all P2PApp and devices via it. View devices' online status, upgrade, restart or CURD P2PApp .~~(100%)
5. Provide wechat official account, user can manage P2PApp nodes and deivce as same as website.
6. Provide WebUI on client side.
7. Support high concurrency on server side.
7. Support private server, open source server program.
8. Optimize our share scheduling model for different network operators.
9. Provide REST APIs and libary for secondary development.
10. Support UDP at application layer, it is easy to implement but not urgent due to only a few applicaitons using UDP protocol.(100%)
10. ~~Support UDP at application layer, it is easy to implement but not urgent due to only a few applicaitons using UDP protocol.~~(100%)
11. Support KCP protocol underlay, currently support Quic only. KCP focus on delay optimization,which has been widely used as game accelerator,it can sacrifice part of bandwidth to reduce timelag.
12. Support Android platform, let the phones to be mobile gateway.
13. Support SMB Windows neighborhood.
14. Direct connection on intranet, for testing.
15. ~~Support UPNP.~~(100%)
Long-Term:

View File

@@ -77,7 +77,7 @@ nohup ./openp2p -d -node OFFICEPC1 -token TOKEN &
# update local client
./openp2p update
# update remote client
curl --insecure 'https://openp2p.cn:27182/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系统需要设置防火墙放行本程序程序会自动设置如果设置失败会影响连接功能。
@@ -91,4 +91,9 @@ firewall-cmd --state
## 卸载
```
./openp2p uninstall
# 已安装时
# windows
C:\Program Files\OpenP2P\openp2p.exe uninstall
# linux,macos
sudo /usr/local/openp2p/openp2p uninstall
```

View File

@@ -79,7 +79,7 @@ Configuration example
# update local client
./openp2p update
# update remote client
curl --insecure 'https://openp2p.cn:27182/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.
@@ -93,4 +93,9 @@ firewall-cmd --state
## Uninstall
```
./openp2p uninstall
# when already installed
# windows
C:\Program Files\OpenP2P\openp2p.exe uninstall
# linux,macos
sudo /usr/local/openp2p/openp2p uninstall
```

View File

@@ -17,6 +17,8 @@ import (
"time"
)
const MinNodeNameLen = 8
func getmac(ip string) string {
//get mac relative to the ip address which connected to the mq.
ifaces, err := net.Interfaces()
@@ -156,7 +158,7 @@ func execOutput(name string, args ...string) string {
func defaultNodeName() string {
name, _ := os.Hostname()
for len(name) < 8 {
for len(name) < MinNodeNameLen {
name = fmt.Sprintf("%s%d", name, rand.Int()%10)
}
return name
@@ -188,6 +190,25 @@ func compareVersion(v1, v2 string) int {
return LESS
}
func parseMajorVer(ver string) int {
v1Arr := strings.Split(ver, ".")
if len(v1Arr) > 0 {
n, _ := strconv.ParseInt(v1Arr[0], 10, 32)
return int(n)
}
return 0
}
func IsIPv6(address string) bool {
return strings.Count(address, ":") >= 2
}
var letters = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-")
func randStr(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}

View File

@@ -49,6 +49,11 @@ func assertCompareVersion(t *testing.T, v1 string, v2 string, result int) {
t.Errorf("compare version %s %s fail\n", v1, v2)
}
}
func assertParseMajorVer(t *testing.T, v string, result int) {
if parseMajorVer(v) != result {
t.Errorf("ParseMajorVer %s fail\n", v)
}
}
func TestCompareVersion(t *testing.T) {
// test =
assertCompareVersion(t, "0.98.0", "0.98.0", EQUAL)
@@ -69,3 +74,23 @@ func TestCompareVersion(t *testing.T) {
assertCompareVersion(t, "", "1.5.0", LESS)
}
func TestParseMajorVer(t *testing.T) {
assertParseMajorVer(t, "0.98.0", 0)
assertParseMajorVer(t, "0.98", 0)
assertParseMajorVer(t, "1.4.0", 1)
assertParseMajorVer(t, "1.5.0", 1)
assertParseMajorVer(t, "0.98.0.22345", 0)
assertParseMajorVer(t, "1.98.0.12345", 1)
assertParseMajorVer(t, "10.98.0.12345", 10)
assertParseMajorVer(t, "1.4.0", 1)
assertParseMajorVer(t, "1.4", 1)
assertParseMajorVer(t, "1", 1)
assertParseMajorVer(t, "2", 2)
assertParseMajorVer(t, "3", 3)
assertParseMajorVer(t, "2.1.0", 2)
assertParseMajorVer(t, "3.0.0", 3)
}

View File

@@ -11,8 +11,6 @@ import (
var gConf Config
const IntValueNotSet int = -99999999
type AppConfig struct {
// required
AppName string
@@ -24,20 +22,23 @@ type AppConfig struct {
PeerUser string
Enabled int // default:1
// runtime info
peerVersion string
peerToken uint64
peerNatType int
hasIPv4 int
IPv6 string
hasUPNPorNATPMP int
peerIP string
peerConeNatPort int
retryNum int
retryTime time.Time
nextRetryTime time.Time
shareBandwidth int
errMsg string
connectTime time.Time
peerVersion string
peerToken uint64
peerNatType int
hasIPv4 int
peerIPv6 string
hasUPNPorNATPMP int
peerIP string
peerConeNatPort int
retryNum int
retryTime time.Time
nextRetryTime time.Time
shareBandwidth int
errMsg string
connectTime time.Time
fromToken uint64
linkMode string
isUnderlayServer int // TODO: bool?
}
// TODO: add loglevel, maxlogfilesize
@@ -104,10 +105,16 @@ func (c *Config) save() {
}
}
func init() {
gConf.LogLevel = 1
gConf.Network.ShareBandwidth = 10
gConf.Network.ServerHost = "api.openp2p.cn"
gConf.Network.ServerPort = WsPort
}
func (c *Config) load() error {
c.mtx.Lock()
c.LogLevel = IntValueNotSet
c.Network.ShareBandwidth = IntValueNotSet
defer c.mtx.Unlock()
data, err := ioutil.ReadFile("config.json")
if err != nil {
@@ -148,13 +155,12 @@ type NetworkConfig struct {
Node string
User string
localIP string
ipv6 string
mac string
os string
publicIP string
natType int
hasIPv4 int
IPv6 string
publicIPv6 string // must lowwer-case not save json
hasUPNPorNATPMP int
ShareBandwidth int
// server info
@@ -162,11 +168,13 @@ type NetworkConfig struct {
ServerPort int
UDPPort1 int
UDPPort2 int
TCPPort int
}
func parseParams(subCommand string) {
fset := flag.NewFlagSet(subCommand, flag.ExitOnError)
serverHost := fset.String("serverhost", "api.openp2p.cn", "server host ")
serverPort := fset.Int("serverport", WsPort, "server port ")
// serverHost := flag.String("serverhost", "127.0.0.1", "server host ") // for debug
token := fset.Uint64("token", 0, "token")
node := fset.String("node", "", "node name. 8-31 characters. if not set, it will be hostname")
@@ -174,13 +182,14 @@ func parseParams(subCommand string) {
dstIP := fset.String("dstip", "127.0.0.1", "destination ip ")
dstPort := fset.Int("dstport", 0, "destination port ")
srcPort := fset.Int("srcport", 0, "source port ")
tcpPort := fset.Int("tcpport", 0, "tcp port for upnp or publicip")
protocol := fset.String("protocol", "tcp", "tcp or udp")
appName := fset.String("appname", "", "app name")
shareBandwidth := fset.Int("sharebandwidth", 10, "N mbps share bandwidth limit, private network no limit")
daemonMode := fset.Bool("d", false, "daemonMode")
notVerbose := fset.Bool("nv", false, "not log console")
newconfig := fset.Bool("newconfig", false, "not load existing config.json")
logLevel := fset.Int("loglevel", 1, "0:debug 1:info 2:warn 3:error")
logLevel := fset.Int("loglevel", 0, "0:info 1:warn 2:error 3:debug")
if subCommand == "" { // no subcommand
fset.Parse(os.Args[1:])
} else {
@@ -197,7 +206,6 @@ func parseParams(subCommand string) {
if !*newconfig {
gConf.load() // load old config. otherwise will clear all apps
}
gConf.LogLevel = *logLevel
if config.SrcPort != 0 {
gConf.add(config, true)
}
@@ -217,16 +225,19 @@ func parseParams(subCommand string) {
if f.Name == "loglevel" {
gConf.LogLevel = *logLevel
}
if f.Name == "tcpport" {
gConf.Network.TCPPort = *tcpPort
}
if f.Name == "token" {
gConf.Network.Token = *token
}
})
if gConf.Network.ServerHost == "" {
gConf.Network.ServerHost = *serverHost
}
if *token != 0 {
gConf.Network.Token = *token
}
if *node != "" {
if len(*node) < 8 {
if len(*node) < MinNodeNameLen {
gLog.Println(LvERROR, ErrNodeTooShort)
os.Exit(9)
}
@@ -236,16 +247,17 @@ func parseParams(subCommand string) {
gConf.Network.Node = defaultNodeName()
}
}
if gConf.LogLevel == IntValueNotSet {
gConf.LogLevel = *logLevel
}
if gConf.Network.ShareBandwidth == IntValueNotSet {
gConf.Network.ShareBandwidth = *shareBandwidth
if gConf.Network.TCPPort == 0 {
if *tcpPort == 0 {
p := int(nodeNameToID(gConf.Network.Node)%15000 + 50000)
tcpPort = &p
}
gConf.Network.TCPPort = *tcpPort
}
gConf.Network.ServerPort = 27183
gConf.Network.UDPPort1 = 27182
gConf.Network.UDPPort2 = 27183
gConf.Network.ServerPort = *serverPort
gConf.Network.UDPPort1 = UDPPort1
gConf.Network.UDPPort2 = UDPPort2
gLog.setLevel(LogLevel(gConf.LogLevel))
if *notVerbose {
gLog.setMode(LogFile)

View File

@@ -1,10 +1,9 @@
{
"network": {
"Node": "YOUR_NODE_NAME",
"User": "YOUR_USER_NAME",
"Password": "YOUR_PASSWORD",
"ServerHost": "openp2p.cn",
"ServerPort": 27182,
"Token": "YOUR_TOKEN",
"ServerHost": "api.openp2p.cn",
"ServerPort": 27183,
"UDPPort1": 27182,
"UDPPort2": 27183
},

BIN
doc/images/p2p-debug.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@@ -0,0 +1,73 @@
# 1. 环境
golang1.18.1
调试端: win10+vscode
被调试端: ubuntu20.04LTS+dlv
## dlv安装
参考官方文档https://github.com/go-delve/delve/tree/master/Documentation/installation
```
go install github.com/go-delve/delve/cmd/dlv@latest
```
## 被调试端
```
cd /your-src-path #要确保两端源码一致
dlv debug --headless --listen=:2345 --api-version=2
# 如果失败可查看更多日志
# dlv debug --headless --listen=:2345 --api-version=2 --log --log-output=rpc,dap,debugger
```
## 调试端vscode
```
打开vscode修改launch.json增加下面远程调试配置
{
"version": "0.2.0",
"configurations": [
{
"name": "RemoteDebug",
"type": "go",
"request": "attach",
"mode": "remote",
"port": 2345,
"host": "192.168.3.29",
}
]
}
按F5启动调试
```
## 没有公网IP或不在同一个局域网无法直连如何调试
到https://openp2p.cn/注册一个用户获得token两端安装一个客户端程序可将被调试端的2345端口通过p2p连接映射到调试端本地。
p2p连接可通过web配置 https://github.com/openp2p-cn/openp2p/blob/master/README-ZH.md#%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8
也可以手动下载https://github.com/openp2p-cn/openp2p/releases 通过命令行手动配置
```
# 注意替换下面YOUR-开头的参数改成自己的
./openp2p -node YOUR-DEBUG-SERVER -token YOUR-TOKEN
openp2p.exe -node YOUR-DEBUG-CLIENT -token YOUR-TOKEN -peernode YOUR-DEBUG-SERVER -dstport 2345 -srcport 2345
2022/04/22 11:07:26 25680 INFO LISTEN ON PORT tcp:2345 START
#显示这条日志说明成功了
```
p2p端口映射完成后把vscode的配置改成本地127.0.0.1,一样可以顺利调试。
```
{
"version": "0.2.0",
"configurations": [
{
"name": "RemoteDebug",
"type": "go",
"request": "attach",
"mode": "remote",
"port": 2345,
"host": "127.0.0.1",
}
]
}
```
# 参考
https://github.com/golang/vscode-go/blob/master/docs/debugging.md#remote-debugging

50
doc/remote-debug-vscpp.md Normal file
View File

@@ -0,0 +1,50 @@
# 1. 环境
visual studio 2022
调试端: win10
被调试端: win10
## 程序编译
一般远程调试我会选择release版本然后将优化去掉即可这样更接近真实版本。
![image](/doc/images/release-debug.png)
```
go install github.com/go-delve/delve/cmd/dlv@latest
```
## 运行远程调试器
C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\Remote Debugger 拷贝到目标机器
如果调试x64程序则cd到Remote Debugger\x64目录
管理员方式打开cmd执行
```
netsh advfirewall set allprofiles state off
msvsmon.exe /noauth /anyuser /silent
```
## visual studio
Attach远程进程按下Ctrl+Atl+P
![image](/doc/images/vs2022-remote-debug-attach.png)
## 没有公网IP或不在同一个局域网无法直连如何调试
到 https://openp2p.cn/ 注册一个用户获得token两端安装一个客户端程序可将被调试端的2345端口通过p2p连接映射到调试端本地。
p2p连接可通过web配置 https://github.com/openp2p-cn/openp2p/blob/master/README-ZH.md#%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8
也可以手动下载https://github.com/openp2p-cn/openp2p/releases 通过命令行手动配置
```
# 注意替换下面YOUR-开头的参数改成自己的
./openp2p -node YOUR-DEBUG-SERVER -token YOUR-TOKEN
openp2p.exe -node YOUR-DEBUG-CLIENT -token YOUR-TOKEN -peernode YOUR-DEBUG-SERVER -dstport 4026 -srcport 4026
2022/04/22 11:07:26 25680 INFO LISTEN ON PORT tcp:4026 START
#显示这条日志说明成功了
```
![image](/doc/images/p2p-debug.png)
可以顺利远程调试
```

View File

@@ -8,9 +8,12 @@ import (
var (
// ErrorS2S string = "s2s is not supported"
// ErrorHandshake string = "handshake error"
ErrorS2S = errors.New("s2s is not supported")
ErrorHandshake = errors.New("handshake error")
ErrorNewUser = errors.New("new user")
ErrorLogin = errors.New("user or password not correct")
ErrNodeTooShort = errors.New("node name too short, it must >=8 charaters")
ErrorS2S = errors.New("s2s is not supported")
ErrorHandshake = errors.New("handshake error")
ErrorNewUser = errors.New("new user")
ErrorLogin = errors.New("user or password not correct")
ErrNodeTooShort = errors.New("node name too short, it must >=8 charaters")
ErrPeerOffline = errors.New("peer offline")
ErrMsgFormat = errors.New("message format wrong")
ErrVersionNotCompatible = errors.New("version not compatible")
)

3
go.mod
View File

@@ -6,7 +6,8 @@ require (
github.com/gorilla/websocket v1.4.2
github.com/kardianos/service v1.2.0
github.com/lucas-clemente/quic-go v0.27.0
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34
github.com/openp2p-cn/go-reuseport v0.3.2
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150
)
require (

View File

@@ -19,7 +19,7 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
}
gLog.Printf(LvDEBUG, "handle push msg type:%d, push header:%+v", subType, pushHead)
switch subType {
case MsgPushConnectReq:
case MsgPushConnectReq: // TODO: handle a msg move to a new function
req := PushConnectReq{}
err := json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req)
if err != nil {
@@ -28,10 +28,20 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
}
gLog.Printf(LvINFO, "%s is connecting...", req.From)
gLog.Println(LvDEBUG, "push connect response to ", req.From)
if compareVersion(req.Version, LeastSupportVersion) == LESS {
gLog.Println(LvERROR, ErrVersionNotCompatible.Error(), ":", req.From)
rsp := PushConnectRsp{
Error: 10,
Detail: ErrVersionNotCompatible.Error(),
To: req.From,
From: pn.config.Node,
}
pn.push(req.From, MsgPushConnectRsp, rsp)
return ErrVersionNotCompatible
}
// verify totp token or token
if VerifyTOTP(req.Token, pn.config.Token, time.Now().Unix()+(pn.serverTs-pn.localTs)) || // localTs may behind, auto adjust ts
VerifyTOTP(req.Token, pn.config.Token, time.Now().Unix()) ||
(req.FromToken == pn.config.Token) {
VerifyTOTP(req.Token, pn.config.Token, time.Now().Unix()) {
gLog.Printf(LvINFO, "Access Granted\n")
config := AppConfig{}
config.peerNatType = req.NatType
@@ -39,8 +49,14 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
config.peerIP = req.FromIP
config.PeerNode = req.From
config.peerVersion = req.Version
config.fromToken = req.Token
config.peerIPv6 = req.IPv6
config.hasIPv4 = req.HasIPv4
config.hasUPNPorNATPMP = req.HasUPNPorNATPMP
config.linkMode = req.LinkMode
config.isUnderlayServer = req.IsUnderlayServer
// share relay node will limit bandwidth
if req.FromToken != pn.config.Token {
if req.Token != pn.config.Token {
gLog.Printf(LvINFO, "set share bandwidth %d mbps", pn.config.ShareBandwidth)
config.shareBandwidth = pn.config.ShareBandwidth
}
@@ -97,7 +113,7 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
SaveKey(req.AppID, req.AppKey)
case MsgPushUpdate:
gLog.Println(LvINFO, "MsgPushUpdate")
update() // download new version first, then exec ./openp2p update
update(pn.config.ServerHost, pn.config.ServerPort) // download new version first, then exec ./openp2p update
targetPath := filepath.Join(defaultInstallPath, defaultBinName)
args := []string{"update"}
env := os.Environ()
@@ -124,6 +140,7 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
appActive := 0
relayNode := ""
relayMode := ""
linkMode := LinkModeUDPPunch
i, ok := pn.apps.Load(fmt.Sprintf("%s%d", config.Protocol, config.SrcPort))
if ok {
app := i.(*p2pApp)
@@ -132,6 +149,7 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
}
relayNode = app.relayNode
relayMode = app.relayMode
linkMode = app.tunnel.linkModeWeb
}
appInfo := AppInfo{
AppName: config.AppName,
@@ -140,6 +158,7 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
SrcPort: config.SrcPort,
RelayNode: relayNode,
RelayMode: relayMode,
LinkMode: linkMode,
PeerNode: config.PeerNode,
DstHost: config.DstHost,
DstPort: config.DstPort,
@@ -154,6 +173,49 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
req.Apps = append(req.Apps, appInfo)
}
pn.write(MsgReport, MsgReportApps, &req)
case MsgPushReportLog:
gLog.Println(LvINFO, "MsgPushReportLog")
req := ReportLogReq{}
err := json.Unmarshal(msg[openP2PHeaderSize:], &req)
if err != nil {
gLog.Printf(LvERROR, "wrong MsgPushReportLog:%s %s", err, string(msg[openP2PHeaderSize:]))
return err
}
if req.FileName == "" {
req.FileName = "openp2p.log"
}
f, err := os.Open(filepath.Join("log", req.FileName))
if err != nil {
gLog.Println(LvERROR, "read log file error:", err)
break
}
fi, err := f.Stat()
if err != nil {
break
}
if req.Offset == 0 && fi.Size() > 4096 {
req.Offset = fi.Size() - 4096
}
if req.Len <= 0 {
req.Len = 4096
}
f.Seek(req.Offset, 0)
if req.Len > 1024*1024 { // too large
break
}
buff := make([]byte, req.Len)
readLength, err := f.Read(buff)
f.Close()
if err != nil {
gLog.Println(LvERROR, "read log content error:", err)
break
}
rsp := ReportLogRsp{}
rsp.Content = string(buff[:readLength])
rsp.FileName = req.FileName
rsp.Total = fi.Size()
rsp.Len = req.Len
pn.write(MsgReport, MsgPushReportLog, &rsp)
case MsgPushEditApp:
gLog.Println(LvINFO, "MsgPushEditApp")
newApp := AppInfo{}

View File

@@ -12,7 +12,7 @@ import (
func handshakeC2C(t *P2PTunnel) (err error) {
gLog.Printf(LvDEBUG, "handshakeC2C %s:%d:%d to %s:%d", t.pn.config.Node, t.coneLocalPort, t.coneNatPort, t.config.peerIP, t.config.peerConeNatPort)
defer gLog.Printf(LvDEBUG, "handshakeC2C ok")
defer gLog.Printf(LvDEBUG, "handshakeC2C end")
conn, err := net.ListenUDP("udp", t.la)
if err != nil {
return err
@@ -45,6 +45,7 @@ func handshakeC2C(t *P2PTunnel) (err error) {
}
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
gLog.Printf(LvDEBUG, "read %d handshake ack ", t.id)
gLog.Printf(LvINFO, "handshakeC2C ok")
return nil
}
}
@@ -54,9 +55,10 @@ func handshakeC2C(t *P2PTunnel) (err error) {
_, err = UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
if err != nil {
gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err)
return err
}
return err
}
gLog.Printf(LvINFO, "handshakeC2C ok")
return nil
}
@@ -115,6 +117,7 @@ func handshakeC2S(t *P2PTunnel) error {
_, err = UDPWrite(conn, dst, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
return err
}
gLog.Printf(LvINFO, "handshakeC2S ok")
return nil
}
@@ -175,6 +178,7 @@ func handshakeS2C(t *P2PTunnel) error {
return fmt.Errorf("wait handshake failed")
case la := <-gotCh:
gLog.Println(LvDEBUG, "symmetric handshake ok", la)
gLog.Printf(LvINFO, "handshakeS2C ok")
}
return nil
}

View File

@@ -17,7 +17,7 @@ import (
// ./openp2p install -node hhd1207-222 -token YOUR-TOKEN -sharebandwidth 0 -peernode hhdhome-n1 -dstip 127.0.0.1 -dstport 50022 -protocol tcp -srcport 22
func install() {
gLog.Println(LvINFO, "openp2p start. version: ", OpenP2PVersion)
gLog.Println(LvINFO, "Contact: QQ Group: 16947733, Email: openp2p.cn@gmail.com")
gLog.Println(LvINFO, "Contact: QQ: 477503927, Email: openp2p.cn@gmail.com")
gLog.Println(LvINFO, "install start")
defer gLog.Println(LvINFO, "install end")
// auto uninstall
@@ -102,6 +102,7 @@ func installByFilename() {
return
}
gLog.Println(LvINFO, "install end")
gLog.Println(LvINFO, "Visit WebUI on https://console.openp2p.cn")
fmt.Println("Press the Any Key to exit")
fmt.Scanln()
os.Exit(0)

92
nat.go
View File

@@ -6,12 +6,55 @@ import (
"log"
"math/rand"
"net"
"strconv"
"strings"
"time"
reuse "github.com/openp2p-cn/go-reuseport"
)
var echoConn *net.UDPConn
func natTCP(serverHost string, serverPort int, localPort int) (publicIP string, publicPort int) {
// dialer := &net.Dialer{
// LocalAddr: &net.TCPAddr{
// IP: net.ParseIP("0.0.0.0"),
// Port: localPort,
// },
// }
conn, err := reuse.DialTimeout("tcp4", fmt.Sprintf("%s:%d", "0.0.0.0", localPort), fmt.Sprintf("%s:%d", serverHost, serverPort), time.Second*5)
// conn, err := net.Dial("tcp4", fmt.Sprintf("%s:%d", serverHost, serverPort))
if err != nil {
fmt.Printf("Dial tcp4 %s:%d error:%s", serverHost, serverPort, err)
return
}
defer conn.Close()
_, wrerr := conn.Write([]byte("1"))
if wrerr != nil {
fmt.Printf("Write error: %s\n", wrerr)
return
}
b := make([]byte, 1000)
conn.SetReadDeadline(time.Now().Add(time.Second * 5))
n, rderr := conn.Read(b)
if rderr != nil {
fmt.Printf("Read error: %s\n", rderr)
return
}
arr := strings.Split(string(b[:n]), ":")
if len(arr) < 2 {
return
}
publicIP = arr[0]
port, _ := strconv.ParseInt(arr[1], 10, 32)
publicPort = int(port)
return
}
func natTest(serverHost string, serverPort int, localPort int, echoPort int) (publicIP string, hasPublicIP int, hasUPNPorNATPMP int, publicPort int, err error) {
conn, err := net.ListenPacket("udp", fmt.Sprintf(":%d", localPort))
if err != nil {
gLog.Println(LvERROR, "natTest listen udp error:", err)
return "", 0, 0, 0, err
}
defer conn.Close()
@@ -48,7 +91,7 @@ func natTest(serverHost string, serverPort int, localPort int, echoPort int) (pu
if i == 1 {
// test upnp or nat-pmp
nat, err := Discover()
if err != nil {
if err != nil || nat == nil {
gLog.Println(LvDEBUG, "could not perform UPNP discover:", err)
break
}
@@ -59,10 +102,12 @@ func natTest(serverHost string, serverPort int, localPort int, echoPort int) (pu
}
log.Println("PublicIP:", ext)
externalPort, err := nat.AddPortMapping("udp", echoPort, echoPort, "openp2p", 60)
externalPort, err := nat.AddPortMapping("udp", echoPort, echoPort, "openp2p", 30)
if err != nil {
gLog.Println(LvDEBUG, "could not add udp UPNP port mapping", externalPort)
break
} else {
nat.AddPortMapping("tcp", echoPort, echoPort, "openp2p", 604800)
}
}
gLog.Printf(LvDEBUG, "public ip test start %s:%d", natRsp.IP, echoPort)
@@ -96,48 +141,53 @@ func natTest(serverHost string, serverPort int, localPort int, echoPort int) (pu
return natRsp.IP, hasPublicIP, hasUPNPorNATPMP, natRsp.Port, nil
}
func getNATType(host string, udp1 int, udp2 int) (publicIP string, NATType int, hasUPNPorNATPMP int, err error) {
func getNATType(host string, udp1 int, udp2 int) (publicIP string, NATType int, hasIPvr int, hasUPNPorNATPMP int, err error) {
// the random local port may be used by other.
localPort := int(rand.Uint32()%10000 + 50000)
echoPort := int(rand.Uint32()%10000 + 50000)
localPort := int(rand.Uint32()%15000 + 50000)
echoPort := P2PNetworkInstance(nil).config.TCPPort
go echo(echoPort)
ip1, hasPublicIP, hasUPNPorNATPMP, port1, err := natTest(host, udp1, localPort, echoPort)
// _, natPort := natTCP(host, 27181, localPort)
// gLog.Println(LvINFO, "nattcp:", natPort)
// _, natPort = natTCP(host, 27180, localPort)
// gLog.Println(LvINFO, "nattcp:", natPort)
ip1, hasIPv4, hasUPNPorNATPMP, port1, err := natTest(host, udp1, localPort, echoPort)
gLog.Printf(LvDEBUG, "local port:%d nat port:%d", localPort, port1)
if err != nil {
return "", 0, hasUPNPorNATPMP, err
return "", 0, hasIPv4, hasUPNPorNATPMP, err
}
if hasPublicIP == 1 || hasUPNPorNATPMP == 1 {
return ip1, NATNone, hasUPNPorNATPMP, nil
if echoConn != nil {
echoConn.Close()
echoConn = nil
}
ip2, _, _, port2, err := natTest(host, udp2, localPort, 0) // 2rd nat test not need testing publicip
// if hasPublicIP == 1 || hasUPNPorNATPMP == 1 {
// return ip1, NATNone, hasUPNPorNATPMP, nil
// }
_, _, _, port2, err := natTest(host, udp2, localPort, 0) // 2rd nat test not need testing publicip
gLog.Printf(LvDEBUG, "local port:%d nat port:%d", localPort, port2)
if err != nil {
return "", 0, hasUPNPorNATPMP, err
}
if ip1 != ip2 {
return "", 0, hasUPNPorNATPMP, fmt.Errorf("ip have changed, please retry again")
return "", 0, hasIPv4, hasUPNPorNATPMP, err
}
natType := NATSymmetric
if port1 == port2 {
natType = NATCone
}
//TODO: NATNone
return ip1, natType, hasUPNPorNATPMP, nil
return ip1, natType, hasIPv4, hasUPNPorNATPMP, nil
}
func echo(echoPort int) {
conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: echoPort})
var err error
echoConn, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: echoPort})
if err != nil {
gLog.Println(LvERROR, "echo server listen error:", err)
return
}
buf := make([]byte, 1600)
defer conn.Close()
// close outside for breaking the ReadFromUDP
// wait 5s for echo testing
conn.SetReadDeadline(time.Now().Add(time.Second * 30))
n, addr, err := conn.ReadFromUDP(buf)
echoConn.SetReadDeadline(time.Now().Add(time.Second * 30))
n, addr, err := echoConn.ReadFromUDP(buf)
if err != nil {
return
}
conn.WriteToUDP(buf[0:n], addr)
echoConn.WriteToUDP(buf[0:n], addr)
}

View File

@@ -42,7 +42,7 @@ func main() {
}
parseParams("")
gLog.Println(LvINFO, "openp2p start. version: ", OpenP2PVersion)
gLog.Println(LvINFO, "Contact: QQ Group: 16947733, Email: openp2p.cn@gmail.com")
gLog.Println(LvINFO, "Contact: QQ: 477503927, Email: openp2p.cn@gmail.com")
if gConf.daemonMode {
d := daemon{}

View File

@@ -9,6 +9,7 @@ import (
"fmt"
"math"
"math/rand"
"net/http"
"net/url"
"strings"
"sync"
@@ -135,6 +136,7 @@ func (pn *P2PNetwork) runAll() {
}
config.nextRetryTime = time.Now().Add(time.Second * time.Duration(increase)) // exponential increase retry time. 1.3^x
config.connectTime = time.Now()
config.peerToken = pn.config.Token
gConf.mtx.Unlock() // AddApp will take a period of time
err := pn.AddApp(*config)
gConf.mtx.Lock()
@@ -159,6 +161,7 @@ func (pn *P2PNetwork) autorunApp() {
func (pn *P2PNetwork) addRelayTunnel(config AppConfig) (*P2PTunnel, uint64, string, error) {
gLog.Printf(LvINFO, "addRelayTunnel to %s start", config.PeerNode)
defer gLog.Printf(LvINFO, "addRelayTunnel to %s end", config.PeerNode)
// request a relay node or specify manually(TODO)
pn.write(MsgRelay, MsgRelayNodeReq, &RelayNodeReq{config.PeerNode})
head, body := pn.read("", MsgRelay, MsgRelayNodeRsp, time.Second*10)
if head == nil {
@@ -178,6 +181,7 @@ func (pn *P2PNetwork) addRelayTunnel(config AppConfig) (*P2PTunnel, uint64, stri
relayConfig := config
relayConfig.PeerNode = rsp.RelayName
relayConfig.peerToken = rsp.RelayToken
///
t, err := pn.addDirectTunnel(relayConfig, 0)
if err != nil {
gLog.Println(LvERROR, "direct connect error:", err)
@@ -236,6 +240,8 @@ func (pn *P2PNetwork) AddApp(config AppConfig) error {
peerNatType = t.config.peerNatType
peerIP = t.config.peerIP
}
// TODO: if tcp failed, should try udp punching, nattype should refactor also, when NATNONE and failed we don't know the peerNatType
if err != nil && err == ErrorHandshake {
gLog.Println(LvERROR, "direct connect failed, try to relay")
t, rtid, relayMode, err = pn.addRelayTunnel(config)
@@ -340,46 +346,113 @@ func (pn *P2PNetwork) addDirectTunnel(config AppConfig, tid uint64) (*P2PTunnel,
}
return true
})
if exist {
return t, nil
}
// create tunnel if not exist
if !exist {
t = &P2PTunnel{pn: pn,
config: config,
id: tid,
t = &P2PTunnel{pn: pn,
config: config,
id: tid,
}
pn.msgMapMtx.Lock()
pn.msgMap[nodeNameToID(config.PeerNode)] = make(chan []byte, 50)
pn.msgMapMtx.Unlock()
// server side
if !isClient {
err := pn.newTunnel(t, tid, isClient)
return t, err // always return
}
// client side
// peer info
initErr := t.requestPeerInfo()
if initErr != nil {
gLog.Println(LvERROR, "init error:", initErr)
return nil, initErr
}
err := ErrorHandshake
// try TCP6
if IsIPv6(t.config.peerIPv6) && IsIPv6(t.pn.config.publicIPv6) {
gLog.Println(LvINFO, "try TCP6")
t.config.linkMode = LinkModeTCP6
t.config.isUnderlayServer = 0
if err = pn.newTunnel(t, tid, isClient); err == nil {
return t, nil
}
pn.msgMapMtx.Lock()
pn.msgMap[nodeNameToID(config.PeerNode)] = make(chan []byte, 50)
pn.msgMapMtx.Unlock()
t.init()
if isClient {
if err := t.connect(); err != nil {
gLog.Println(LvERROR, "p2pTunnel connect error:", err)
return t, err
}
}
// TODO: try UDP6
// try TCP4
if t.config.hasIPv4 == 1 || t.pn.config.hasIPv4 == 1 || t.config.hasUPNPorNATPMP == 1 || t.pn.config.hasUPNPorNATPMP == 1 {
gLog.Println(LvINFO, "try TCP4")
t.config.linkMode = LinkModeTCP4
if t.config.hasIPv4 == 1 || t.config.hasUPNPorNATPMP == 1 {
t.config.isUnderlayServer = 0
} else {
if err := t.listen(); err != nil {
gLog.Println(LvERROR, "p2pTunnel listen error:", err)
return t, err
}
t.config.isUnderlayServer = 1
}
if err = pn.newTunnel(t, tid, isClient); err == nil {
return t, nil
}
}
// TODO: try UDP4
// try TCPPunch
if t.config.peerNatType == NATCone && t.pn.config.natType == NATCone { // TODO: support c2s
gLog.Println(LvINFO, "try TCP4 Punch")
t.config.linkMode = LinkModeTCPPunch
t.config.isUnderlayServer = 0
if err = pn.newTunnel(t, tid, isClient); err == nil {
return t, nil
}
}
// try UDPPunch
if t.config.peerNatType == NATCone || t.pn.config.natType == NATCone {
gLog.Println(LvINFO, "try UDP4 Punch")
t.config.linkMode = LinkModeUDPPunch
t.config.isUnderlayServer = 0
if err = pn.newTunnel(t, tid, isClient); err == nil {
return t, nil
}
}
return nil, err
}
func (pn *P2PNetwork) newTunnel(t *P2PTunnel, tid uint64, isClient bool) error {
t.initPort()
if isClient {
if err := t.connect(); err != nil {
gLog.Println(LvERROR, "p2pTunnel connect error:", err)
return err
}
} else {
if err := t.listen(); err != nil {
gLog.Println(LvERROR, "p2pTunnel listen error:", err)
return err
}
}
// store it when success
gLog.Printf(LvDEBUG, "store tunnel %d", tid)
pn.allTunnels.Store(tid, t)
return t, nil
return nil
}
func (pn *P2PNetwork) init() error {
gLog.Println(LvINFO, "init start")
var err error
for {
// detect nat type
pn.config.publicIP, pn.config.natType, pn.config.hasUPNPorNATPMP, err = getNATType(pn.config.ServerHost, pn.config.UDPPort1, pn.config.UDPPort2)
pn.config.publicIP, pn.config.natType, pn.config.hasIPv4, pn.config.hasUPNPorNATPMP, err = getNATType(pn.config.ServerHost, pn.config.UDPPort1, pn.config.UDPPort2)
// for testcase
if strings.Contains(pn.config.Node, "openp2pS2STest") {
pn.config.natType = NATSymmetric
pn.config.hasIPv4 = 0
pn.config.hasUPNPorNATPMP = 0
}
if strings.Contains(pn.config.Node, "openp2pC2CTest") {
pn.config.natType = NATCone
pn.config.hasIPv4 = 0
pn.config.hasUPNPorNATPMP = 0
}
if err != nil {
gLog.Println(LvDEBUG, "detect NAT type error:", err)
@@ -387,10 +460,10 @@ func (pn *P2PNetwork) init() error {
}
gLog.Println(LvDEBUG, "detect NAT type:", pn.config.natType, " publicIP:", pn.config.publicIP)
gatewayURL := fmt.Sprintf("%s:%d", pn.config.ServerHost, pn.config.ServerPort)
forwardPath := "/openp2p/v1/login"
uri := "/openp2p/v1/login"
config := tls.Config{InsecureSkipVerify: true} // 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
u := url.URL{Scheme: "wss", Host: gatewayURL, Path: forwardPath}
u := url.URL{Scheme: "wss", Host: gatewayURL, Path: uri}
q := u.Query()
q.Add("node", pn.config.Node)
q.Add("token", fmt.Sprintf("%d", pn.config.Token))
@@ -429,7 +502,7 @@ func (pn *P2PNetwork) init() error {
gLog.Println(LvDEBUG, "netinfo:", rsp)
if rsp != nil && rsp.Country != "" {
if IsIPv6(rsp.IP.String()) {
pn.config.ipv6 = rsp.IP.String()
pn.config.publicIPv6 = rsp.IP.String()
req.IPv6 = rsp.IP.String()
}
req.NetInfo = *rsp
@@ -471,9 +544,12 @@ func (pn *P2PNetwork) handleMessage(t int, msg []byte) {
pn.config.User = rsp.User
gConf.setToken(rsp.Token)
gConf.setUser(rsp.User)
if len(rsp.Node) >= MinNodeNameLen {
gConf.setNode(rsp.Node)
}
gConf.save()
pn.localTs = time.Now().Unix()
gLog.Printf(LvINFO, "login ok. user=%s,Server ts=%d, local ts=%d", rsp.User, rsp.Ts, pn.localTs)
gLog.Printf(LvINFO, "login ok. user=%s,node=%s,Server ts=%d, local ts=%d", rsp.User, rsp.Node, rsp.Ts, pn.localTs)
}
case MsgHeartbeat:
gLog.Printf(LvDEBUG, "P2PNetwork heartbeat ok")
@@ -611,3 +687,23 @@ func (pn *P2PNetwork) updateAppHeartbeat(appID uint64) {
return false
})
}
func (pn *P2PNetwork) refreshIPv6() {
if !IsIPv6(pn.config.publicIPv6) { // not support ipv6, not refresh
return
}
client := &http.Client{Timeout: time.Second * 10}
r, err := client.Get("http://6.ipw.cn")
if err != nil {
gLog.Println(LvINFO, "refreshIPv6 error:", err)
return
}
defer r.Body.Close()
buf := make([]byte, 1024)
n, err := r.Body.Read(buf)
if n <= 0 {
gLog.Println(LvINFO, "netInfo error:", err, n)
return
}
pn.config.publicIPv6 = string(buf[:n])
}

View File

@@ -28,37 +28,64 @@ type P2PTunnel struct {
tunnelServer bool // different from underlayServer
coneLocalPort int
coneNatPort int
linkModeWeb string // use config.linkmode
}
func (t *P2PTunnel) init() {
func (t *P2PTunnel) requestPeerInfo() error {
// request peer info
t.pn.write(MsgQuery, MsgQueryPeerInfoReq, &QueryPeerInfoReq{t.config.peerToken, t.config.PeerNode})
head, body := t.pn.read("", MsgQuery, MsgQueryPeerInfoRsp, time.Second*10)
if head == nil {
return ErrPeerOffline
}
rsp := QueryPeerInfoRsp{}
err := json.Unmarshal(body, &rsp)
if err != nil {
gLog.Printf(LvERROR, "wrong QueryPeerInfoRsp:%s", err)
return ErrMsgFormat
}
if rsp.Online == 0 {
return ErrPeerOffline
}
if compareVersion(rsp.Version, LeastSupportVersion) == LESS {
return ErrVersionNotCompatible
}
t.config.peerVersion = rsp.Version
t.config.hasIPv4 = rsp.HasIPv4
t.config.peerIP = rsp.IPv4
t.config.peerIPv6 = rsp.IPv6
t.config.hasUPNPorNATPMP = rsp.HasUPNPorNATPMP
t.config.peerNatType = rsp.NatType
///
return nil
}
func (t *P2PTunnel) initPort() {
t.running = true
t.hbMtx.Lock()
t.hbTime = time.Now()
t.hbMtx.Unlock()
t.hbTimeRelay = time.Now().Add(time.Second * 600) // TODO: test fake time
localPort := int(rand.Uint32()%10000 + 50000)
if t.pn.config.natType == NATCone {
// prepare one random cone hole
_, _, _, port1, _ := natTest(t.pn.config.ServerHost, t.pn.config.UDPPort1, localPort, 0)
t.coneLocalPort = localPort
t.coneNatPort = port1
t.la = &net.UDPAddr{IP: net.ParseIP(t.pn.config.localIP), Port: t.coneLocalPort}
} else {
t.coneLocalPort = localPort
t.coneNatPort = localPort // symmetric doesn't need coneNatPort
if t.pn.config.hasUPNPorNATPMP == 1 {
nat, err := Discover()
if err != nil {
gLog.Println(LvDEBUG, "could not perform UPNP discover:", err)
} else {
externalPort, err := nat.AddPortMapping("tcp", localPort, localPort, "openp2p", 30) // timeout the connection still alive, make the timeout short
if err != nil {
gLog.Println(LvDEBUG, "could not add udp UPNP port mapping", externalPort)
}
}
}
t.la = &net.UDPAddr{IP: net.ParseIP(t.pn.config.localIP), Port: t.coneLocalPort}
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.pn.refreshIPv6()
}
if t.config.linkMode == LinkModeTCP6 || t.config.linkMode == LinkModeTCP4 {
t.coneLocalPort = t.pn.config.TCPPort
t.coneNatPort = t.pn.config.TCPPort // symmetric doesn't need coneNatPort
}
if t.config.linkMode == LinkModeUDPPunch {
// prepare one random cone hole
_, _, _, natPort, _ := natTest(t.pn.config.ServerHost, t.pn.config.UDPPort1, localPort, 0)
t.coneLocalPort = localPort
t.coneNatPort = natPort
}
if t.config.linkMode == LinkModeTCPPunch {
// prepare one random cone hole
_, natPort := natTCP(t.pn.config.ServerHost, IfconfigPort1, localPort)
t.coneLocalPort = localPort
t.coneNatPort = natPort
}
t.la = &net.UDPAddr{IP: net.ParseIP(t.pn.config.localIP), Port: t.coneLocalPort}
gLog.Printf(LvDEBUG, "prepare punching port %d:%d", t.coneLocalPort, t.coneNatPort)
}
@@ -67,18 +94,22 @@ func (t *P2PTunnel) connect() error {
t.tunnelServer = false
appKey := uint64(0)
req := PushConnectReq{
Token: t.config.peerToken,
From: t.pn.config.Node,
FromToken: t.pn.config.Token,
FromIP: t.pn.config.publicIP,
ConeNatPort: t.coneNatPort,
NatType: t.pn.config.natType,
HasIPv4: t.pn.config.hasIPv4,
IPv6: t.pn.config.IPv6,
HasUPNPorNATPMP: t.pn.config.hasUPNPorNATPMP,
ID: t.id,
AppKey: appKey,
Version: OpenP2PVersion,
Token: t.config.peerToken,
From: t.pn.config.Node,
FromIP: t.pn.config.publicIP,
ConeNatPort: t.coneNatPort,
NatType: t.pn.config.natType,
HasIPv4: t.pn.config.hasIPv4,
IPv6: t.pn.config.publicIPv6,
HasUPNPorNATPMP: t.pn.config.hasUPNPorNATPMP,
ID: t.id,
AppKey: appKey,
Version: OpenP2PVersion,
LinkMode: t.config.linkMode,
IsUnderlayServer: t.config.isUnderlayServer ^ 1,
}
if req.Token == 0 { // no relay token
req.Token = t.pn.config.Token
}
t.pn.push(t.config.PeerNode, MsgPushConnectReq, req)
head, body := t.pn.read(t.config.PeerNode, MsgPush, MsgPushConnectRsp, time.Second*10)
@@ -97,7 +128,7 @@ func (t *P2PTunnel) connect() error {
}
t.config.peerNatType = rsp.NatType
t.config.hasIPv4 = rsp.HasIPv4
t.config.IPv6 = rsp.IPv6
t.config.peerIPv6 = rsp.IPv6
t.config.hasUPNPorNATPMP = rsp.HasUPNPorNATPMP
t.config.peerVersion = rsp.Version
t.config.peerConeNatPort = rsp.ConeNatPort
@@ -158,7 +189,7 @@ func (t *P2PTunnel) close() {
}
func (t *P2PTunnel) start() error {
if !t.isSupportTCP() {
if t.config.linkMode == LinkModeUDPPunch {
if err := t.handshake(); err != nil {
return err
}
@@ -172,7 +203,7 @@ func (t *P2PTunnel) start() error {
}
func (t *P2PTunnel) handshake() error {
if t.config.peerConeNatPort > 0 {
if t.config.peerConeNatPort > 0 { // only peer is cone should prepare t.ra
var err error
t.ra, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", t.config.peerIP, t.config.peerConeNatPort))
if err != nil {
@@ -182,7 +213,7 @@ func (t *P2PTunnel) handshake() error {
gLog.Println(LvDEBUG, "handshake to ", t.config.PeerNode)
var err error
// TODO: handle NATNone, nodes with public ip has no punching
if (t.pn.config.natType == NATCone && t.config.peerNatType == NATCone) || (t.pn.config.natType == NATNone || t.config.peerNatType == NATNone) {
if t.pn.config.natType == NATCone && t.config.peerNatType == NATCone {
err = handshakeC2C(t)
} else if t.config.peerNatType == NATSymmetric && t.pn.config.natType == NATSymmetric {
err = ErrorS2S
@@ -203,28 +234,22 @@ func (t *P2PTunnel) handshake() error {
}
func (t *P2PTunnel) connectUnderlay() (err error) {
if !t.isSupportTCP() {
t.conn, err = t.connectUnderlayQuic()
if err != nil {
return err
}
} else {
// TODO: udp or tcp first?
// prepare a la ra for udp
// t.conn, err = t.connectUnderlayQuic()
// TODO: support ipv6
// if t.pn.config.hasIPv4 == 1 || t.config.hasIPv4 == 1 {
switch t.config.linkMode {
case LinkModeTCP6:
t.conn, err = t.connectUnderlayTCP6()
case LinkModeTCP4:
t.conn, err = t.connectUnderlayTCP()
if err != nil {
return err
}
// }
// if IsIPv6(t.pn.config.IPv6) && IsIPv6(t.config.IPv6) { // both have ipv6
// t.conn, err = t.connectUnderlayTCP6()
// if err != nil {
// return err
// }
// }
case LinkModeTCPPunch:
t.conn, err = t.connectUnderlayTCP()
case LinkModeUDPPunch:
t.conn, err = t.connectUnderlayQuic()
}
if err != nil {
return err
}
if t.conn == nil {
return errors.New("connect underlay error")
}
t.setRun(true)
go t.readLoop()
@@ -236,7 +261,7 @@ func (t *P2PTunnel) connectUnderlayQuic() (c underlay, err error) {
gLog.Println(LvINFO, "connectUnderlayQuic start")
defer gLog.Println(LvINFO, "connectUnderlayQuic end")
var qConn *underlayQUIC
if t.isUnderlayServer() {
if t.config.isUnderlayServer == 1 {
time.Sleep(time.Millisecond * 10) // punching udp port will need some times in some env
qConn, err = listenQuic(t.la.String(), TunnelIdleTimeout)
if err != nil {
@@ -289,6 +314,7 @@ func (t *P2PTunnel) connectUnderlayQuic() (c underlay, err error) {
gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin))
gLog.Println(LvDEBUG, "quic connection ok")
t.linkModeWeb = LinkModeUDPPunch
return qConn, nil
}
@@ -297,37 +323,36 @@ func (t *P2PTunnel) connectUnderlayTCP() (c underlay, err error) {
gLog.Println(LvINFO, "connectUnderlayTCP start")
defer gLog.Println(LvINFO, "connectUnderlayTCP end")
var qConn *underlayTCP
if t.isUnderlayServer() {
if t.config.isUnderlayServer == 1 {
t.pn.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
qConn, err = listenTCP(t.coneNatPort, TunnelIdleTimeout)
qConn, err = listenTCP(t.config.peerIP, t.config.peerConeNatPort, t.coneLocalPort, t.config.linkMode)
if err != nil {
return nil, fmt.Errorf("listen TCP error:%s", err)
}
_, buff, err := qConn.ReadBuffer()
if err != nil {
qConn.listener.Close()
return nil, fmt.Errorf("read start msg error:%s", err)
}
if buff != nil {
gLog.Println(LvDEBUG, string(buff))
}
qConn.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, []byte("OpenP2P,hello2"))
gLog.Println(LvDEBUG, "TCP connection ok")
gLog.Println(LvINFO, "TCP connection ok")
return qConn, nil
}
//else
t.pn.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, time.Second*5)
gLog.Println(LvDEBUG, "TCP dial to ", t.ra.String())
qConn, err = dialTCP(t.config.peerIP, t.config.peerConeNatPort)
gLog.Println(LvDEBUG, "TCP dial to ", t.config.peerIP, ":", t.config.peerConeNatPort)
qConn, err = dialTCP(t.config.peerIP, t.config.peerConeNatPort, t.coneLocalPort, t.config.linkMode)
if err != nil {
return nil, fmt.Errorf("TCP dial to %s error:%s", t.ra.String(), err)
return nil, fmt.Errorf("TCP dial to %s:%d error:%s", t.config.peerIP, t.config.peerConeNatPort, err)
}
handshakeBegin := time.Now()
qConn.WriteBytes(MsgP2P, MsgTunnelHandshake, []byte("OpenP2P,hello"))
_, buff, err := qConn.ReadBuffer()
if err != nil {
qConn.listener.Close()
return nil, fmt.Errorf("read MsgTunnelHandshake error:%s", err)
}
if buff != nil {
@@ -335,19 +360,20 @@ func (t *P2PTunnel) connectUnderlayTCP() (c underlay, err error) {
}
gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin))
gLog.Println(LvDEBUG, "TCP connection ok")
gLog.Println(LvINFO, "TCP connection ok")
t.linkModeWeb = LinkModeIPv4
return qConn, nil
}
func (t *P2PTunnel) connectUnderlayTCP6() (c underlay, err error) {
gLog.Println(LvINFO, "connectUnderlayTCP start")
defer gLog.Println(LvINFO, "connectUnderlayTCP end")
gLog.Println(LvINFO, "connectUnderlayTCP6 start")
defer gLog.Println(LvINFO, "connectUnderlayTCP6 end")
var qConn *underlayTCP6
if t.isUnderlayServer() {
if t.config.isUnderlayServer == 1 {
t.pn.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
qConn, err = listenTCP6(t.coneNatPort, TunnelIdleTimeout)
if err != nil {
return nil, fmt.Errorf("listen TCP error:%s", err)
return nil, fmt.Errorf("listen TCP6 error:%s", err)
}
_, buff, err := qConn.ReadBuffer()
if err != nil {
@@ -358,16 +384,16 @@ func (t *P2PTunnel) connectUnderlayTCP6() (c underlay, err error) {
gLog.Println(LvDEBUG, string(buff))
}
qConn.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, []byte("OpenP2P,hello2"))
gLog.Println(LvDEBUG, "TCP connection ok")
gLog.Println(LvDEBUG, "TCP6 connection ok")
return qConn, nil
}
//else
t.pn.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, time.Second*5)
gLog.Println(LvDEBUG, "TCP dial to ", t.ra.String())
qConn, err = dialTCP6(t.config.IPv6, t.config.peerConeNatPort)
gLog.Println(LvDEBUG, "TCP6 dial to ", t.config.peerIPv6)
qConn, err = dialTCP6(t.config.peerIPv6, t.config.peerConeNatPort)
if err != nil {
return nil, fmt.Errorf("TCP dial to %s error:%s", t.ra.String(), err)
return nil, fmt.Errorf("TCP6 dial to %s:%d error:%s", t.config.peerIPv6, t.config.peerConeNatPort, err)
}
handshakeBegin := time.Now()
qConn.WriteBytes(MsgP2P, MsgTunnelHandshake, []byte("OpenP2P,hello"))
@@ -381,7 +407,8 @@ func (t *P2PTunnel) connectUnderlayTCP6() (c underlay, err error) {
}
gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin))
gLog.Println(LvDEBUG, "TCP connection ok")
gLog.Println(LvDEBUG, "TCP6 connection ok")
t.linkModeWeb = LinkModeIPv6
return qConn, nil
}
@@ -551,19 +578,25 @@ func (t *P2PTunnel) heartbeatLoop() {
func (t *P2PTunnel) listen() error {
// notify client to connect
rsp := PushConnectRsp{
Error: 0,
Detail: "connect ok",
To: t.config.PeerNode,
From: t.pn.config.Node,
NatType: t.pn.config.natType,
HasIPv4: t.pn.config.hasIPv4,
IPv6: t.pn.config.IPv6,
Error: 0,
Detail: "connect ok",
To: t.config.PeerNode,
From: t.pn.config.Node,
NatType: t.pn.config.natType,
HasIPv4: t.pn.config.hasIPv4,
// IPv6: t.pn.config.IPv6,
HasUPNPorNATPMP: t.pn.config.hasUPNPorNATPMP,
FromIP: t.pn.config.publicIP,
ConeNatPort: t.coneNatPort,
ID: t.id,
Version: OpenP2PVersion,
}
// only private node set ipv6
if t.config.fromToken == t.pn.config.Token {
t.pn.refreshIPv6()
rsp.IPv6 = t.pn.config.publicIPv6
}
t.pn.push(t.config.PeerNode, MsgPushConnectRsp, rsp)
gLog.Printf(LvDEBUG, "p2ptunnel wait for connecting")
t.tunnelServer = true
@@ -586,24 +619,3 @@ func (t *P2PTunnel) closeOverlayConns(appID uint64) {
return true
})
}
func (t *P2PTunnel) isUnderlayServer() bool {
if t.pn.config.natType == NATNone && t.config.peerNatType != NATNone {
return true
}
if t.pn.config.natType != NATNone && t.config.peerNatType == NATNone {
return false
}
// NAT or both has public IP
return t.tunnelServer
}
func (t *P2PTunnel) isSupportTCP() bool {
if t.config.peerVersion == "" || compareVersion(t.config.peerVersion, LeastSupportTCPVersion) == LESS {
return false
}
if t.pn.config.natType == NATNone || t.config.peerNatType == NATNone {
return true
}
return false
}

View File

@@ -10,9 +10,17 @@ import (
"time"
)
const OpenP2PVersion = "1.5.6"
const OpenP2PVersion = "3.4.0"
const ProducnName string = "openp2p"
const LeastSupportTCPVersion = "1.5.0"
const LeastSupportVersion = "3.0.0"
const (
IfconfigPort1 = 27180
IfconfigPort2 = 27181
WsPort = 27183
UDPPort1 = 27182
UDPPort2 = 27183
)
type openP2PHeader struct {
DataLen uint32
@@ -68,6 +76,7 @@ const (
MsgP2P = 4
MsgRelay = 5
MsgReport = 6
MsgQuery = 7
)
// TODO: seperate node push and web push.
@@ -86,6 +95,7 @@ const (
MsgPushRestart = 11
MsgPushEditNode = 12
MsgPushAPPKey = 13
MsgPushReportLog = 14
)
// MsgP2P sub type message
@@ -117,6 +127,7 @@ const (
MsgReportQuery
MsgReportConnect
MsgReportApps
MsgReportLog
)
const (
@@ -137,7 +148,7 @@ const (
PublicIPEchoTimeout = time.Second * 1
NatTestTimeout = time.Second * 10
ClientAPITimeout = time.Second * 10
MaxDirectTry = 5
MaxDirectTry = 3
)
// NATNone has public ip
@@ -155,6 +166,23 @@ const (
UderlayTCP = "tcp"
)
// linkmode
const (
LinkModeUDPPunch = "udppunch"
LinkModeTCPPunch = "tcppunch"
LinkModeIPv4 = "ipv4" // for web
LinkModeIPv6 = "ipv6" // for web
LinkModeTCP6 = "tcp6"
LinkModeTCP4 = "tcp4"
LinkModeUDP6 = "udp6"
LinkModeUDP4 = "udp4"
)
const (
MsgQueryPeerInfoReq = iota
MsgQueryPeerInfoRsp
)
func newMessage(mainType uint16, subType uint16, packet interface{}) ([]byte, error) {
data, err := json.Marshal(packet)
if err != nil {
@@ -180,18 +208,20 @@ func nodeNameToID(name string) uint64 {
}
type PushConnectReq struct {
From string `json:"from,omitempty"`
FromToken uint64 `json:"fromToken,omitempty"` //my token
Version string `json:"version,omitempty"`
Token uint64 `json:"token,omitempty"` // totp token
ConeNatPort int `json:"coneNatPort,omitempty"` // if isPublic, is public port
NatType int `json:"natType,omitempty"`
HasIPv4 int `json:"hasIPv4,omitempty"`
IPv6 string `json:"IPv6,omitempty"`
HasUPNPorNATPMP int `json:"hasUPNPorNATPMP,omitempty"`
FromIP string `json:"fromIP,omitempty"`
ID uint64 `json:"id,omitempty"`
AppKey uint64 `json:"appKey,omitempty"` // for underlay tcp
From string `json:"from,omitempty"`
FromToken uint64 `json:"fromToken,omitempty"` // deprecated
Version string `json:"version,omitempty"`
Token uint64 `json:"token,omitempty"` // if public totp token
ConeNatPort int `json:"coneNatPort,omitempty"` // if isPublic, is public port
NatType int `json:"natType,omitempty"`
HasIPv4 int `json:"hasIPv4,omitempty"`
IPv6 string `json:"IPv6,omitempty"`
HasUPNPorNATPMP int `json:"hasUPNPorNATPMP,omitempty"`
FromIP string `json:"fromIP,omitempty"`
ID uint64 `json:"id,omitempty"`
AppKey uint64 `json:"appKey,omitempty"` // for underlay tcp
LinkMode string `json:"linkMode,omitempty"`
IsUnderlayServer int `json:"isServer,omitempty"` // Requset spec peer is server
}
type PushConnectRsp struct {
Error int `json:"error,omitempty"`
@@ -200,7 +230,7 @@ type PushConnectRsp struct {
Detail string `json:"detail,omitempty"`
NatType int `json:"natType,omitempty"`
HasIPv4 int `json:"hasIPv4,omitempty"`
IPv6 string `json:"IPv6,omitempty"`
IPv6 string `json:"IPv6,omitempty"` // if public relay node, ipv6 not set
HasUPNPorNATPMP int `json:"hasUPNPorNATPMP,omitempty"`
ConeNatPort int `json:"coneNatPort,omitempty"` //it's not only cone, but also upnp or nat-pmp hole
FromIP string `json:"fromIP,omitempty"`
@@ -216,6 +246,7 @@ type LoginRsp struct {
Error int `json:"error,omitempty"`
Detail string `json:"detail,omitempty"`
User string `json:"user,omitempty"`
Node string `json:"node,omitempty"`
Token uint64 `json:"token,omitempty"`
Ts int64 `json:"ts,omitempty"`
}
@@ -323,6 +354,7 @@ type AppInfo struct {
ShareBandwidth int `json:"shareBandWidth,omitempty"`
RelayNode string `json:"relayNode,omitempty"`
RelayMode string `json:"relayMode,omitempty"`
LinkMode string `json:"linkMode,omitempty"`
Version string `json:"version,omitempty"`
RetryTime string `json:"retryTime,omitempty"`
ConnectTime string `json:"connectTime,omitempty"`
@@ -334,6 +366,18 @@ type ReportApps struct {
Apps []AppInfo
}
type ReportLogReq struct {
FileName string `json:"fileName,omitempty"`
Offset int64 `json:"offset,omitempty"`
Len int64 `json:"len,omitempty"`
}
type ReportLogRsp struct {
FileName string `json:"fileName,omitempty"`
Content string `json:"content,omitempty"`
Len int64 `json:"len,omitempty"`
Total int64 `json:"total,omitempty"`
}
type UpdateInfo struct {
Error int `json:"error,omitempty"`
ErrorDetail string `json:"errorDetail,omitempty"`
@@ -372,3 +416,17 @@ type EditNode struct {
NewName string `json:"newName,omitempty"`
Bandwidth int `json:"bandwidth,omitempty"`
}
type QueryPeerInfoReq struct {
Token uint64 `json:"token,omitempty"` // if public totp token
PeerNode string `json:"peerNode,omitempty"`
}
type QueryPeerInfoRsp struct {
Online int `json:"online,omitempty"`
Version string `json:"version,omitempty"`
NatType int `json:"natType,omitempty"`
IPv4 string `json:"IPv4,omitempty"`
HasIPv4 int `json:"hasIPv4,omitempty"` // has public ipv4
IPv6 string `json:"IPv6,omitempty"` // if public relay node, ipv6 not set
HasUPNPorNATPMP int `json:"hasUPNPorNATPMP,omitempty"`
}

151
quic.go
View File

@@ -1,151 +0,0 @@
package main
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"io"
"math/big"
"net"
"sync"
"time"
"github.com/lucas-clemente/quic-go"
)
//quic.DialContext do not support version 44,disable it
var quicVersion []quic.VersionNumber
type quicConn struct {
listener quic.Listener
writeMtx *sync.Mutex
quic.Stream
quic.Session
}
func (conn *quicConn) ReadMessage() (*openP2PHeader, []byte, error) {
headBuf := make([]byte, openP2PHeaderSize)
_, err := io.ReadFull(conn, headBuf)
if err != nil {
return nil, nil, err
}
head, err := decodeHeader(headBuf)
if err != nil {
return nil, nil, err
}
dataBuf := make([]byte, head.DataLen)
_, err = io.ReadFull(conn, dataBuf)
return head, dataBuf, err
}
func (conn *quicConn) WriteBytes(mainType uint16, subType uint16, data []byte) error {
writeBytes := append(encodeHeader(mainType, subType, uint32(len(data))), data...)
conn.writeMtx.Lock()
_, err := conn.Write(writeBytes)
conn.writeMtx.Unlock()
return err
}
func (conn *quicConn) WriteBuffer(data []byte) error {
conn.writeMtx.Lock()
_, err := conn.Write(data)
conn.writeMtx.Unlock()
return err
}
func (conn *quicConn) WriteMessage(mainType uint16, subType uint16, packet interface{}) error {
// TODO: call newMessage
data, err := json.Marshal(packet)
if err != nil {
return err
}
writeBytes := append(encodeHeader(mainType, subType, uint32(len(data))), data...)
conn.writeMtx.Lock()
_, err = conn.Write(writeBytes)
conn.writeMtx.Unlock()
return err
}
func (conn *quicConn) Close() error {
conn.Stream.CancelRead(1)
conn.Session.CloseWithError(0, "")
return nil
}
func (conn *quicConn) CloseListener() {
if conn.listener != nil {
conn.listener.Close()
}
}
func (conn *quicConn) Accept() error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
sess, err := conn.listener.Accept(ctx)
if err != nil {
return err
}
stream, err := sess.AcceptStream(context.Background())
if err != nil {
return err
}
conn.Stream = stream
conn.Session = sess
return nil
}
func listenQuic(addr string, idleTimeout time.Duration) (*quicConn, error) {
gLog.Println(LevelDEBUG, "quic listen on ", addr)
listener, err := quic.ListenAddr(addr, generateTLSConfig(),
&quic.Config{Versions: quicVersion, MaxIdleTimeout: idleTimeout, DisablePathMTUDiscovery: true})
if err != nil {
return nil, fmt.Errorf("quic.ListenAddr error:%s", err)
}
return &quicConn{listener: listener, writeMtx: &sync.Mutex{}}, nil
}
func dialQuic(conn *net.UDPConn, remoteAddr *net.UDPAddr, idleTimeout time.Duration) (*quicConn, error) {
tlsConf := &tls.Config{
InsecureSkipVerify: true,
NextProtos: []string{"openp2pv1"},
}
session, err := quic.DialContext(context.Background(), conn, remoteAddr, conn.LocalAddr().String(), tlsConf,
&quic.Config{Versions: quicVersion, MaxIdleTimeout: idleTimeout, DisablePathMTUDiscovery: true})
if err != nil {
return nil, fmt.Errorf("quic.DialContext error:%s", err)
}
stream, err := session.OpenStreamSync(context.Background())
if err != nil {
return nil, fmt.Errorf("OpenStreamSync error:%s", err)
}
qConn := &quicConn{nil, &sync.Mutex{}, stream, session}
return qConn, nil
}
// Setup a bare-bones TLS config for the server
func generateTLSConfig() *tls.Config {
key, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
panic(err)
}
template := x509.Certificate{SerialNumber: big.NewInt(1)}
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
if err != nil {
panic(err)
}
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
if err != nil {
panic(err)
}
return &tls.Config{
Certificates: []tls.Certificate{tlsCert},
NextProtos: []string{"openp2pv1"},
}
}

View File

@@ -1,35 +0,0 @@
//go:build darwin
// +build darwin
package main
import (
"strings"
"syscall"
)
const (
defaultInstallPath = "/usr/local/openp2p"
defaultBinName = "openp2p"
)
func getOsName() (osName string) {
output := execOutput("sw_vers", "-productVersion")
osName = "Mac OS X " + strings.TrimSpace(output)
return
}
func setRLimit() error {
var limit syscall.Rlimit
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
return err
}
limit.Cur = 10240
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
return err
}
return nil
}
func setFirewall() {
}

View File

@@ -1,75 +0,0 @@
//go:build linux
// +build linux
package main
import (
"bufio"
"bytes"
"io/ioutil"
"os"
"strings"
"syscall"
)
const (
defaultInstallPath = "/usr/local/openp2p"
defaultBinName = "openp2p"
)
func getOsName() (osName string) {
var sysnamePath string
sysnamePath = "/etc/redhat-release"
_, err := os.Stat(sysnamePath)
if err != nil && os.IsNotExist(err) {
str := "PRETTY_NAME="
f, err := os.Open("/etc/os-release")
if err != nil && os.IsNotExist(err) {
str = "DISTRIB_ID="
f, err = os.Open("/etc/openwrt_release")
}
if err == nil {
buf := bufio.NewReader(f)
for {
line, err := buf.ReadString('\n')
if err == nil {
line = strings.TrimSpace(line)
pos := strings.Count(line, str)
if pos > 0 {
len1 := len([]rune(str)) + 1
rs := []rune(line)
osName = string(rs[len1 : (len(rs))-1])
break
}
} else {
break
}
}
}
} else {
buff, err := ioutil.ReadFile(sysnamePath)
if err == nil {
osName = string(bytes.TrimSpace(buff))
}
}
if osName == "" {
osName = "Linux"
}
return
}
func setRLimit() error {
var limit syscall.Rlimit
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
return err
}
limit.Max = 1024 * 1024
limit.Cur = limit.Max
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
return err
}
return nil
}
func setFirewall() {
}

View File

@@ -1,56 +0,0 @@
//go:build windows
// +build windows
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"golang.org/x/sys/windows/registry"
)
const (
defaultInstallPath = "C:\\Program Files\\OpenP2P"
defaultBinName = "openp2p.exe"
)
func getOsName() (osName string) {
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE|registry.WOW64_64KEY)
if err != nil {
return
}
defer k.Close()
pn, _, err := k.GetStringValue("ProductName")
if err == nil {
osName = pn
}
return
}
func setRLimit() error {
return nil
}
func setFirewall() {
fullPath, err := filepath.Abs(os.Args[0])
if err != nil {
gLog.Println(LevelERROR, "add firewall error:", err)
return
}
isXP := false
osName := getOsName()
if strings.Contains(osName, "XP") || strings.Contains(osName, "2003") {
isXP = true
}
if isXP {
exec.Command("cmd.exe", `/c`, fmt.Sprintf(`netsh firewall del allowedprogram "%s"`, fullPath)).Run()
exec.Command("cmd.exe", `/c`, fmt.Sprintf(`netsh firewall add allowedprogram "%s" "%s" ENABLE`, ProducnName, fullPath)).Run()
} else { // win7 or later
exec.Command("cmd.exe", `/c`, fmt.Sprintf(`netsh advfirewall firewall del rule name="%s"`, ProducnName)).Run()
exec.Command("cmd.exe", `/c`, fmt.Sprintf(`netsh advfirewall firewall add rule name="%s" dir=in action=allow program="%s" enable=yes`, ProducnName, fullPath)).Run()
}
}

View File

@@ -25,6 +25,9 @@ func VerifyTOTP(code uint64, token uint64, ts int64) bool {
if code == 0 {
return false
}
if code == token {
return true
}
if code == GenTOTP(token, ts) || code == GenTOTP(token, ts-TOTPStep) || code == GenTOTP(token, ts+TOTPStep) {
return true
}

View File

@@ -7,10 +7,11 @@ import (
"net"
"sync"
"time"
reuse "github.com/openp2p-cn/go-reuseport"
)
type underlayTCP struct {
listener net.Listener
writeMtx *sync.Mutex
net.Conn
}
@@ -66,13 +67,20 @@ func (conn *underlayTCP) Close() error {
return conn.Conn.Close()
}
func listenTCP(port int, idleTimeout time.Duration) (*underlayTCP, error) {
addr, _ := net.ResolveTCPAddr("tcp", fmt.Sprintf("0.0.0.0:%d", port))
func listenTCP(host string, port int, localPort int, mode string) (*underlayTCP, error) {
if mode == LinkModeTCPPunch {
c, err := reuse.DialTimeout("tcp", fmt.Sprintf("0.0.0.0:%d", localPort), fmt.Sprintf("%s:%d", host, port), SymmetricHandshakeAckTimeout) // TODO: timeout
if err != nil {
gLog.Println(LvDEBUG, "send tcp punch: ", err)
return nil, err
}
return &underlayTCP{writeMtx: &sync.Mutex{}, Conn: c}, nil
}
addr, _ := net.ResolveTCPAddr("tcp", fmt.Sprintf("0.0.0.0:%d", localPort))
l, err := net.ListenTCP("tcp", addr)
if err != nil {
return nil, err
}
defer l.Close()
l.SetDeadline(time.Now().Add(SymmetricHandshakeAckTimeout))
c, err := l.Accept()
defer l.Close()
@@ -82,11 +90,19 @@ func listenTCP(port int, idleTimeout time.Duration) (*underlayTCP, error) {
return &underlayTCP{writeMtx: &sync.Mutex{}, Conn: c}, nil
}
func dialTCP(host string, port int) (*underlayTCP, error) {
c, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), SymmetricHandshakeAckTimeout)
func dialTCP(host string, port int, localPort int, mode string) (*underlayTCP, error) {
var c net.Conn
var err error
if mode == LinkModeTCPPunch {
c, err = reuse.DialTimeout("tcp", fmt.Sprintf("0.0.0.0:%d", localPort), fmt.Sprintf("%s:%d", host, port), SymmetricHandshakeAckTimeout)
} else {
c, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), SymmetricHandshakeAckTimeout)
}
if err != nil {
fmt.Printf("Dial %s:%d error:%s", host, port, err)
gLog.Printf(LvERROR, "Dial %s:%d error:%s", host, port, err)
return nil, err
}
gLog.Printf(LvDEBUG, "Dial %s:%d OK", host, port)
return &underlayTCP{writeMtx: &sync.Mutex{}, Conn: c}, nil
}

View File

@@ -67,7 +67,7 @@ func (conn *underlayTCP6) Close() error {
}
func listenTCP6(port int, idleTimeout time.Duration) (*underlayTCP6, error) {
addr, _ := net.ResolveTCPAddr("tcp6", fmt.Sprintf("0.0.0.0:%d", port))
addr, _ := net.ResolveTCPAddr("tcp6", fmt.Sprintf("[::]:%d", port))
l, err := net.ListenTCP("tcp6", addr)
if err != nil {
return nil, err
@@ -83,9 +83,9 @@ func listenTCP6(port int, idleTimeout time.Duration) (*underlayTCP6, error) {
}
func dialTCP6(host string, port int) (*underlayTCP6, error) {
c, err := net.DialTimeout("tcp6", fmt.Sprintf("%s:%d", host, port), SymmetricHandshakeAckTimeout)
c, err := net.DialTimeout("tcp6", fmt.Sprintf("[%s]:%d", host, port), SymmetricHandshakeAckTimeout)
if err != nil {
fmt.Printf("Dial %s:%d error:%s", host, port, err)
gLog.Printf(LvERROR, "Dial %s:%d error:%s", host, port, err)
return nil, err
}
return &underlayTCP6{writeMtx: &sync.Mutex{}, Conn: c}, nil

View File

@@ -16,7 +16,7 @@ import (
"time"
)
func update() {
func update(host string, port int) {
gLog.Println(LvINFO, "update start")
defer gLog.Println(LvINFO, "update end")
c := http.Client{
@@ -27,7 +27,7 @@ func update() {
}
goos := runtime.GOOS
goarch := runtime.GOARCH
rsp, err := c.Get(fmt.Sprintf("https://openp2p.cn:27183/api/v1/update?fromver=%s&os=%s&arch=%s", OpenP2PVersion, goos, goarch))
rsp, err := c.Get(fmt.Sprintf("https://%s:%d/api/v1/update?fromver=%s&os=%s&arch=%s", host, port, OpenP2PVersion, goos, goarch))
if err != nil {
gLog.Println(LvERROR, "update:query update list failed:", err)
return

41
upnp.go
View File

@@ -1,12 +1,8 @@
/*
Taken from taipei-torrent
Just enough UPnP to be able to forward ports
Taken from taipei-torrent And fix some bugs
*/
package main
// BUG(jae): TODO: use syscalls to get actual ourIP. http://pastebin.com/9exZG4rh
import (
"bytes"
"encoding/xml"
@@ -34,11 +30,12 @@ type NAT interface {
}
func Discover() (nat NAT, err error) {
localIP := localIPv4()
ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
if err != nil {
return
}
conn, err := net.ListenPacket("udp4", ":0")
conn, err := net.ListenPacket("udp4", fmt.Sprintf("%s:0", localIP))
if err != nil {
return
}
@@ -67,6 +64,7 @@ func Discover() (nat NAT, err error) {
var n int
_, _, err = socket.ReadFromUDP(answerBytes)
if err != nil {
gLog.Println(LvDEBUG, "UPNP discover error:", err)
return
}
@@ -98,12 +96,10 @@ func Discover() (nat NAT, err error) {
if err != nil {
return
}
var ourIP net.IP
ourIP, err = localIPv4()
if err != nil {
return
}
nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP.String(), urnDomain: urnDomain}
nat = &upnpNAT{serviceURL: serviceURL, ourIP: localIP, urnDomain: urnDomain}
return
}
}
@@ -174,29 +170,14 @@ func getChildService(d *Device, serviceType string) *UPNPService {
return nil
}
func localIPv4() (net.IP, error) {
tt, err := net.Interfaces()
func localIPv4() string { // TODO: multi nic will wrong
conn, err := net.Dial("udp", "8.8.8.8:80")
if err != nil {
return nil, err
return ""
}
for _, t := range tt {
aa, err := t.Addrs()
if err != nil {
return nil, err
}
for _, a := range aa {
ipnet, ok := a.(*net.IPNet)
if !ok {
continue
}
v4 := ipnet.IP.To4()
if v4 == nil || v4[0] == 127 { // loopback address
continue
}
return v4, nil
}
}
return nil, errors.New("cannot find local IP address")
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
return localAddr.IP.String()
}
func getServiceURL(rootURL string) (url, urnDomain string, err error) {