Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
532d3667ce | ||
|
|
b2d35c6f97 | ||
|
|
037f3cc34e | ||
|
|
6b8d3f7d47 | ||
|
|
26e0fdf605 | ||
|
|
3653ec19cd | ||
|
|
c733a2a4a1 | ||
|
|
b54fa2c6be | ||
|
|
133fe046f8 | ||
|
|
95b46f51d0 | ||
|
|
7686af39e0 | ||
|
|
16b937ebd7 | ||
|
|
ac454ec694 | ||
|
|
029d69869f |
68
README-ZH.md
@@ -1,7 +1,8 @@
|
|||||||
# [English](/README.md)|中文
|
# [English](/README.md)|中文
|
||||||
网站: [openp2p.cn](https://openp2p.cn)
|
网站: [openp2p.cn](https://openp2p.cn)
|
||||||
## OpenP2P是什么
|
## OpenP2P是什么
|
||||||
它是一个开源、免费、轻量级的P2P共享网络。任何设备接入OpenP2P,随时随地访问它们。相比BT网络用来共享文件,OpenP2P网络用来共享带宽。
|
它是一个开源、免费、轻量级的P2P共享网络。你的设备将组成一个私有P2P网络,里面的设备可以直接访问其它成员,或者通过其它成员转发数据间接访问。如果私有网络无法完成通信,将会到公有P2P网络寻找共享节点协助通信。
|
||||||
|
相比BT网络用来共享文件,OpenP2P网络用来共享带宽。
|
||||||
我们的目标是:充分利用带宽,利用共享节点转发数据,建设一个远程连接的通用基础设施。
|
我们的目标是:充分利用带宽,利用共享节点转发数据,建设一个远程连接的通用基础设施。
|
||||||
|
|
||||||
## 为什么选择OpenP2P
|
## 为什么选择OpenP2P
|
||||||
@@ -22,44 +23,49 @@
|
|||||||
### 5. 跨平台
|
### 5. 跨平台
|
||||||
因为轻量,所以很容易支持各个平台。支持主流的操作系统:Windows,Linux,MacOS;和主流的cpu架构:386、amd64、arm、arm64、mipsle、mipsle64、mips、mips64
|
因为轻量,所以很容易支持各个平台。支持主流的操作系统:Windows,Linux,MacOS;和主流的cpu架构:386、amd64、arm、arm64、mipsle、mipsle64、mips、mips64
|
||||||
### 6. 高效
|
### 6. 高效
|
||||||
P2P直连可以让你的设备跑满带宽。不论你的设备在任何网络环境,无论NAT1-4(Cone或Symmetric),都支持。依靠Quic协议优秀的拥塞算法,能在糟糕的网络环境获得高带宽低延时。
|
P2P直连可以让你的设备跑满带宽。不论你的设备在任何网络环境,无论NAT1-4(Cone或Symmetric),UPNP,IPv6都支持。依靠Quic协议优秀的拥塞算法,能在糟糕的网络环境获得高带宽低延时。
|
||||||
|
|
||||||
### 7. 二次开发
|
### 7. 二次开发
|
||||||
基于OpenP2P只需数行代码,就能让原来只能局域网通信的程序,变成任何内网都能通信
|
基于OpenP2P只需数行代码,就能让原来只能局域网通信的程序,变成任何内网都能通信
|
||||||
|
|
||||||
## 快速入门
|
## 快速入门
|
||||||
|
仅需简单4步就能用起来。
|
||||||
|
下面是一个远程办公例子:在家里连入办公室Windows电脑。
|
||||||
|
### 1.注册
|
||||||
|
前往<https://console.openp2p.cn> 注册新用户,暂无需任何认证
|
||||||
|
|
||||||
> :warning: 本文所有命令, Windows环境使用"openp2p.exe", Linux环境使用"./openp2p"
|

|
||||||
|
### 2.安装
|
||||||
|
分别在本地和远程电脑下载后双击运行,一键安装
|
||||||
|
|
||||||
|

|
||||||
以一个最常见的例子说明OpenP2P如何使用:远程办公,在家里连入办公室Windows电脑。
|
|
||||||
相信很多人在疫情下远程办公是刚需。
|
|
||||||
1. 先确认办公室电脑已开启远程桌面功能(如何开启参考官方说明https://docs.microsoft.com/zh-cn/windows-server/remote/remote-desktop-services/clients/remote-desktop-allow-access)
|
|
||||||
2. 在办公室下载最新的`OpenP2P`[下载页](https://openp2p.cn/),解压出来,在命令行执行
|
|
||||||
```
|
|
||||||
openp2p.exe install -node OFFICEPC1 -user USERNAME1 -password PASSWORD1
|
|
||||||
```
|
|
||||||
|
|
||||||
> :warning: **切记将标记大写的参数改成自己的,3个参数的长度必须>=8个字符**
|
Windows默认会阻止没有花钱买它家证书签名过的程序,选择“仍要运行”即可。
|
||||||
|
|
||||||

|

|
||||||
3. 在家里下载最新的OpenP2P,解压出来,在命令行执行
|
|
||||||
```
|

|
||||||
openp2p.exe -d -node HOMEPC123 -user USERNAME1 -password PASSWORD1 --peernode OFFICEPC1 --dstip 127.0.0.1 --dstport 3389 --srcport 23389 --protocol tcp
|
### 3.新建P2P应用
|
||||||
```
|
|
||||||
> :warning: **切记将标记大写的参数改成自己的**
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

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

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

|
|
||||||

|
|
||||||
`LISTEN ON PORT 23389 START` 看到这行日志表示P2PApp建立成功,监听23389端口。只需连接本机的127.0.0.1:23389就相当于连接公司Windows电脑的3389端口。
|
|
||||||
|
|
||||||
4. 在家里Windows电脑,按Win+R输入mstsc打开远程桌面,输入127.0.0.1:23389 /admin
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 详细使用说明
|
## 详细使用说明
|
||||||
[这里](/USAGE-ZH.md)详细介绍如何使用和运行参数
|
[这里](/USAGE-ZH.md)介绍如何手动运行
|
||||||
|
|
||||||
## 典型应用场景
|
## 典型应用场景
|
||||||
特别适合大流量的内网访问
|
特别适合大流量的内网访问
|
||||||
@@ -89,6 +95,7 @@ P2P直连可以让你的设备跑满带宽。不论你的设备在任何网络
|
|||||||
服务端有个调度模型,根据带宽、ping值、稳定性、服务时长,尽可能地使共享节点均匀地提供服务。连接共享节点使用TOTP密码,hmac-sha256算法校验,它是一次性密码,和我们平时使用的手机验证码或银行密码器一样的原理。
|
服务端有个调度模型,根据带宽、ping值、稳定性、服务时长,尽可能地使共享节点均匀地提供服务。连接共享节点使用TOTP密码,hmac-sha256算法校验,它是一次性密码,和我们平时使用的手机验证码或银行密码器一样的原理。
|
||||||
|
|
||||||
## 编译
|
## 编译
|
||||||
|
go version go1.18.1+
|
||||||
cd到代码根目录,执行
|
cd到代码根目录,执行
|
||||||
```
|
```
|
||||||
export GOPROXY=https://goproxy.io,direct
|
export GOPROXY=https://goproxy.io,direct
|
||||||
@@ -98,16 +105,16 @@ go build
|
|||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
近期计划:
|
近期计划:
|
||||||
1. 支持IPv6
|
1. 支持IPv6(100%)
|
||||||
2. 支持随系统自动启动,安装成系统服务
|
2. 支持随系统自动启动,安装成系统服务(100%)
|
||||||
3. 提供一些免费服务器给特别差的网络,如广电网络
|
3. 提供一些免费服务器给特别差的网络,如广电网络(100%)
|
||||||
4. 建立网站,用户可以在网站管理所有P2PApp和设备。查看设备在线状态,升级,增删查改重启P2PApp等
|
4. 建立网站,用户可以在网站管理所有P2PApp和设备。查看设备在线状态,升级,增删查改重启P2PApp等(100%)
|
||||||
5. 建立公众号,用户可在微信公众号管理所有P2PApp和设备
|
5. 建立公众号,用户可在微信公众号管理所有P2PApp和设备
|
||||||
6. 客户端提供WebUI
|
6. 客户端提供WebUI
|
||||||
7. 支持自有服务器高并发连接
|
7. 支持自有服务器高并发连接
|
||||||
8. 共享节点调度模型优化,对不同的运营商优化
|
8. 共享节点调度模型优化,对不同的运营商优化
|
||||||
9. 方便二次开发,提供API和lib
|
9. 方便二次开发,提供API和lib
|
||||||
10. 应用层支持UDP协议,实现很简单,但UDP应用较少暂不急
|
10. 应用层支持UDP协议,实现很简单,但UDP应用较少暂不急(100%)
|
||||||
11. 底层通信支持KCP协议,目前仅支持Quic;KCP专门对延时优化,被游戏加速器广泛使用,可以牺牲一定的带宽降低延时
|
11. 底层通信支持KCP协议,目前仅支持Quic;KCP专门对延时优化,被游戏加速器广泛使用,可以牺牲一定的带宽降低延时
|
||||||
12. 支持Android系统,让旧手机焕发青春变成移动网关
|
12. 支持Android系统,让旧手机焕发青春变成移动网关
|
||||||
13. 支持Windows网上邻居共享文件
|
13. 支持Windows网上邻居共享文件
|
||||||
@@ -119,8 +126,7 @@ go build
|
|||||||
|
|
||||||
## 参与贡献
|
## 参与贡献
|
||||||
TODO或ISSUE里如果有你擅长的领域,或者你有特别好的主意,可以加入OpenP2P项目,贡献你的代码。待项目茁壮成长后,你们就是知名开源项目的主要代码贡献者,岂不快哉。
|
TODO或ISSUE里如果有你擅长的领域,或者你有特别好的主意,可以加入OpenP2P项目,贡献你的代码。待项目茁壮成长后,你们就是知名开源项目的主要代码贡献者,岂不快哉。
|
||||||
## 商业合作
|
|
||||||
它是一个中国人发起的项目,更懂国内网络环境,更懂用户需求,更好的企业级支持
|
|
||||||
## 技术交流
|
## 技术交流
|
||||||
QQ群:16947733
|
QQ群:16947733
|
||||||
邮箱:openp2p.cn@gmail.com tenderiron@139.com
|
邮箱:openp2p.cn@gmail.com tenderiron@139.com
|
||||||
|
|||||||
80
README.md
@@ -1,7 +1,8 @@
|
|||||||
# English|[中文](/README-ZH.md)
|
# English|[中文](/README-ZH.md)
|
||||||
Website: [openp2p.cn](https://openp2p.cn)
|
Website: [openp2p.cn](https://openp2p.cn)
|
||||||
## What is OpenP2P
|
## What is OpenP2P
|
||||||
It is an open source, free, and lightweight P2P sharing network. As long as any device joins in, you can access them anywhere. Compared with the BT network used to share files, the OpenP2P network is used to share bandwidth.
|
It is an open source, free, and lightweight P2P sharing network. Your devices will form a private P2P network, in which devices can directly access other members, or indirectly access through other members forwarding data.
|
||||||
|
If the private network cannot complete the communication, it will go to the public P2P network to find a shared node to assist in the communication. Compared with the BT network used to share files, the OpenP2P network is used to share bandwidth.
|
||||||
Our goal is to make full use of bandwidth, use shared nodes to relay data, and build a common infrastructure for remote connections.
|
Our goal is to make full use of bandwidth, use shared nodes to relay data, and build a common infrastructure for remote connections.
|
||||||
## Why OpenP2P
|
## Why OpenP2P
|
||||||
### 1. Free
|
### 1. Free
|
||||||
@@ -24,58 +25,52 @@ 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.
|
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
|
### 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),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
|
### 7. Integration
|
||||||
Your applicaiton can call OpenP2P with a few code to make any internal networks communicate with each other.
|
Your applicaiton can call OpenP2P with a few code to make any internal networks communicate with each other.
|
||||||
|
|
||||||
## Get Started
|
## Get Started
|
||||||
A common scenario to introduce OpenP2P: remote work. At home connects to office's Linux PC .
|
Just 4 simple steps to use.
|
||||||
Under the outbreak of covid-19 pandemic, surely remote work becomes a fundamental demand.
|
Here's an example of remote work: connecting to an office Windows computer at home.
|
||||||
|
|
||||||
|
### 1.Register
|
||||||
|
Go to <https://console.openp2p.cn> register a new user
|
||||||
|
|
||||||
> :warning: all commands in this doc, Windows env uses "openp2p.exe", Linux env uses "./openp2p"
|

|
||||||
|
### 2.Install
|
||||||
|
Download on local and remote computers and double-click to run, one-click installation
|
||||||
|
|
||||||
1. Make sure your office device(Linux) has opened the access of ssh.
|

|
||||||
```
|
|
||||||
netstat -nl | grep 22
|
|
||||||
```
|
|
||||||
Output sample
|
|
||||||

|
|
||||||
|
|
||||||
2. Download the latest version of `OpenP2P` [Download Page](https://openp2p.cn/),unzip the downloaded package, and execute below command line.
|
By default, Windows will block programs that have not been signed by the Microsoft's certificate, and you can select "Run anyway".
|
||||||
```
|
|
||||||
tar xzvf ${PackageName}
|
|
||||||
./openp2p install -node OFFICEPC1 -user USERNAME1 -password PASSWORD1
|
|
||||||
```
|
|
||||||
|
|
||||||
> :warning: **Must change the parameters marked in UPPERCASE to your own. These 3 parameters must >= 8 charaters**
|

|
||||||
|
|
||||||
Output sample
|

|
||||||

|
|
||||||
|
|
||||||
3. Download OpenP2P on your home device,unzip and execute below command line.
|
|
||||||
```
|
|
||||||
openp2p.exe -d -node HOMEPC123 -user USERNAME1 -password PASSWORD1 --peernode OFFICEPC1 --dstip 127.0.0.1 --dstport 22 --srcport 22022 --protocol tcp
|
|
||||||
```
|
|
||||||
|
|
||||||
> :warning: **Must change the parameters marked in UPPERCASE to your own**
|
### 3.New P2PApp
|
||||||
|
|
||||||
Output sample
|

|
||||||

|
|
||||||
The log of `LISTEN ON PORT 22022 START` indicates P2PApp runs successfully on your home device, listing port is 22022. Once connects to local ip:port,127.0.0.1:22022, it means the home device has conneccted to the office device's port, 22.
|
|
||||||

|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
4. Test the connection between office device and home device.In your home deivce, run SSH to login the office device.
|

|
||||||
```
|
|
||||||
ssh -p22022 root@127.0.0.1:22022
|
### 4.Use P2PApp
|
||||||
```
|
You can see the P2P application you just created on the "MyHomePC" device, just connect to the "local listening port" shown in the figure below.
|
||||||

|
|
||||||
|

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

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
[Here](/USAGE.md) is a detailed description of how to use and running parameters
|
[Here](/USAGE.md) describes how to run manually
|
||||||
|
|
||||||
## Scenarios
|
## Scenarios
|
||||||
Especially suitable for large traffic intranet access.
|
Especially suitable for large traffic intranet access.
|
||||||
@@ -108,6 +103,7 @@ That's right, the relay node is naturally an man-in-middle, so AES encryption is
|
|||||||
The server side has a scheduling model, which calculate bandwith, ping value,stability and service duration to provide a well-proportioned service to every share node. It uses TOTP(Time-based One-time Password) with hmac-sha256 algorithem, its theory as same as the cellphone validation code or bank cipher coder.
|
The server side has a scheduling model, which calculate bandwith, ping value,stability and service duration to provide a well-proportioned service to every share node. It uses TOTP(Time-based One-time Password) with hmac-sha256 algorithem, its theory as same as the cellphone validation code or bank cipher coder.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
go version go1.18.1+
|
||||||
cd root directory of the socure code and execute
|
cd root directory of the socure code and execute
|
||||||
```
|
```
|
||||||
export GOPROXY=https://goproxy.io,direct
|
export GOPROXY=https://goproxy.io,direct
|
||||||
@@ -117,18 +113,18 @@ go build
|
|||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
Short-Term:
|
Short-Term:
|
||||||
1. Support IPv6.
|
1. Support IPv6.(100%)
|
||||||
2. Support auto run when system boot, setup system service.
|
2. Support auto run when system boot, setup system service.(100%)
|
||||||
3. Provide free servers to some low-performance network.
|
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 .
|
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.
|
5. Provide wechat official account, user can manage P2PApp nodes and deivce as same as website.
|
||||||
6. Provide WebUI on client side.
|
6. Provide WebUI on client side.
|
||||||
7. Support high concurrency on server side.
|
7. Support high concurrency on server side.
|
||||||
8. Optimize our share scheduling model for different network operators.
|
8. Optimize our share scheduling model for different network operators.
|
||||||
9. Provide REST APIs and libary for secondary development.
|
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.
|
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.
|
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 .
|
12. Support Android platform, let the phones to be mobile gateway.
|
||||||
13. Support SMB Windows neighborhood.
|
13. Support SMB Windows neighborhood.
|
||||||
14. Direct connection on intranet, for testing.
|
14. Direct connection on intranet, for testing.
|
||||||
|
|
||||||
|
|||||||
67
USAGE-ZH.md
@@ -1,44 +1,83 @@
|
|||||||
# 详细运行参数说明
|
# 手动运行说明
|
||||||
|
大部分情况通过<https://console.openp2p.cn> 操作即可。有些情况需要手动运行
|
||||||
> :warning: 本文所有命令, Windows环境使用"openp2p.exe", Linux环境使用"./openp2p"
|
> :warning: 本文所有命令, Windows环境使用"openp2p.exe", Linux环境使用"./openp2p"
|
||||||
|
|
||||||
|
|
||||||
## 安装和监听
|
## 安装和监听
|
||||||
```
|
```
|
||||||
./openp2p install -node OFFICEPC1 -user USERNAME1 -password PASSWORD1
|
./openp2p install -node OFFICEPC1 -token TOKEN
|
||||||
或
|
或
|
||||||
./openp2p -d -node OFFICEPC1 -user USERNAME1 -password PASSWORD1
|
./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: 独一无二的节点名字,唯一标识
|
||||||
>* -user: 独一无二的用户名字,该节点属于这个user
|
>* -token: 在<console.openp2p.cn>“我的”里面找到
|
||||||
>* -password: 密码
|
>* -sharebandwidth: 作为共享节点时提供带宽,默认10mbps. 如果是光纤大带宽,设置越大效果越好. 0表示不共享,该节点只在私有的P2P网络使用。不加入共享的P2P网络,这样也意味着无法使用别人的共享节点
|
||||||
>* -sharebandwidth: 作为共享节点时提供带宽,默认10mbps. 如果是光纤大带宽,设置越大效果越好
|
|
||||||
>* -loglevel: 需要查看更多调试日志,设置0;默认是1
|
>* -loglevel: 需要查看更多调试日志,设置0;默认是1
|
||||||
>* -noshare: 不共享,该节点只在私有的P2P网络使用。不加入共享的P2P网络,这样也意味着无法使用别人的共享节点
|
|
||||||
|
|
||||||
|
### 在docker容器里运行openp2p
|
||||||
|
我们暂时还没提供官方docker镜像,你可以在随便一个容器里运行
|
||||||
|
```
|
||||||
|
nohup ./openp2p -d -node OFFICEPC1 -token TOKEN &
|
||||||
|
#这里由于一般的镜像都精简过,install系统服务会失败,所以使用直接daemon模式后台运行
|
||||||
|
```
|
||||||
## 连接
|
## 连接
|
||||||
```
|
```
|
||||||
./openp2p -d -node HOMEPC123 -user USERNAME1 -password PASSWORD1 -peernode OFFICEPC1 -dstip 127.0.0.1 -dstport 3389 -srcport 23389 -protocol tcp
|
./openp2p -d -node HOMEPC123 -token TOKEN -appname OfficeWindowsRemote -peernode OFFICEPC1 -dstip 127.0.0.1 -dstport 3389 -srcport 23389
|
||||||
使用配置文件,建立多个P2PApp
|
使用配置文件,建立多个P2PApp
|
||||||
./openp2p -d -f
|
./openp2p -d
|
||||||
./openp2p -f
|
|
||||||
```
|
```
|
||||||
|
>* -appname: 这个P2P应用名字
|
||||||
>* -peernode: 目标节点名字
|
>* -peernode: 目标节点名字
|
||||||
>* -dstip: 目标服务地址,默认本机127.0.0.1
|
>* -dstip: 目标服务地址,默认本机127.0.0.1
|
||||||
>* -dstport: 目标服务端口,常见的如windows远程桌面3389,Linux ssh 22
|
>* -dstport: 目标服务端口,常见的如windows远程桌面3389,Linux ssh 22
|
||||||
>* -protocol: 目标服务协议 tcp、udp
|
>* -protocol: 目标服务协议 tcp、udp
|
||||||
>* -peeruser: 目标用户,如果是同一个用户下的节点,则无需设置
|
|
||||||
>* -peerpassword: 目标密码,如果是同一个用户下的节点,则无需设置
|
## 配置文件
|
||||||
>* -f: 配置文件,如果希望配置多个P2PApp参考[config.json](/config.json)
|
一般保存在当前目录,安装模式下会保存到 `C:\Program Files\OpenP2P\config.json` 或 `/usr/local/openp2p/config.json`
|
||||||
|
希望修改参数,或者配置多个P2PApp可手动修改配置文件
|
||||||
|
|
||||||
|
配置实例
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"network": {
|
||||||
|
"Node": "hhd1207-222",
|
||||||
|
"Token": "TOKEN",
|
||||||
|
"ShareBandwidth": 0,
|
||||||
|
"ServerHost": "api.openp2p.cn",
|
||||||
|
"ServerPort": 27183,
|
||||||
|
"UDPPort1": 27182,
|
||||||
|
"UDPPort2": 27183
|
||||||
|
},
|
||||||
|
"apps": [
|
||||||
|
{
|
||||||
|
"AppName": "OfficeWindowsPC",
|
||||||
|
"Protocol": "tcp",
|
||||||
|
"SrcPort": 23389,
|
||||||
|
"PeerNode": "OFFICEPC1",
|
||||||
|
"DstPort": 3389,
|
||||||
|
"DstHost": "localhost",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AppName": "OfficeServerSSH",
|
||||||
|
"Protocol": "tcp",
|
||||||
|
"SrcPort": 22,
|
||||||
|
"PeerNode": "OFFICEPC1",
|
||||||
|
"DstPort": 22,
|
||||||
|
"DstHost": "192.168.1.5",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## 升级客户端
|
## 升级客户端
|
||||||
```
|
```
|
||||||
# update local client
|
# update local client
|
||||||
./openp2p update
|
./openp2p update
|
||||||
# update remote client
|
# 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系统需要设置防火墙放行本程序,程序会自动设置,如果设置失败会影响连接功能。
|
Windows系统需要设置防火墙放行本程序,程序会自动设置,如果设置失败会影响连接功能。
|
||||||
|
|||||||
68
USAGE.md
@@ -1,45 +1,85 @@
|
|||||||
# 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
|
||||||
> :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 -user USERNAME1 -password PASSWORD1
|
./openp2p install -node OFFICEPC1 -token TOKEN
|
||||||
Or
|
Or
|
||||||
./openp2p -d -node OFFICEPC1 -user USERNAME1 -password PASSWORD1
|
./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
|
||||||
>* -user: Unique user name, the node belongs to this user
|
>* -token: See <console.openp2p.cn> "Profile"
|
||||||
>* -password: Password
|
>* -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 people’s 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
|
|
||||||
>* -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
|
||||||
>* -noshare: 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 people’s shared nodes
|
|
||||||
|
### Run in Docker container
|
||||||
|
We don't provide official docker image yet, you can run it in any container
|
||||||
|
```
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
## Connect
|
## Connect
|
||||||
```
|
```
|
||||||
./openp2p -d -node HOMEPC123 -user USERNAME1 -password PASSWORD1 -peernode OFFICEPC1 -dstip 127.0.0.1 -dstport 3389 -srcport 23389 -protocol tcp
|
./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 -f
|
./openp2p -d
|
||||||
./openp2p -f
|
|
||||||
```
|
```
|
||||||
|
>* -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
|
||||||
>* -peeruser: The target user, if it is a node under the same user, no need to set
|
|
||||||
>* -peerpassword: The target password, if it is a node under the same user, no need to set
|
|
||||||
>* -f: Configuration file, if you want to configure multiple P2PApp refer to [config.json](/config.json)
|
|
||||||
|
|
||||||
|
## 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`
|
||||||
|
If you want to modify the parameters, or configure multiple P2PApps, you can manually modify the configuration file
|
||||||
|
|
||||||
|
Configuration example
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"network": {
|
||||||
|
"Node": "hhd1207-222",
|
||||||
|
"Token": "TOKEN",
|
||||||
|
"ShareBandwidth": 0,
|
||||||
|
"ServerHost": "api.openp2p.cn",
|
||||||
|
"ServerPort": 27183,
|
||||||
|
"UDPPort1": 27182,
|
||||||
|
"UDPPort2": 27183
|
||||||
|
},
|
||||||
|
"apps": [
|
||||||
|
{
|
||||||
|
"AppName": "OfficeWindowsPC",
|
||||||
|
"Protocol": "tcp",
|
||||||
|
"SrcPort": 23389,
|
||||||
|
"PeerNode": "OFFICEPC1",
|
||||||
|
"DstPort": 3389,
|
||||||
|
"DstHost": "localhost",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AppName": "OfficeServerSSH",
|
||||||
|
"Protocol": "tcp",
|
||||||
|
"SrcPort": 22,
|
||||||
|
"PeerNode": "OFFICEPC1",
|
||||||
|
"DstPort": 22,
|
||||||
|
"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://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.
|
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.
|
||||||
|
|||||||
@@ -7,39 +7,39 @@ import (
|
|||||||
|
|
||||||
// BandwidthLimiter ...
|
// BandwidthLimiter ...
|
||||||
type BandwidthLimiter struct {
|
type BandwidthLimiter struct {
|
||||||
freeFlowTime time.Time
|
ts time.Time
|
||||||
bandwidth int // mbps
|
bw int // mbps
|
||||||
freeFlow int // bytes
|
freeBytes int // bytes
|
||||||
maxFreeFlow int // bytes
|
maxFreeBytes int // bytes
|
||||||
freeFlowMtx sync.Mutex
|
mtx sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// mbps
|
// mbps
|
||||||
func newBandwidthLimiter(bw int) *BandwidthLimiter {
|
func newBandwidthLimiter(bw int) *BandwidthLimiter {
|
||||||
return &BandwidthLimiter{
|
return &BandwidthLimiter{
|
||||||
bandwidth: bw,
|
bw: bw,
|
||||||
freeFlowTime: time.Now(),
|
ts: time.Now(),
|
||||||
maxFreeFlow: bw * 1024 * 1024 / 8,
|
maxFreeBytes: bw * 1024 * 1024 / 8,
|
||||||
freeFlow: bw * 1024 * 1024 / 8,
|
freeBytes: bw * 1024 * 1024 / 8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add ...
|
// Add ...
|
||||||
func (bl *BandwidthLimiter) Add(bytes int) {
|
func (bl *BandwidthLimiter) Add(bytes int) {
|
||||||
if bl.bandwidth <= 0 {
|
if bl.bw <= 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bl.freeFlowMtx.Lock()
|
bl.mtx.Lock()
|
||||||
defer bl.freeFlowMtx.Unlock()
|
defer bl.mtx.Unlock()
|
||||||
// calc free flow 1000*1000/1024/1024=0.954; 1024*1024/1000/1000=1.048
|
// calc free flow 1000*1000/1024/1024=0.954; 1024*1024/1000/1000=1.048
|
||||||
bl.freeFlow += int(time.Now().Sub(bl.freeFlowTime) * time.Duration(bl.bandwidth) / 8 / 954)
|
bl.freeBytes += int(time.Since(bl.ts) * time.Duration(bl.bw) / 8 / 954)
|
||||||
if bl.freeFlow > bl.maxFreeFlow {
|
if bl.freeBytes > bl.maxFreeBytes {
|
||||||
bl.freeFlow = bl.maxFreeFlow
|
bl.freeBytes = bl.maxFreeBytes
|
||||||
}
|
}
|
||||||
bl.freeFlow -= bytes
|
bl.freeBytes -= bytes
|
||||||
bl.freeFlowTime = time.Now()
|
bl.ts = time.Now()
|
||||||
if bl.freeFlow < 0 {
|
if bl.freeBytes < 0 {
|
||||||
// sleep for the overflow
|
// sleep for the overflow
|
||||||
time.Sleep(time.Millisecond * time.Duration(-bl.freeFlow/(bl.bandwidth*1048/8)))
|
time.Sleep(time.Millisecond * time.Duration(-bl.freeBytes/(bl.bw*1048/8)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
49
common.go
@@ -7,9 +7,13 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -75,6 +79,7 @@ func pkcs7UnPadding(origData []byte, dataLen int) ([]byte, error) {
|
|||||||
return origData[:(dataLen - unPadLen)], nil
|
return origData[:(dataLen - unPadLen)], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AES-CBC
|
||||||
func encryptBytes(key []byte, out, in []byte, plainLen int) ([]byte, error) {
|
func encryptBytes(key []byte, out, in []byte, plainLen int) ([]byte, error) {
|
||||||
if len(key) == 0 {
|
if len(key) == 0 {
|
||||||
return in[:plainLen], nil
|
return in[:plainLen], nil
|
||||||
@@ -120,20 +125,20 @@ func netInfo() *NetInfo {
|
|||||||
client := &http.Client{Transport: tr, Timeout: time.Second * 10}
|
client := &http.Client{Transport: tr, Timeout: time.Second * 10}
|
||||||
r, err := client.Get("https://ifconfig.co/json")
|
r, err := client.Get("https://ifconfig.co/json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelINFO, "netInfo error:", err)
|
gLog.Println(LvINFO, "netInfo error:", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
buf := make([]byte, 1024*64)
|
buf := make([]byte, 1024*64)
|
||||||
n, err := r.Body.Read(buf)
|
n, err := r.Body.Read(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelINFO, "netInfo error:", err)
|
gLog.Println(LvINFO, "netInfo error:", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
rsp := NetInfo{}
|
rsp := NetInfo{}
|
||||||
err = json.Unmarshal(buf[:n], &rsp)
|
err = json.Unmarshal(buf[:n], &rsp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Printf(LevelERROR, "wrong NetInfo:%s", err)
|
gLog.Printf(LvERROR, "wrong NetInfo:%s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return &rsp
|
return &rsp
|
||||||
@@ -148,3 +153,41 @@ func execOutput(name string, args ...string) string {
|
|||||||
cmdGetOsName.Run()
|
cmdGetOsName.Run()
|
||||||
return cmdOut.String()
|
return cmdOut.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func defaultNodeName() string {
|
||||||
|
name, _ := os.Hostname()
|
||||||
|
for len(name) < 8 {
|
||||||
|
name = fmt.Sprintf("%s%d", name, rand.Int()%10)
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
const EQUAL int = 0
|
||||||
|
const GREATER int = 1
|
||||||
|
const LESS int = -1
|
||||||
|
|
||||||
|
func compareVersion(v1, v2 string) int {
|
||||||
|
if v1 == v2 {
|
||||||
|
return EQUAL
|
||||||
|
}
|
||||||
|
v1Arr := strings.Split(v1, ".")
|
||||||
|
v2Arr := strings.Split(v2, ".")
|
||||||
|
for i, subVer := range v1Arr {
|
||||||
|
if len(v2Arr) <= i {
|
||||||
|
return GREATER
|
||||||
|
}
|
||||||
|
subv1, _ := strconv.Atoi(subVer)
|
||||||
|
subv2, _ := strconv.Atoi(v2Arr[i])
|
||||||
|
if subv1 > subv2 {
|
||||||
|
return GREATER
|
||||||
|
}
|
||||||
|
if subv1 < subv2 {
|
||||||
|
return LESS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return LESS
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsIPv6(address string) bool {
|
||||||
|
return strings.Count(address, ":") >= 2
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,3 +43,29 @@ func TestAESCBC(t *testing.T) {
|
|||||||
func TestNetInfo(t *testing.T) {
|
func TestNetInfo(t *testing.T) {
|
||||||
log.Println(netInfo())
|
log.Println(netInfo())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func assertCompareVersion(t *testing.T, v1 string, v2 string, result int) {
|
||||||
|
if compareVersion(v1, v2) != result {
|
||||||
|
t.Errorf("compare version %s %s fail\n", v1, v2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestCompareVersion(t *testing.T) {
|
||||||
|
// test =
|
||||||
|
assertCompareVersion(t, "0.98.0", "0.98.0", EQUAL)
|
||||||
|
assertCompareVersion(t, "0.98", "0.98", EQUAL)
|
||||||
|
assertCompareVersion(t, "1.4.0", "1.4.0", EQUAL)
|
||||||
|
assertCompareVersion(t, "1.5.0", "1.5.0", EQUAL)
|
||||||
|
// test >
|
||||||
|
assertCompareVersion(t, "0.98.0.22345", "0.98.0.12345", GREATER)
|
||||||
|
assertCompareVersion(t, "1.98.0.12345", "0.98", GREATER)
|
||||||
|
assertCompareVersion(t, "10.98.0.12345", "9.98.0.12345", GREATER)
|
||||||
|
assertCompareVersion(t, "1.4.0", "0.98.0.12345", GREATER)
|
||||||
|
assertCompareVersion(t, "1.4", "0.98.0.12345", GREATER)
|
||||||
|
assertCompareVersion(t, "1", "0.98.0.12345", GREATER)
|
||||||
|
// test <
|
||||||
|
assertCompareVersion(t, "0.98.0.12345", "0.98.0.12346", LESS)
|
||||||
|
assertCompareVersion(t, "9.98.0.12345", "10.98.0.12345", LESS)
|
||||||
|
assertCompareVersion(t, "1.4.2", "1.5.0", LESS)
|
||||||
|
assertCompareVersion(t, "", "1.5.0", LESS)
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
234
config.go
@@ -2,88 +2,264 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var gConf Config
|
var gConf Config
|
||||||
|
|
||||||
|
const IntValueNotSet int = -99999999
|
||||||
|
|
||||||
type AppConfig struct {
|
type AppConfig struct {
|
||||||
// required
|
// required
|
||||||
Protocol string
|
AppName string
|
||||||
SrcPort int
|
Protocol string
|
||||||
PeerNode string
|
SrcPort int
|
||||||
DstPort int
|
PeerNode string
|
||||||
DstHost string
|
DstPort int
|
||||||
PeerUser string
|
DstHost string
|
||||||
PeerPassword string
|
PeerUser string
|
||||||
|
Enabled int // default:1
|
||||||
// runtime info
|
// runtime info
|
||||||
|
peerVersion string
|
||||||
peerToken uint64
|
peerToken uint64
|
||||||
peerNatType int
|
peerNatType int
|
||||||
|
hasIPv4 int
|
||||||
|
IPv6 string
|
||||||
|
hasUPNPorNATPMP int
|
||||||
peerIP string
|
peerIP string
|
||||||
peerConeNatPort int
|
peerConeNatPort int
|
||||||
retryNum int
|
retryNum int
|
||||||
retryTime time.Time
|
retryTime time.Time
|
||||||
|
nextRetryTime time.Time
|
||||||
shareBandwidth int
|
shareBandwidth int
|
||||||
|
errMsg string
|
||||||
|
connectTime time.Time
|
||||||
|
fromToken uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add loglevel, maxlogfilesize
|
// TODO: add loglevel, maxlogfilesize
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Network NetworkConfig `json:"network"`
|
Network NetworkConfig `json:"network"`
|
||||||
Apps []AppConfig `json:"apps"`
|
Apps []*AppConfig `json:"apps"`
|
||||||
|
LogLevel int
|
||||||
daemonMode bool
|
daemonMode bool
|
||||||
|
mtx sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) add(app AppConfig) {
|
func (c *Config) switchApp(app AppConfig, enabled int) {
|
||||||
if app.SrcPort == 0 || app.DstPort == 0 {
|
c.mtx.Lock()
|
||||||
return
|
defer c.mtx.Unlock()
|
||||||
}
|
|
||||||
for i := 0; i < len(c.Apps); i++ {
|
for i := 0; i < len(c.Apps); i++ {
|
||||||
if c.Apps[i].Protocol == app.Protocol && c.Apps[i].SrcPort == app.SrcPort {
|
if c.Apps[i].Protocol == app.Protocol && c.Apps[i].SrcPort == app.SrcPort {
|
||||||
|
c.Apps[i].Enabled = enabled
|
||||||
|
c.Apps[i].retryNum = 0
|
||||||
|
c.Apps[i].nextRetryTime = time.Now()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) add(app AppConfig, override bool) {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
if app.SrcPort == 0 || app.DstPort == 0 {
|
||||||
|
gLog.Println(LvERROR, "invalid app ", app)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if override {
|
||||||
|
for i := 0; i < len(c.Apps); i++ {
|
||||||
|
if c.Apps[i].Protocol == app.Protocol && c.Apps[i].SrcPort == app.SrcPort {
|
||||||
|
c.Apps[i] = &app // override it
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Apps = append(c.Apps, &app)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) delete(app AppConfig) {
|
||||||
|
if app.SrcPort == 0 || app.DstPort == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
for i := 0; i < len(c.Apps); i++ {
|
||||||
|
if c.Apps[i].Protocol == app.Protocol && c.Apps[i].SrcPort == app.SrcPort {
|
||||||
|
c.Apps = append(c.Apps[:i], c.Apps[i+1:]...)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.Apps = append(c.Apps, app)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) save() {
|
func (c *Config) save() {
|
||||||
data, _ := json.MarshalIndent(c, "", "")
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
data, _ := json.MarshalIndent(c, "", " ")
|
||||||
err := ioutil.WriteFile("config.json", data, 0644)
|
err := ioutil.WriteFile("config.json", data, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, "save config.json error:", err)
|
gLog.Println(LvERROR, "save config.json error:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) load() error {
|
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")
|
data, err := ioutil.ReadFile("config.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, "read config.json error:", err)
|
// gLog.Println(LevelERROR, "read config.json error:", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(data, &c)
|
err = json.Unmarshal(data, &c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, "parse config.json error:", err)
|
gLog.Println(LvERROR, "parse config.json error:", err)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Config) setToken(token uint64) {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
c.Network.Token = token
|
||||||
|
}
|
||||||
|
func (c *Config) setUser(user string) {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
c.Network.User = user
|
||||||
|
}
|
||||||
|
func (c *Config) setNode(node string) {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
c.Network.Node = node
|
||||||
|
}
|
||||||
|
func (c *Config) setShareBandwidth(bw int) {
|
||||||
|
c.mtx.Lock()
|
||||||
|
defer c.mtx.Unlock()
|
||||||
|
c.Network.ShareBandwidth = bw
|
||||||
|
}
|
||||||
|
|
||||||
type NetworkConfig struct {
|
type NetworkConfig struct {
|
||||||
// local info
|
// local info
|
||||||
Node string
|
Token uint64
|
||||||
User string
|
Node string
|
||||||
Password string
|
User string
|
||||||
NoShare bool
|
localIP string
|
||||||
localIP string
|
mac string
|
||||||
ipv6 string
|
os string
|
||||||
hostName string
|
publicIP string
|
||||||
mac string
|
natType int
|
||||||
os string
|
hasIPv4 int
|
||||||
publicIP string
|
IPv6 string
|
||||||
natType int
|
hasUPNPorNATPMP int
|
||||||
ShareBandwidth int
|
ShareBandwidth int
|
||||||
// server info
|
// server info
|
||||||
ServerHost string
|
ServerHost string
|
||||||
ServerPort int
|
ServerPort int
|
||||||
UDPPort1 int
|
UDPPort1 int
|
||||||
UDPPort2 int
|
UDPPort2 int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseParams(subCommand string) {
|
||||||
|
fset := flag.NewFlagSet(subCommand, flag.ExitOnError)
|
||||||
|
serverHost := fset.String("serverhost", "api.openp2p.cn", "server host ")
|
||||||
|
// 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")
|
||||||
|
peerNode := fset.String("peernode", "", "peer node name that you want to connect")
|
||||||
|
dstIP := fset.String("dstip", "127.0.0.1", "destination ip ")
|
||||||
|
dstPort := fset.Int("dstport", 0, "destination port ")
|
||||||
|
srcPort := fset.Int("srcport", 0, "source port ")
|
||||||
|
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")
|
||||||
|
if subCommand == "" { // no subcommand
|
||||||
|
fset.Parse(os.Args[1:])
|
||||||
|
} else {
|
||||||
|
fset.Parse(os.Args[2:])
|
||||||
|
}
|
||||||
|
|
||||||
|
config := AppConfig{Enabled: 1}
|
||||||
|
config.PeerNode = *peerNode
|
||||||
|
config.DstHost = *dstIP
|
||||||
|
config.DstPort = *dstPort
|
||||||
|
config.SrcPort = *srcPort
|
||||||
|
config.Protocol = *protocol
|
||||||
|
config.AppName = *appName
|
||||||
|
if !*newconfig {
|
||||||
|
gConf.load() // load old config. otherwise will clear all apps
|
||||||
|
}
|
||||||
|
gConf.LogLevel = *logLevel
|
||||||
|
if config.SrcPort != 0 {
|
||||||
|
gConf.add(config, true)
|
||||||
|
}
|
||||||
|
// gConf.mtx.Lock() // when calling this func it's single-thread no lock
|
||||||
|
gConf.daemonMode = *daemonMode
|
||||||
|
// spec paramters in commandline will always be used
|
||||||
|
fset.Visit(func(f *flag.Flag) {
|
||||||
|
if f.Name == "sharebandwidth" {
|
||||||
|
gConf.Network.ShareBandwidth = *shareBandwidth
|
||||||
|
}
|
||||||
|
if f.Name == "node" {
|
||||||
|
gConf.Network.Node = *node
|
||||||
|
}
|
||||||
|
if f.Name == "serverhost" {
|
||||||
|
gConf.Network.ServerHost = *serverHost
|
||||||
|
}
|
||||||
|
if f.Name == "loglevel" {
|
||||||
|
gConf.LogLevel = *logLevel
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if gConf.Network.ServerHost == "" {
|
||||||
|
gConf.Network.ServerHost = *serverHost
|
||||||
|
}
|
||||||
|
if *token != 0 {
|
||||||
|
gConf.Network.Token = *token
|
||||||
|
}
|
||||||
|
if *node != "" {
|
||||||
|
if len(*node) < 8 {
|
||||||
|
gLog.Println(LvERROR, ErrNodeTooShort)
|
||||||
|
os.Exit(9)
|
||||||
|
}
|
||||||
|
gConf.Network.Node = *node
|
||||||
|
} else {
|
||||||
|
if gConf.Network.Node == "" { // if node name not set. use os.Hostname
|
||||||
|
gConf.Network.Node = defaultNodeName()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if gConf.LogLevel == IntValueNotSet {
|
||||||
|
gConf.LogLevel = *logLevel
|
||||||
|
}
|
||||||
|
if gConf.Network.ShareBandwidth == IntValueNotSet {
|
||||||
|
gConf.Network.ShareBandwidth = *shareBandwidth
|
||||||
|
}
|
||||||
|
|
||||||
|
gConf.Network.ServerPort = 27183
|
||||||
|
gConf.Network.UDPPort1 = 27182
|
||||||
|
gConf.Network.UDPPort2 = 27183
|
||||||
|
gLog.setLevel(LogLevel(gConf.LogLevel))
|
||||||
|
if *notVerbose {
|
||||||
|
gLog.setMode(LogFile)
|
||||||
|
}
|
||||||
|
// gConf.mtx.Unlock()
|
||||||
|
gConf.save()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conf *AppConfig) isSupportTCP(pnConf NetworkConfig) bool {
|
||||||
|
if conf.peerVersion == "" || compareVersion(conf.peerVersion, LeastSupportTCPVersion) == LESS {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if pnConf.hasIPv4 == 1 || pnConf.hasUPNPorNATPMP == 1 || conf.hasIPv4 == 1 || conf.hasUPNPorNATPMP == 1 || (IsIPv6(pnConf.IPv6) && IsIPv6(conf.IPv6)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
166
daemon.go
@@ -1,9 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
@@ -17,35 +15,34 @@ type daemon struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *daemon) Start(s service.Service) error {
|
func (d *daemon) Start(s service.Service) error {
|
||||||
gLog.Println(LevelINFO, "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(LevelINFO, "service stop")
|
gLog.Println(LvINFO, "service stop")
|
||||||
d.running = false
|
d.running = false
|
||||||
if d.proc != nil {
|
if d.proc != nil {
|
||||||
gLog.Println(LevelINFO, "stop worker")
|
gLog.Println(LvINFO, "stop worker")
|
||||||
d.proc.Kill()
|
d.proc.Kill()
|
||||||
}
|
}
|
||||||
if service.Interactive() {
|
if service.Interactive() {
|
||||||
gLog.Println(LevelINFO, "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(LevelINFO, "daemon run start")
|
gLog.Println(LvINFO, "daemon run start")
|
||||||
defer gLog.Println(LevelINFO, "daemon run end")
|
defer gLog.Println(LvINFO, "daemon run end")
|
||||||
os.Chdir(filepath.Dir(os.Args[0])) // for system service
|
|
||||||
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(LevelINFO, mydir)
|
gLog.Println(LvINFO, mydir)
|
||||||
conf := &service.Config{
|
conf := &service.Config{
|
||||||
Name: ProducnName,
|
Name: ProducnName,
|
||||||
DisplayName: ProducnName,
|
DisplayName: ProducnName,
|
||||||
@@ -63,22 +60,35 @@ func (d *daemon) run() {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
args = append(args, "-bydaemon")
|
args = append(args, "-nv")
|
||||||
for {
|
for {
|
||||||
// start worker
|
// start worker
|
||||||
gLog.Println(LevelINFO, "start worker process")
|
tmpDump := filepath.Join("log", "dump.log.tmp")
|
||||||
execSpec := &os.ProcAttr{Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}}
|
dumpFile := filepath.Join("log", "dump.log")
|
||||||
|
f, err := os.Create(filepath.Join(tmpDump))
|
||||||
|
if err != nil {
|
||||||
|
gLog.Printf(LvERROR, "start worker error:%s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gLog.Println(LvINFO, "start worker process, args:", args)
|
||||||
|
execSpec := &os.ProcAttr{Env: append(os.Environ(), "GOTRACEBACK=crash"), Files: []*os.File{os.Stdin, os.Stdout, f}}
|
||||||
p, err := os.StartProcess(binPath, args, execSpec)
|
p, err := os.StartProcess(binPath, args, execSpec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Printf(LevelERROR, "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()
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
err = os.Rename(tmpDump, dumpFile)
|
||||||
|
if err != nil {
|
||||||
|
gLog.Printf(LvERROR, "rename dump error:%s", err)
|
||||||
|
}
|
||||||
if !d.running {
|
if !d.running {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
gLog.Printf(LevelERROR, "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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,129 +113,3 @@ func (d *daemon) Control(ctrlComm string, exeAbsPath string, args []string) erro
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// examples:
|
|
||||||
// listen:
|
|
||||||
// ./openp2p install -node hhd1207-222 -user tenderiron -password 13760636579 -noshare
|
|
||||||
// listen and build p2papp:
|
|
||||||
// ./openp2p install -node hhd1207-222 -user tenderiron -password 13760636579 -noshare -peernode hhdhome-n1 -dstip 127.0.0.1 -dstport 50022 -protocol tcp -srcport 22
|
|
||||||
func install() {
|
|
||||||
gLog = InitLogger(filepath.Dir(os.Args[0]), "openp2p-install", LevelDEBUG, 1024*1024, LogConsole)
|
|
||||||
// save config file
|
|
||||||
installFlag := flag.NewFlagSet("install", flag.ExitOnError)
|
|
||||||
serverHost := installFlag.String("serverhost", "api.openp2p.cn", "server host ")
|
|
||||||
// serverHost := flag.String("serverhost", "127.0.0.1", "server host ") // for debug
|
|
||||||
user := installFlag.String("user", "", "user name. 8-31 characters")
|
|
||||||
node := installFlag.String("node", "", "node name. 8-31 characters")
|
|
||||||
password := installFlag.String("password", "", "user password. 8-31 characters")
|
|
||||||
peerNode := installFlag.String("peernode", "", "peer node name that you want to connect")
|
|
||||||
peerUser := installFlag.String("peeruser", "", "peer node user (default peeruser=user)")
|
|
||||||
peerPassword := installFlag.String("peerpassword", "", "peer node password (default peerpassword=password)")
|
|
||||||
dstIP := installFlag.String("dstip", "127.0.0.1", "destination ip ")
|
|
||||||
dstPort := installFlag.Int("dstport", 0, "destination port ")
|
|
||||||
srcPort := installFlag.Int("srcport", 0, "source port ")
|
|
||||||
protocol := installFlag.String("protocol", "tcp", "tcp or udp")
|
|
||||||
noShare := installFlag.Bool("noshare", false, "disable using the huge numbers of shared nodes in OpenP2P network, your connectivity will be weak. also this node will not shared with others")
|
|
||||||
shareBandwidth := installFlag.Int("sharebandwidth", 10, "N mbps share bandwidth limit, private node no limit")
|
|
||||||
// logLevel := installFlag.Int("loglevel", 1, "0:debug 1:info 2:warn 3:error")
|
|
||||||
installFlag.Parse(os.Args[2:])
|
|
||||||
checkParams(*node, *user, *password)
|
|
||||||
gConf.Network.ServerHost = *serverHost
|
|
||||||
gConf.Network.User = *user
|
|
||||||
gConf.Network.Node = *node
|
|
||||||
gConf.Network.Password = *password
|
|
||||||
gConf.Network.ServerPort = 27182
|
|
||||||
gConf.Network.UDPPort1 = 27182
|
|
||||||
gConf.Network.UDPPort2 = 27183
|
|
||||||
gConf.Network.NoShare = *noShare
|
|
||||||
gConf.Network.ShareBandwidth = *shareBandwidth
|
|
||||||
config := AppConfig{}
|
|
||||||
config.PeerNode = *peerNode
|
|
||||||
config.PeerUser = *peerUser
|
|
||||||
config.PeerPassword = *peerPassword
|
|
||||||
config.DstHost = *dstIP
|
|
||||||
config.DstPort = *dstPort
|
|
||||||
config.SrcPort = *srcPort
|
|
||||||
config.Protocol = *protocol
|
|
||||||
gConf.add(config)
|
|
||||||
os.MkdirAll(defaultInstallPath, 0775)
|
|
||||||
err := os.Chdir(defaultInstallPath)
|
|
||||||
if err != nil {
|
|
||||||
gLog.Println(LevelERROR, "cd error:", err)
|
|
||||||
}
|
|
||||||
gConf.save()
|
|
||||||
|
|
||||||
// copy files
|
|
||||||
|
|
||||||
targetPath := filepath.Join(defaultInstallPath, defaultBinName)
|
|
||||||
binPath, _ := os.Executable()
|
|
||||||
src, errFiles := os.Open(binPath) // can not use args[0], on Windows call openp2p is ok(=openp2p.exe)
|
|
||||||
if errFiles != nil {
|
|
||||||
gLog.Printf(LevelERROR, "os.OpenFile %s error:%s", os.Args[0], errFiles)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
dst, errFiles := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0775)
|
|
||||||
if errFiles != nil {
|
|
||||||
gLog.Printf(LevelERROR, "os.OpenFile %s error:%s", targetPath, errFiles)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, errFiles = io.Copy(dst, src)
|
|
||||||
if errFiles != nil {
|
|
||||||
gLog.Printf(LevelERROR, "io.Copy error:%s", errFiles)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
src.Close()
|
|
||||||
dst.Close()
|
|
||||||
|
|
||||||
// install system service
|
|
||||||
d := daemon{}
|
|
||||||
|
|
||||||
// args := []string{""}
|
|
||||||
gLog.Println(LevelINFO, "targetPath:", targetPath)
|
|
||||||
err = d.Control("install", targetPath, []string{"-d", "-f"})
|
|
||||||
if err != nil {
|
|
||||||
gLog.Println(LevelERROR, "install system service error:", err)
|
|
||||||
} else {
|
|
||||||
gLog.Println(LevelINFO, "install system service ok.")
|
|
||||||
}
|
|
||||||
time.Sleep(time.Second * 2)
|
|
||||||
err = d.Control("start", targetPath, []string{"-d", "-f"})
|
|
||||||
if err != nil {
|
|
||||||
gLog.Println(LevelERROR, "start openp2p service error:", err)
|
|
||||||
} else {
|
|
||||||
gLog.Println(LevelINFO, "start openp2p service ok.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func uninstall() {
|
|
||||||
gLog = InitLogger(filepath.Dir(os.Args[0]), "openp2p-install", LevelDEBUG, 1024*1024, LogFileAndConsole)
|
|
||||||
d := daemon{}
|
|
||||||
d.Control("stop", "", nil)
|
|
||||||
err := d.Control("uninstall", "", nil)
|
|
||||||
if err != nil {
|
|
||||||
gLog.Println(LevelERROR, "uninstall system service error:", err)
|
|
||||||
} else {
|
|
||||||
gLog.Println(LevelINFO, "uninstall system service ok.")
|
|
||||||
}
|
|
||||||
binPath := filepath.Join(defaultInstallPath, defaultBinName)
|
|
||||||
os.Remove(binPath + "0")
|
|
||||||
os.Rename(binPath, binPath+"0")
|
|
||||||
os.RemoveAll(defaultInstallPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkParams(node, user, password string) {
|
|
||||||
if len(node) < 8 {
|
|
||||||
gLog.Println(LevelERROR, "node name too short, it must >=8 charaters")
|
|
||||||
os.Exit(9)
|
|
||||||
}
|
|
||||||
if len(user) < 8 {
|
|
||||||
gLog.Println(LevelERROR, "user name too short, it must >=8 charaters")
|
|
||||||
os.Exit(9)
|
|
||||||
}
|
|
||||||
if len(password) < 8 {
|
|
||||||
gLog.Println(LevelERROR, "password too short, it must >=8 charaters")
|
|
||||||
os.Exit(9)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
BIN
doc/images/afterconnect_en.png
Normal file
|
After Width: | Height: | Size: 5.6 MiB |
BIN
doc/images/devices.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
doc/images/devices_en.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
doc/images/install.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
doc/images/install_en.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
doc/images/mstscconnect_en.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
doc/images/newapp.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
doc/images/newapp_en.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
doc/images/newappedit.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
doc/images/newappedit_en.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
doc/images/p2pappok.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
doc/images/p2pappok_en.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 98 KiB |
BIN
doc/images/register.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
doc/images/register_en.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
doc/images/stillrun.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
doc/images/stillrun_en.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
doc/images/win10warn.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
doc/images/win10warn_en.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
@@ -8,6 +8,9 @@ import (
|
|||||||
var (
|
var (
|
||||||
// ErrorS2S string = "s2s is not supported"
|
// ErrorS2S string = "s2s is not supported"
|
||||||
// ErrorHandshake string = "handshake error"
|
// ErrorHandshake string = "handshake error"
|
||||||
ErrorS2S = errors.New("s2s is not supported")
|
ErrorS2S = errors.New("s2s is not supported")
|
||||||
ErrorHandshake = errors.New("handshake error")
|
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")
|
||||||
)
|
)
|
||||||
|
|||||||
21
go.mod
@@ -1,10 +1,27 @@
|
|||||||
module openp2p
|
module openp2p
|
||||||
|
|
||||||
go 1.16
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/kardianos/service v1.2.0
|
github.com/kardianos/service v1.2.0
|
||||||
github.com/lucas-clemente/quic-go v0.24.0
|
github.com/lucas-clemente/quic-go v0.27.0
|
||||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34
|
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/cheekybits/genny v1.0.0 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||||
|
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
|
||||||
|
github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect
|
||||||
|
github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect
|
||||||
|
github.com/nxadm/tail v1.4.8 // indirect
|
||||||
|
github.com/onsi/ginkgo v1.16.4 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect
|
||||||
|
golang.org/x/mod v0.4.2 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect
|
||||||
|
golang.org/x/tools v0.1.1 // indirect
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||||
|
)
|
||||||
|
|||||||
224
handlepush.go
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
|
||||||
|
pushHead := PushHeader{}
|
||||||
|
err := binary.Read(bytes.NewReader(msg[openP2PHeaderSize:openP2PHeaderSize+PushHeaderSize]), binary.LittleEndian, &pushHead)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
gLog.Printf(LvDEBUG, "handle push msg type:%d, push header:%+v", subType, pushHead)
|
||||||
|
switch subType {
|
||||||
|
case MsgPushConnectReq:
|
||||||
|
req := PushConnectReq{}
|
||||||
|
err := json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req)
|
||||||
|
if err != nil {
|
||||||
|
gLog.Printf(LvERROR, "wrong MsgPushConnectReq:%s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
gLog.Printf(LvINFO, "%s is connecting...", req.From)
|
||||||
|
gLog.Println(LvDEBUG, "push connect response to ", req.From)
|
||||||
|
// 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()) {
|
||||||
|
gLog.Printf(LvINFO, "Access Granted\n")
|
||||||
|
config := AppConfig{}
|
||||||
|
config.peerNatType = req.NatType
|
||||||
|
config.peerConeNatPort = req.ConeNatPort
|
||||||
|
config.peerIP = req.FromIP
|
||||||
|
config.PeerNode = req.From
|
||||||
|
config.peerVersion = req.Version
|
||||||
|
config.fromToken = req.Token
|
||||||
|
config.IPv6 = req.IPv6
|
||||||
|
// share relay node will limit bandwidth
|
||||||
|
if req.Token != pn.config.Token {
|
||||||
|
gLog.Printf(LvINFO, "set share bandwidth %d mbps", pn.config.ShareBandwidth)
|
||||||
|
config.shareBandwidth = pn.config.ShareBandwidth
|
||||||
|
}
|
||||||
|
// go pn.AddTunnel(config, req.ID)
|
||||||
|
go pn.addDirectTunnel(config, req.ID)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
gLog.Println(LvERROR, "Access Denied:", req.From)
|
||||||
|
rsp := PushConnectRsp{
|
||||||
|
Error: 1,
|
||||||
|
Detail: fmt.Sprintf("connect to %s error: Access Denied", pn.config.Node),
|
||||||
|
To: req.From,
|
||||||
|
From: pn.config.Node,
|
||||||
|
}
|
||||||
|
pn.push(req.From, MsgPushConnectRsp, rsp)
|
||||||
|
case MsgPushRsp:
|
||||||
|
rsp := PushRsp{}
|
||||||
|
err := json.Unmarshal(msg[openP2PHeaderSize:], &rsp)
|
||||||
|
if err != nil {
|
||||||
|
gLog.Printf(LvERROR, "wrong pushRsp:%s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rsp.Error == 0 {
|
||||||
|
gLog.Printf(LvDEBUG, "push ok, detail:%s", rsp.Detail)
|
||||||
|
} else {
|
||||||
|
gLog.Printf(LvERROR, "push error:%d, detail:%s", rsp.Error, rsp.Detail)
|
||||||
|
}
|
||||||
|
case MsgPushAddRelayTunnelReq:
|
||||||
|
req := AddRelayTunnelReq{}
|
||||||
|
err := json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req)
|
||||||
|
if err != nil {
|
||||||
|
gLog.Printf(LvERROR, "wrong RelayNodeRsp:%s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
config := AppConfig{}
|
||||||
|
config.PeerNode = req.RelayName
|
||||||
|
config.peerToken = req.RelayToken
|
||||||
|
go func(r AddRelayTunnelReq) {
|
||||||
|
t, errDt := pn.addDirectTunnel(config, 0)
|
||||||
|
if errDt == nil {
|
||||||
|
// notify peer relay ready
|
||||||
|
msg := TunnelMsg{ID: t.id}
|
||||||
|
pn.push(r.From, MsgPushAddRelayTunnelRsp, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
}(req)
|
||||||
|
case MsgPushAPPKey:
|
||||||
|
req := APPKeySync{}
|
||||||
|
err := json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req)
|
||||||
|
if err != nil {
|
||||||
|
gLog.Printf(LvERROR, "wrong APPKeySync:%s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
SaveKey(req.AppID, req.AppKey)
|
||||||
|
case MsgPushUpdate:
|
||||||
|
gLog.Println(LvINFO, "MsgPushUpdate")
|
||||||
|
update() // download new version first, then exec ./openp2p update
|
||||||
|
targetPath := filepath.Join(defaultInstallPath, defaultBinName)
|
||||||
|
args := []string{"update"}
|
||||||
|
env := os.Environ()
|
||||||
|
cmd := exec.Command(targetPath, args...)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Env = env
|
||||||
|
err := cmd.Run()
|
||||||
|
if err == nil {
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
case MsgPushRestart:
|
||||||
|
gLog.Println(LvINFO, "MsgPushRestart")
|
||||||
|
os.Exit(0)
|
||||||
|
return err
|
||||||
|
case MsgPushReportApps:
|
||||||
|
gLog.Println(LvINFO, "MsgPushReportApps")
|
||||||
|
req := ReportApps{}
|
||||||
|
gConf.mtx.Lock()
|
||||||
|
defer gConf.mtx.Unlock()
|
||||||
|
for _, config := range gConf.Apps {
|
||||||
|
appActive := 0
|
||||||
|
relayNode := ""
|
||||||
|
relayMode := ""
|
||||||
|
linkMode := LinkModeUDPPunch
|
||||||
|
i, ok := pn.apps.Load(fmt.Sprintf("%s%d", config.Protocol, config.SrcPort))
|
||||||
|
if ok {
|
||||||
|
app := i.(*p2pApp)
|
||||||
|
if app.isActive() {
|
||||||
|
appActive = 1
|
||||||
|
}
|
||||||
|
relayNode = app.relayNode
|
||||||
|
relayMode = app.relayMode
|
||||||
|
linkMode = app.tunnel.linkMode
|
||||||
|
}
|
||||||
|
appInfo := AppInfo{
|
||||||
|
AppName: config.AppName,
|
||||||
|
Error: config.errMsg,
|
||||||
|
Protocol: config.Protocol,
|
||||||
|
SrcPort: config.SrcPort,
|
||||||
|
RelayNode: relayNode,
|
||||||
|
RelayMode: relayMode,
|
||||||
|
LinkMode: linkMode,
|
||||||
|
PeerNode: config.PeerNode,
|
||||||
|
DstHost: config.DstHost,
|
||||||
|
DstPort: config.DstPort,
|
||||||
|
PeerUser: config.PeerUser,
|
||||||
|
PeerIP: config.peerIP,
|
||||||
|
PeerNatType: config.peerNatType,
|
||||||
|
RetryTime: config.retryTime.Local().Format("2006-01-02T15:04:05-0700"),
|
||||||
|
ConnectTime: config.connectTime.Local().Format("2006-01-02T15:04:05-0700"),
|
||||||
|
IsActive: appActive,
|
||||||
|
Enabled: config.Enabled,
|
||||||
|
}
|
||||||
|
req.Apps = append(req.Apps, appInfo)
|
||||||
|
}
|
||||||
|
pn.write(MsgReport, MsgReportApps, &req)
|
||||||
|
case MsgPushEditApp:
|
||||||
|
gLog.Println(LvINFO, "MsgPushEditApp")
|
||||||
|
newApp := AppInfo{}
|
||||||
|
err := json.Unmarshal(msg[openP2PHeaderSize:], &newApp)
|
||||||
|
if err != nil {
|
||||||
|
gLog.Printf(LvERROR, "wrong MsgPushEditApp:%s %s", err, string(msg[openP2PHeaderSize:]))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
oldConf := AppConfig{Enabled: 1}
|
||||||
|
// protocol0+srcPort0 exist, delApp
|
||||||
|
oldConf.AppName = newApp.AppName
|
||||||
|
oldConf.Protocol = newApp.Protocol0
|
||||||
|
oldConf.SrcPort = newApp.SrcPort0
|
||||||
|
oldConf.PeerNode = newApp.PeerNode
|
||||||
|
oldConf.DstHost = newApp.DstHost
|
||||||
|
oldConf.DstPort = newApp.DstPort
|
||||||
|
|
||||||
|
gConf.delete(oldConf)
|
||||||
|
// AddApp
|
||||||
|
newConf := oldConf
|
||||||
|
newConf.Protocol = newApp.Protocol
|
||||||
|
newConf.SrcPort = newApp.SrcPort
|
||||||
|
gConf.add(newConf, false)
|
||||||
|
gConf.save() // save quickly for the next request reportApplist
|
||||||
|
pn.DeleteApp(oldConf) // DeleteApp may cost some times, execute at the end
|
||||||
|
// autoReconnect will auto AddApp
|
||||||
|
// pn.AddApp(config)
|
||||||
|
// TODO: report result
|
||||||
|
case MsgPushEditNode:
|
||||||
|
gLog.Println(LvINFO, "MsgPushEditNode")
|
||||||
|
req := EditNode{}
|
||||||
|
err := json.Unmarshal(msg[openP2PHeaderSize:], &req)
|
||||||
|
if err != nil {
|
||||||
|
gLog.Printf(LvERROR, "wrong MsgPushEditNode:%s %s", err, string(msg[openP2PHeaderSize:]))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
gConf.setNode(req.NewName)
|
||||||
|
gConf.setShareBandwidth(req.Bandwidth)
|
||||||
|
gConf.save()
|
||||||
|
// TODO: hot reload
|
||||||
|
os.Exit(0)
|
||||||
|
case MsgPushSwitchApp:
|
||||||
|
gLog.Println(LvINFO, "MsgPushSwitchApp")
|
||||||
|
app := AppInfo{}
|
||||||
|
err := json.Unmarshal(msg[openP2PHeaderSize:], &app)
|
||||||
|
if err != nil {
|
||||||
|
gLog.Printf(LvERROR, "wrong MsgPushSwitchApp:%s %s", err, string(msg[openP2PHeaderSize:]))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
config := AppConfig{Enabled: app.Enabled, SrcPort: app.SrcPort, Protocol: app.Protocol}
|
||||||
|
gLog.Println(LvINFO, app.AppName, " switch to ", app.Enabled)
|
||||||
|
gConf.switchApp(config, app.Enabled)
|
||||||
|
if app.Enabled == 0 {
|
||||||
|
// disable APP
|
||||||
|
pn.DeleteApp(config)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
pn.msgMapMtx.Lock()
|
||||||
|
ch := pn.msgMap[pushHead.From]
|
||||||
|
pn.msgMapMtx.Unlock()
|
||||||
|
ch <- msg
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
58
holepunch.go
@@ -11,8 +11,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func handshakeC2C(t *P2PTunnel) (err error) {
|
func handshakeC2C(t *P2PTunnel) (err error) {
|
||||||
gLog.Printf(LevelDEBUG, "handshakeC2C %s:%d:%d to %s:%d", t.pn.config.Node, t.coneLocalPort, t.coneNatPort, t.config.peerIP, t.config.peerConeNatPort)
|
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(LevelDEBUG, "handshakeC2C ok")
|
defer gLog.Printf(LvDEBUG, "handshakeC2C ok")
|
||||||
conn, err := net.ListenUDP("udp", t.la)
|
conn, err := net.ListenUDP("udp", t.la)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -20,40 +20,40 @@ func handshakeC2C(t *P2PTunnel) (err error) {
|
|||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
_, err = UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id})
|
_, err = UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelDEBUG, "handshakeC2C write MsgPunchHandshake error:", err)
|
gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshake error:", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ra, head, _, _, err := UDPRead(conn, 5000)
|
ra, head, _, _, err := UDPRead(conn, 5000)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
time.Sleep(time.Millisecond * 200)
|
time.Sleep(time.Millisecond * 200)
|
||||||
gLog.Println(LevelDEBUG, err, ", return this error when ip was not reachable, retry read")
|
gLog.Println(LvDEBUG, err, ", return this error when ip was not reachable, retry read")
|
||||||
ra, head, _, _, err = UDPRead(conn, 5000)
|
ra, head, _, _, err = UDPRead(conn, 5000)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelDEBUG, "handshakeC2C read MsgPunchHandshake error:", err)
|
gLog.Println(LvDEBUG, "handshakeC2C read MsgPunchHandshake error:", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.ra, _ = net.ResolveUDPAddr("udp", ra.String())
|
t.ra, _ = net.ResolveUDPAddr("udp", ra.String())
|
||||||
// cone server side
|
// cone server side
|
||||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake {
|
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake {
|
||||||
gLog.Printf(LevelDEBUG, "read %d handshake ", t.id)
|
gLog.Printf(LvDEBUG, "read %d handshake ", t.id)
|
||||||
UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||||
_, head, _, _, err = UDPRead(conn, 5000)
|
_, head, _, _, err = UDPRead(conn, 5000)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err)
|
gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
|
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
|
||||||
gLog.Printf(LevelDEBUG, "read %d handshake ack ", t.id)
|
gLog.Printf(LvDEBUG, "read %d handshake ack ", t.id)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// cone client side will only read handshake ack
|
// cone client side will only read handshake ack
|
||||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
|
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
|
||||||
gLog.Printf(LevelDEBUG, "read %d handshake ack ", t.id)
|
gLog.Printf(LvDEBUG, "read %d handshake ack ", t.id)
|
||||||
_, err = UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
_, err = UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err)
|
gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -61,8 +61,8 @@ func handshakeC2C(t *P2PTunnel) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handshakeC2S(t *P2PTunnel) error {
|
func handshakeC2S(t *P2PTunnel) error {
|
||||||
gLog.Printf(LevelDEBUG, "handshakeC2S start")
|
gLog.Printf(LvDEBUG, "handshakeC2S start")
|
||||||
defer gLog.Printf(LevelDEBUG, "handshakeC2S end")
|
defer gLog.Printf(LvDEBUG, "handshakeC2S end")
|
||||||
// even if read timeout, continue handshake
|
// even if read timeout, continue handshake
|
||||||
t.pn.read(t.config.PeerNode, MsgPush, MsgPushHandshakeStart, SymmetricHandshakeAckTimeout)
|
t.pn.read(t.config.PeerNode, MsgPush, MsgPushHandshakeStart, SymmetricHandshakeAckTimeout)
|
||||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
@@ -73,7 +73,7 @@ func handshakeC2S(t *P2PTunnel) error {
|
|||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
go func() error {
|
go func() error {
|
||||||
gLog.Printf(LevelDEBUG, "send symmetric handshake to %s from %d:%d start", t.config.peerIP, t.coneLocalPort, t.coneNatPort)
|
gLog.Printf(LvDEBUG, "send symmetric handshake to %s from %d:%d start", t.config.peerIP, t.coneLocalPort, t.coneNatPort)
|
||||||
for i := 0; i < SymmetricHandshakeNum; i++ {
|
for i := 0; i < SymmetricHandshakeNum; i++ {
|
||||||
// TODO: auto calc cost time
|
// TODO: auto calc cost time
|
||||||
time.Sleep(SymmetricHandshakeInterval)
|
time.Sleep(SymmetricHandshakeInterval)
|
||||||
@@ -83,35 +83,35 @@ func handshakeC2S(t *P2PTunnel) error {
|
|||||||
}
|
}
|
||||||
_, err = UDPWrite(conn, dst, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id})
|
_, err = UDPWrite(conn, dst, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelDEBUG, "handshakeC2S write MsgPunchHandshake error:", err)
|
gLog.Println(LvDEBUG, "handshakeC2S write MsgPunchHandshake error:", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gLog.Println(LevelDEBUG, "send symmetric handshake end")
|
gLog.Println(LvDEBUG, "send symmetric handshake end")
|
||||||
return nil
|
return nil
|
||||||
}()
|
}()
|
||||||
deadline := time.Now().Add(SymmetricHandshakeAckTimeout)
|
deadline := time.Now().Add(SymmetricHandshakeAckTimeout)
|
||||||
err = conn.SetReadDeadline(deadline)
|
err = conn.SetReadDeadline(deadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, "SymmetricHandshakeAckTimeout SetReadDeadline error")
|
gLog.Println(LvERROR, "SymmetricHandshakeAckTimeout SetReadDeadline error")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// read response of the punching hole ok port
|
// read response of the punching hole ok port
|
||||||
result := make([]byte, 1024)
|
result := make([]byte, 1024)
|
||||||
_, dst, err := conn.ReadFrom(result)
|
_, dst, err := conn.ReadFrom(result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, "handshakeC2S wait timeout")
|
gLog.Println(LvERROR, "handshakeC2S wait timeout")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
head := &openP2PHeader{}
|
head := &openP2PHeader{}
|
||||||
err = binary.Read(bytes.NewReader(result[:openP2PHeaderSize]), binary.LittleEndian, head)
|
err = binary.Read(bytes.NewReader(result[:openP2PHeaderSize]), binary.LittleEndian, head)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, "parse p2pheader error:", err)
|
gLog.Println(LvERROR, "parse p2pheader error:", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.ra, _ = net.ResolveUDPAddr("udp", dst.String())
|
t.ra, _ = net.ResolveUDPAddr("udp", dst.String())
|
||||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
|
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
|
||||||
gLog.Printf(LevelDEBUG, "handshakeC2S read %d handshake ack %s", t.id, dst.String())
|
gLog.Printf(LvDEBUG, "handshakeC2S read %d handshake ack %s", t.id, dst.String())
|
||||||
_, err = UDPWrite(conn, dst, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
_, err = UDPWrite(conn, dst, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -119,11 +119,11 @@ func handshakeC2S(t *P2PTunnel) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handshakeS2C(t *P2PTunnel) error {
|
func handshakeS2C(t *P2PTunnel) error {
|
||||||
gLog.Printf(LevelDEBUG, "handshakeS2C start")
|
gLog.Printf(LvDEBUG, "handshakeS2C start")
|
||||||
defer gLog.Printf(LevelDEBUG, "handshakeS2C end")
|
defer gLog.Printf(LvDEBUG, "handshakeS2C end")
|
||||||
gotCh := make(chan *net.UDPAddr, 5)
|
gotCh := make(chan *net.UDPAddr, 5)
|
||||||
// sequencely udp send handshake, do not parallel send
|
// sequencely udp send handshake, do not parallel send
|
||||||
gLog.Printf(LevelDEBUG, "send symmetric handshake to %s:%d start", t.config.peerIP, t.config.peerConeNatPort)
|
gLog.Printf(LvDEBUG, "send symmetric handshake to %s:%d start", t.config.peerIP, t.config.peerConeNatPort)
|
||||||
gotIt := false
|
gotIt := false
|
||||||
gotMtx := sync.Mutex{}
|
gotMtx := sync.Mutex{}
|
||||||
for i := 0; i < SymmetricHandshakeNum; i++ {
|
for i := 0; i < SymmetricHandshakeNum; i++ {
|
||||||
@@ -132,7 +132,7 @@ func handshakeS2C(t *P2PTunnel) error {
|
|||||||
go func(t *P2PTunnel) error {
|
go func(t *P2PTunnel) error {
|
||||||
conn, err := net.ListenUDP("udp", nil)
|
conn, err := net.ListenUDP("udp", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Printf(LevelDEBUG, "listen error")
|
gLog.Printf(LvDEBUG, "listen error")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
@@ -150,15 +150,15 @@ func handshakeS2C(t *P2PTunnel) error {
|
|||||||
gotIt = true
|
gotIt = true
|
||||||
t.la, _ = net.ResolveUDPAddr("udp", conn.LocalAddr().String())
|
t.la, _ = net.ResolveUDPAddr("udp", conn.LocalAddr().String())
|
||||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake {
|
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake {
|
||||||
gLog.Printf(LevelDEBUG, "handshakeS2C read %d handshake ", t.id)
|
gLog.Printf(LvDEBUG, "handshakeS2C read %d handshake ", t.id)
|
||||||
UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||||
_, head, _, _, err = UDPRead(conn, 5000)
|
_, head, _, _, err = UDPRead(conn, 5000)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelDEBUG, "handshakeS2C handshake error")
|
gLog.Println(LvDEBUG, "handshakeS2C handshake error")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
|
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
|
||||||
gLog.Printf(LevelDEBUG, "handshakeS2C read %d handshake ack %s", t.id, conn.LocalAddr().String())
|
gLog.Printf(LvDEBUG, "handshakeS2C read %d handshake ack %s", t.id, conn.LocalAddr().String())
|
||||||
gotCh <- t.la
|
gotCh <- t.la
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -166,15 +166,15 @@ func handshakeS2C(t *P2PTunnel) error {
|
|||||||
return nil
|
return nil
|
||||||
}(t)
|
}(t)
|
||||||
}
|
}
|
||||||
gLog.Printf(LevelDEBUG, "send symmetric handshake end")
|
gLog.Printf(LvDEBUG, "send symmetric handshake end")
|
||||||
gLog.Println(LevelDEBUG, "handshakeS2C ready, notify peer connect")
|
gLog.Println(LvDEBUG, "handshakeS2C ready, notify peer connect")
|
||||||
t.pn.push(t.config.PeerNode, MsgPushHandshakeStart, TunnelMsg{ID: t.id})
|
t.pn.push(t.config.PeerNode, MsgPushHandshakeStart, TunnelMsg{ID: t.id})
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-time.After(SymmetricHandshakeAckTimeout):
|
case <-time.After(SymmetricHandshakeAckTimeout):
|
||||||
return fmt.Errorf("wait handshake failed")
|
return fmt.Errorf("wait handshake failed")
|
||||||
case la := <-gotCh:
|
case la := <-gotCh:
|
||||||
gLog.Println(LevelDEBUG, "symmetric handshake ok", la)
|
gLog.Println(LvDEBUG, "symmetric handshake ok", la)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
127
install.go
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// examples:
|
||||||
|
// listen:
|
||||||
|
// ./openp2p install -node hhd1207-222 -token YOUR-TOKEN -sharebandwidth 0
|
||||||
|
// listen and build p2papp:
|
||||||
|
// ./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, "install start")
|
||||||
|
defer gLog.Println(LvINFO, "install end")
|
||||||
|
// auto uninstall
|
||||||
|
err := os.MkdirAll(defaultInstallPath, 0775)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
gLog.Printf(LvERROR, "MkdirAll %s error:%s", defaultInstallPath, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = os.Chdir(defaultInstallPath)
|
||||||
|
if err != nil {
|
||||||
|
gLog.Println(LvERROR, "cd error:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall()
|
||||||
|
// save config file
|
||||||
|
parseParams("install")
|
||||||
|
targetPath := filepath.Join(defaultInstallPath, defaultBinName)
|
||||||
|
d := daemon{}
|
||||||
|
// copy files
|
||||||
|
|
||||||
|
binPath, _ := os.Executable()
|
||||||
|
src, errFiles := os.Open(binPath) // can not use args[0], on Windows call openp2p is ok(=openp2p.exe)
|
||||||
|
if errFiles != nil {
|
||||||
|
gLog.Printf(LvERROR, "os.OpenFile %s error:%s", os.Args[0], errFiles)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dst, errFiles := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0775)
|
||||||
|
if errFiles != nil {
|
||||||
|
gLog.Printf(LvERROR, "os.OpenFile %s error:%s", targetPath, errFiles)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, errFiles = io.Copy(dst, src)
|
||||||
|
if errFiles != nil {
|
||||||
|
gLog.Printf(LvERROR, "io.Copy error:%s", errFiles)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
src.Close()
|
||||||
|
dst.Close()
|
||||||
|
|
||||||
|
// install system service
|
||||||
|
gLog.Println(LvINFO, "targetPath:", targetPath)
|
||||||
|
err = d.Control("install", targetPath, []string{"-d"})
|
||||||
|
if err == nil {
|
||||||
|
gLog.Println(LvINFO, "install system service ok.")
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second * 2)
|
||||||
|
err = d.Control("start", targetPath, []string{"-d"})
|
||||||
|
if err != nil {
|
||||||
|
gLog.Println(LvERROR, "start openp2p service error:", err)
|
||||||
|
} else {
|
||||||
|
gLog.Println(LvINFO, "start openp2p service ok.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func installByFilename() {
|
||||||
|
params := strings.Split(filepath.Base(os.Args[0]), "-")
|
||||||
|
if len(params) < 4 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
serverHost := params[1]
|
||||||
|
token := params[2]
|
||||||
|
gLog.Println(LvINFO, "install start")
|
||||||
|
targetPath := os.Args[0]
|
||||||
|
args := []string{"install"}
|
||||||
|
args = append(args, "-serverhost")
|
||||||
|
args = append(args, serverHost)
|
||||||
|
args = append(args, "-token")
|
||||||
|
args = append(args, token)
|
||||||
|
env := os.Environ()
|
||||||
|
cmd := exec.Command(targetPath, args...)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Env = env
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
gLog.Println(LvERROR, "install by filename, start process error:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gLog.Println(LvINFO, "install end")
|
||||||
|
fmt.Println("Press the Any Key to exit")
|
||||||
|
fmt.Scanln()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
func uninstall() {
|
||||||
|
gLog.Println(LvINFO, "uninstall start")
|
||||||
|
defer gLog.Println(LvINFO, "uninstall end")
|
||||||
|
d := daemon{}
|
||||||
|
err := d.Control("stop", "", nil)
|
||||||
|
if err != nil { // service maybe not install
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = d.Control("uninstall", "", nil)
|
||||||
|
if err != nil {
|
||||||
|
gLog.Println(LvERROR, "uninstall system service error:", err)
|
||||||
|
} else {
|
||||||
|
gLog.Println(LvINFO, "uninstall system service ok.")
|
||||||
|
}
|
||||||
|
binPath := filepath.Join(defaultInstallPath, defaultBinName)
|
||||||
|
os.Remove(binPath + "0")
|
||||||
|
os.Remove(binPath)
|
||||||
|
// os.RemoveAll(defaultInstallPath) // reserve config.json
|
||||||
|
}
|
||||||
139
log.go
@@ -8,17 +8,15 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LogLevel ...
|
|
||||||
type LogLevel int
|
type LogLevel int
|
||||||
|
|
||||||
var gLog *V8log
|
var gLog *logger
|
||||||
|
|
||||||
// LevelDEBUG ...
|
|
||||||
const (
|
const (
|
||||||
LevelDEBUG LogLevel = iota
|
LvDEBUG LogLevel = iota
|
||||||
LevelINFO
|
LvINFO
|
||||||
LevelWARN
|
LvWARN
|
||||||
LevelERROR
|
LvERROR
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -30,10 +28,10 @@ func init() {
|
|||||||
logFileNames = make(map[LogLevel]string)
|
logFileNames = make(map[LogLevel]string)
|
||||||
loglevel = make(map[LogLevel]string)
|
loglevel = make(map[LogLevel]string)
|
||||||
logFileNames[0] = ".log"
|
logFileNames[0] = ".log"
|
||||||
loglevel[LevelDEBUG] = "DEBUG"
|
loglevel[LvDEBUG] = "DEBUG"
|
||||||
loglevel[LevelINFO] = "INFO"
|
loglevel[LvINFO] = "INFO"
|
||||||
loglevel[LevelWARN] = "WARN"
|
loglevel[LvWARN] = "WARN"
|
||||||
loglevel[LevelERROR] = "ERROR"
|
loglevel[LvERROR] = "ERROR"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,25 +41,21 @@ const (
|
|||||||
LogFileAndConsole
|
LogFileAndConsole
|
||||||
)
|
)
|
||||||
|
|
||||||
// V8log ...
|
type logger struct {
|
||||||
type V8log struct {
|
|
||||||
loggers map[LogLevel]*log.Logger
|
loggers map[LogLevel]*log.Logger
|
||||||
files map[LogLevel]*os.File
|
files map[LogLevel]*os.File
|
||||||
llevel LogLevel
|
level LogLevel
|
||||||
stopSig chan bool
|
|
||||||
logDir string
|
logDir string
|
||||||
mtx *sync.Mutex
|
mtx *sync.Mutex
|
||||||
stoped bool
|
|
||||||
lineEnding string
|
lineEnding string
|
||||||
pid int
|
pid int
|
||||||
maxLogSize int64
|
maxLogSize int64
|
||||||
mode int
|
mode int
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitLogger ...
|
func NewLogger(path string, filePrefix string, level LogLevel, maxLogSize int64, mode int) *logger {
|
||||||
func InitLogger(path string, filePrefix string, level LogLevel, maxLogSize int64, mode int) *V8log {
|
loggers := make(map[LogLevel]*log.Logger)
|
||||||
logger := make(map[LogLevel]*log.Logger)
|
logfiles := make(map[LogLevel]*os.File)
|
||||||
openedfile := make(map[LogLevel]*os.File)
|
|
||||||
var (
|
var (
|
||||||
logdir string
|
logdir string
|
||||||
)
|
)
|
||||||
@@ -71,15 +65,15 @@ func InitLogger(path string, filePrefix string, level LogLevel, maxLogSize int64
|
|||||||
logdir = path + "/log/"
|
logdir = path + "/log/"
|
||||||
}
|
}
|
||||||
os.MkdirAll(logdir, 0777)
|
os.MkdirAll(logdir, 0777)
|
||||||
for l := range logFileNames {
|
for lv := range logFileNames {
|
||||||
logFilePath := logdir + filePrefix + logFileNames[l]
|
logFilePath := logdir + filePrefix + logFileNames[lv]
|
||||||
f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
os.Chmod(logFilePath, 0666)
|
os.Chmod(logFilePath, 0666)
|
||||||
openedfile[l] = f
|
logfiles[lv] = f
|
||||||
logger[l] = log.New(f, "", log.LstdFlags)
|
loggers[lv] = log.New(f, "", log.LstdFlags)
|
||||||
}
|
}
|
||||||
var le string
|
var le string
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
@@ -87,99 +81,84 @@ func InitLogger(path string, filePrefix string, level LogLevel, maxLogSize int64
|
|||||||
} else {
|
} else {
|
||||||
le = "\n"
|
le = "\n"
|
||||||
}
|
}
|
||||||
pLog := &V8log{logger, openedfile, level, make(chan bool, 10), logdir, &sync.Mutex{}, false, le, os.Getpid(), maxLogSize, mode}
|
pLog := &logger{loggers, logfiles, level, logdir, &sync.Mutex{}, le, os.Getpid(), maxLogSize, mode}
|
||||||
go pLog.checkFile()
|
go pLog.checkFile()
|
||||||
return pLog
|
return pLog
|
||||||
}
|
}
|
||||||
|
|
||||||
// UninitLogger ...
|
func (l *logger) setLevel(level LogLevel) {
|
||||||
func (vl *V8log) UninitLogger() {
|
l.mtx.Lock()
|
||||||
if !vl.stoped {
|
defer l.mtx.Unlock()
|
||||||
vl.stoped = true
|
l.level = level
|
||||||
close(vl.stopSig)
|
}
|
||||||
for l := range logFileNames {
|
func (l *logger) setMode(mode int) {
|
||||||
if l >= vl.llevel {
|
l.mtx.Lock()
|
||||||
vl.files[l].Close()
|
defer l.mtx.Unlock()
|
||||||
}
|
l.mode = mode
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vl *V8log) checkFile() {
|
func (l *logger) checkFile() {
|
||||||
if vl.maxLogSize <= 0 {
|
if l.maxLogSize <= 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ticker := time.NewTicker(time.Minute)
|
ticker := time.NewTicker(time.Minute)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
vl.mtx.Lock()
|
l.mtx.Lock()
|
||||||
for l, logFile := range vl.files {
|
for lv, logFile := range l.files {
|
||||||
f, e := logFile.Stat()
|
f, e := logFile.Stat()
|
||||||
if e != nil {
|
if e != nil {
|
||||||
break
|
continue
|
||||||
}
|
}
|
||||||
if f.Size() <= vl.maxLogSize {
|
if f.Size() <= l.maxLogSize {
|
||||||
break
|
continue
|
||||||
}
|
}
|
||||||
logFile.Close()
|
logFile.Close()
|
||||||
fname := f.Name()
|
fname := f.Name()
|
||||||
backupPath := vl.logDir + fname + ".0"
|
backupPath := l.logDir + fname + ".0"
|
||||||
os.Remove(backupPath)
|
os.Remove(backupPath)
|
||||||
os.Rename(vl.logDir+fname, backupPath)
|
os.Rename(l.logDir+fname, backupPath)
|
||||||
newFile, e := os.OpenFile(vl.logDir+fname, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
newFile, e := os.OpenFile(l.logDir+fname, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
if e == nil {
|
if e == nil {
|
||||||
vl.loggers[l].SetOutput(newFile)
|
l.loggers[lv].SetOutput(newFile)
|
||||||
vl.files[l] = newFile
|
l.files[lv] = newFile
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
vl.mtx.Unlock()
|
l.mtx.Unlock()
|
||||||
case <-vl.stopSig:
|
|
||||||
}
|
|
||||||
if vl.stoped {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Printf Warning: report error log depends on this Print format.
|
func (l *logger) Printf(level LogLevel, format string, params ...interface{}) {
|
||||||
func (vl *V8log) Printf(level LogLevel, format string, params ...interface{}) {
|
l.mtx.Lock()
|
||||||
vl.mtx.Lock()
|
defer l.mtx.Unlock()
|
||||||
defer vl.mtx.Unlock()
|
if level < l.level {
|
||||||
if vl.stoped {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if level < vl.llevel {
|
pidAndLevel := []interface{}{l.pid, loglevel[level]}
|
||||||
return
|
|
||||||
}
|
|
||||||
pidAndLevel := []interface{}{vl.pid, loglevel[level]}
|
|
||||||
params = append(pidAndLevel, params...)
|
params = append(pidAndLevel, params...)
|
||||||
if vl.mode == LogFile || vl.mode == LogFileAndConsole {
|
if l.mode == LogFile || l.mode == LogFileAndConsole {
|
||||||
vl.loggers[0].Printf("%d %s "+format+vl.lineEnding, params...)
|
l.loggers[0].Printf("%d %s "+format+l.lineEnding, params...)
|
||||||
}
|
}
|
||||||
if vl.mode == LogConsole || vl.mode == LogFileAndConsole {
|
if l.mode == LogConsole || l.mode == LogFileAndConsole {
|
||||||
log.Printf("%d %s "+format+vl.lineEnding, params...)
|
log.Printf("%d %s "+format+l.lineEnding, params...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Println ...
|
func (l *logger) Println(level LogLevel, params ...interface{}) {
|
||||||
func (vl *V8log) Println(level LogLevel, params ...interface{}) {
|
l.mtx.Lock()
|
||||||
vl.mtx.Lock()
|
defer l.mtx.Unlock()
|
||||||
defer vl.mtx.Unlock()
|
if level < l.level {
|
||||||
if vl.stoped {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if level < vl.llevel {
|
pidAndLevel := []interface{}{l.pid, " ", loglevel[level], " "}
|
||||||
return
|
|
||||||
}
|
|
||||||
pidAndLevel := []interface{}{vl.pid, " ", loglevel[level], " "}
|
|
||||||
params = append(pidAndLevel, params...)
|
params = append(pidAndLevel, params...)
|
||||||
params = append(params, vl.lineEnding)
|
params = append(params, l.lineEnding)
|
||||||
if vl.mode == LogFile || vl.mode == LogFileAndConsole {
|
if l.mode == LogFile || l.mode == LogFileAndConsole {
|
||||||
vl.loggers[0].Print(params...)
|
l.loggers[0].Print(params...)
|
||||||
}
|
}
|
||||||
if vl.mode == LogConsole || vl.mode == LogFileAndConsole {
|
if l.mode == LogConsole || l.mode == LogFileAndConsole {
|
||||||
log.Print(params...)
|
log.Print(params...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
87
nat.go
@@ -3,47 +3,69 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func natTest(serverHost string, serverPort int, localPort int, echoPort int) (publicIP string, isPublicIP int, publicPort int, err error) {
|
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))
|
conn, err := net.ListenPacket("udp", fmt.Sprintf(":%d", localPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, 0, err
|
return "", 0, 0, 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, 0, err
|
return "", 0, 0, 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, &NatDetectReq{SrcPort: localPort, EchoPort: echoPort})
|
msg, err := newMessage(MsgNATDetect, 0, &NatDetectReq{SrcPort: localPort, EchoPort: echoPort})
|
||||||
_, err = conn.WriteTo(msg, dst)
|
_, err = conn.WriteTo(msg, dst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, 0, err
|
return "", 0, 0, 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, 0, err
|
return "", 0, 0, 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(LevelERROR, "NAT detect error:", err)
|
gLog.Println(LvERROR, "NAT detect error:", err)
|
||||||
return "", 0, 0, err
|
return "", 0, 0, 0, err
|
||||||
}
|
}
|
||||||
natRsp := NatDetectRsp{}
|
natRsp := NatDetectRsp{}
|
||||||
err = json.Unmarshal(buffer[openP2PHeaderSize:nRead], &natRsp)
|
err = json.Unmarshal(buffer[openP2PHeaderSize:nRead], &natRsp)
|
||||||
|
hasPublicIP = 0
|
||||||
|
hasUPNPorNATPMP = 0
|
||||||
// testing for public ip
|
// testing for public ip
|
||||||
if echoPort != 0 {
|
if echoPort != 0 {
|
||||||
for {
|
for i := 0; i < 2; i++ {
|
||||||
gLog.Printf(LevelINFO, "public ip test start %s:%d", natRsp.IP, echoPort)
|
if i == 1 {
|
||||||
|
// test upnp or nat-pmp
|
||||||
|
nat, err := Discover()
|
||||||
|
if err != nil {
|
||||||
|
gLog.Println(LvDEBUG, "could not perform UPNP discover:", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ext, err := nat.GetExternalAddress()
|
||||||
|
if err != nil {
|
||||||
|
gLog.Println(LvDEBUG, "could not perform UPNP external address:", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
log.Println("PublicIP:", ext)
|
||||||
|
|
||||||
|
externalPort, err := nat.AddPortMapping("udp", echoPort, echoPort, "openp2p", 60)
|
||||||
|
if err != nil {
|
||||||
|
gLog.Println(LvDEBUG, "could not add udp UPNP port mapping", externalPort)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gLog.Printf(LvDEBUG, "public ip test start %s:%d", natRsp.IP, echoPort)
|
||||||
conn, err := net.ListenUDP("udp", nil)
|
conn, err := net.ListenUDP("udp", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
@@ -60,56 +82,55 @@ func natTest(serverHost string, serverPort int, localPort int, echoPort int) (pu
|
|||||||
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 {
|
||||||
gLog.Println(LevelINFO, "public ip:YES")
|
if i == 1 {
|
||||||
natRsp.IsPublicIP = 1
|
gLog.Println(LvDEBUG, "UPNP or NAT-PMP:YES")
|
||||||
} else {
|
hasUPNPorNATPMP = 1
|
||||||
gLog.Println(LevelINFO, "public ip:NO")
|
} else {
|
||||||
|
gLog.Println(LvDEBUG, "public ip:YES")
|
||||||
|
hasPublicIP = 1
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return natRsp.IP, natRsp.IsPublicIP, natRsp.Port, nil
|
return natRsp.IP, hasPublicIP, hasUPNPorNATPMP, 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, hasIPvr int, hasUPNPorNATPMP 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()%10000 + 50000)
|
localPort := int(rand.Uint32()%10000 + 50000)
|
||||||
echoPort := int(rand.Uint32()%10000 + 50000)
|
echoPort := int(rand.Uint32()%10000 + 50000)
|
||||||
go echo(echoPort)
|
go echo(echoPort)
|
||||||
ip1, isPublicIP, port1, err := natTest(host, udp1, localPort, echoPort)
|
ip1, hasIPv4, hasUPNPorNATPMP, port1, err := natTest(host, udp1, localPort, echoPort)
|
||||||
gLog.Printf(LevelDEBUG, "local port:%d nat port:%d", localPort, port1)
|
gLog.Printf(LvDEBUG, "local port:%d nat port:%d", localPort, port1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, err
|
return "", 0, hasIPv4, hasUPNPorNATPMP, err
|
||||||
}
|
}
|
||||||
if isPublicIP == 1 {
|
// if hasPublicIP == 1 || hasUPNPorNATPMP == 1 {
|
||||||
return ip1, NATNone, nil
|
// return ip1, NATNone, hasUPNPorNATPMP, nil
|
||||||
}
|
// }
|
||||||
ip2, _, port2, err := natTest(host, udp2, localPort, 0) // 2rd nat test not need testing publicip
|
_, _, _, port2, err := natTest(host, udp2, localPort, 0) // 2rd nat test not need testing publicip
|
||||||
gLog.Printf(LevelDEBUG, "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, hasIPv4, hasUPNPorNATPMP, err
|
||||||
}
|
|
||||||
if ip1 != ip2 {
|
|
||||||
return "", 0, fmt.Errorf("ip have changed, please retry again")
|
|
||||||
}
|
}
|
||||||
natType := NATSymmetric
|
natType := NATSymmetric
|
||||||
if port1 == port2 {
|
if port1 == port2 {
|
||||||
natType = NATCone
|
natType = NATCone
|
||||||
}
|
}
|
||||||
//TODO: NATNone
|
return ip1, natType, hasIPv4, hasUPNPorNATPMP, nil
|
||||||
return ip1, natType, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func echo(echoPort int) {
|
func echo(echoPort int) {
|
||||||
conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: echoPort})
|
conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: echoPort})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, "echo server listen error:", err)
|
gLog.Println(LvERROR, "echo server listen error:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
buf := make([]byte, 1600)
|
buf := make([]byte, 1600)
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
// wait 5s for echo testing
|
// wait 5s for echo testing
|
||||||
conn.SetReadDeadline(time.Now().Add(time.Second * 5))
|
conn.SetReadDeadline(time.Now().Add(time.Second * 30))
|
||||||
n, addr, err := conn.ReadFromUDP(buf)
|
n, addr, err := conn.ReadFromUDP(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|||||||
131
openp2p.go
@@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
@@ -11,24 +10,24 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
binDir := filepath.Dir(os.Args[0])
|
||||||
|
os.Chdir(binDir) // for system service
|
||||||
|
gLog = NewLogger(binDir, "openp2p", LvDEBUG, 1024*1024, LogFileAndConsole)
|
||||||
// TODO: install sub command, deamon process
|
// TODO: install sub command, deamon process
|
||||||
// groups := flag.String("groups", "", "you could join in several groups. like: GroupName1:Password1;GroupName2:Password2; group name 8-31 characters")
|
|
||||||
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 "update":
|
case "update":
|
||||||
gLog = InitLogger(filepath.Dir(os.Args[0]), "openp2p", LevelDEBUG, 1024*1024, LogFileAndConsole)
|
gLog = NewLogger(filepath.Dir(os.Args[0]), "openp2p", LvDEBUG, 1024*1024, LogFileAndConsole)
|
||||||
update()
|
|
||||||
targetPath := filepath.Join(defaultInstallPath, defaultBinName)
|
targetPath := filepath.Join(defaultInstallPath, defaultBinName)
|
||||||
d := daemon{}
|
d := daemon{}
|
||||||
err := d.Control("restart", targetPath, []string{"-d", "-f"})
|
err := d.Control("restart", targetPath, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, "restart service error:", err)
|
gLog.Println(LvERROR, "restart service error:", err)
|
||||||
} else {
|
} else {
|
||||||
gLog.Println(LevelINFO, "restart service ok.")
|
gLog.Println(LvINFO, "restart service ok.")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case "install":
|
case "install":
|
||||||
@@ -38,125 +37,27 @@ func main() {
|
|||||||
uninstall()
|
uninstall()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
installByFilename()
|
||||||
}
|
}
|
||||||
serverHost := flag.String("serverhost", "api.openp2p.cn", "server host ")
|
parseParams("")
|
||||||
// serverHost := flag.String("serverhost", "127.0.0.1", "server host ") // for debug
|
gLog.Println(LvINFO, "openp2p start. version: ", OpenP2PVersion)
|
||||||
user := flag.String("user", "", "user name. 8-31 characters")
|
gLog.Println(LvINFO, "Contact: QQ Group: 16947733, Email: openp2p.cn@gmail.com")
|
||||||
node := flag.String("node", "", "node name. 8-31 characters")
|
|
||||||
password := flag.String("password", "", "user password. 8-31 characters")
|
|
||||||
peerNode := flag.String("peernode", "", "peer node name that you want to connect")
|
|
||||||
peerUser := flag.String("peeruser", "", "peer node user (default peeruser=user)")
|
|
||||||
peerPassword := flag.String("peerpassword", "", "peer node password (default peerpassword=password)")
|
|
||||||
dstIP := flag.String("dstip", "127.0.0.1", "destination ip ")
|
|
||||||
dstPort := flag.Int("dstport", 0, "destination port ")
|
|
||||||
srcPort := flag.Int("srcport", 0, "source port ")
|
|
||||||
protocol := flag.String("protocol", "tcp", "tcp or udp")
|
|
||||||
noShare := flag.Bool("noshare", false, "disable using the huge numbers of shared nodes in OpenP2P network, your connectivity will be weak. also this node will not shared with others")
|
|
||||||
shareBandwidth := flag.Int("sharebandwidth", 10, "N mbps share bandwidth limit, private node no limit")
|
|
||||||
configFile := flag.Bool("f", false, "config file")
|
|
||||||
daemonMode := flag.Bool("d", false, "daemonMode")
|
|
||||||
byDaemon := flag.Bool("bydaemon", false, "start by daemon")
|
|
||||||
logLevel := flag.Int("loglevel", 1, "0:debug 1:info 2:warn 3:error")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
gLog = InitLogger(filepath.Dir(os.Args[0]), "openp2p", LogLevel(*logLevel), 1024*1024, LogFileAndConsole)
|
if gConf.daemonMode {
|
||||||
gLog.Println(LevelINFO, "openp2p start. version: ", OpenP2PVersion)
|
|
||||||
if *daemonMode {
|
|
||||||
d := daemon{}
|
d := daemon{}
|
||||||
d.run()
|
d.run()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !*configFile {
|
|
||||||
// validate cmd params
|
|
||||||
checkParams(*node, *user, *password)
|
|
||||||
if *peerNode != "" {
|
|
||||||
if *dstPort == 0 {
|
|
||||||
gLog.Println(LevelERROR, "dstPort not set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if *srcPort == 0 {
|
|
||||||
gLog.Println(LevelERROR, "srcPort not set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config := AppConfig{}
|
gLog.Println(LvINFO, &gConf)
|
||||||
config.PeerNode = *peerNode
|
|
||||||
config.PeerUser = *peerUser
|
|
||||||
config.PeerPassword = *peerPassword
|
|
||||||
config.DstHost = *dstIP
|
|
||||||
config.DstPort = *dstPort
|
|
||||||
config.SrcPort = *srcPort
|
|
||||||
config.Protocol = *protocol
|
|
||||||
gLog.Println(LevelINFO, config)
|
|
||||||
if *configFile {
|
|
||||||
if err := gConf.load(); err != nil {
|
|
||||||
gLog.Println(LevelERROR, "load config error. exit.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
gConf.add(config)
|
|
||||||
gConf.Network = NetworkConfig{
|
|
||||||
Node: *node,
|
|
||||||
User: *user,
|
|
||||||
Password: *password,
|
|
||||||
NoShare: *noShare,
|
|
||||||
ServerHost: *serverHost,
|
|
||||||
ServerPort: 27182,
|
|
||||||
UDPPort1: 27182,
|
|
||||||
UDPPort2: 27183,
|
|
||||||
ipv6: "240e:3b7:621:def0:fda4:dd7f:36a1:2803", // TODO: detect real ipv6
|
|
||||||
ShareBandwidth: *shareBandwidth,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// gConf.save() // not change config file
|
|
||||||
gConf.daemonMode = *byDaemon
|
|
||||||
|
|
||||||
gLog.Println(LevelINFO, gConf)
|
|
||||||
setFirewall()
|
setFirewall()
|
||||||
network := P2PNetworkInstance(&gConf.Network)
|
network := P2PNetworkInstance(&gConf.Network)
|
||||||
if ok := network.Connect(30000); !ok {
|
if ok := network.Connect(30000); !ok {
|
||||||
gLog.Println(LevelERROR, "P2PNetwork login error")
|
gLog.Println(LvERROR, "P2PNetwork login error")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, app := range gConf.Apps {
|
gLog.Println(LvINFO, "waiting for connection...")
|
||||||
// set default peer user password
|
|
||||||
if app.PeerPassword == "" {
|
|
||||||
app.PeerPassword = gConf.Network.Password
|
|
||||||
}
|
|
||||||
if app.PeerUser == "" {
|
|
||||||
app.PeerUser = gConf.Network.User
|
|
||||||
}
|
|
||||||
err := network.AddApp(app)
|
|
||||||
if err != nil {
|
|
||||||
gLog.Println(LevelERROR, "addTunnel error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// test
|
|
||||||
// go func() {
|
|
||||||
|
|
||||||
// time.Sleep(time.Second * 30)
|
|
||||||
// config := AppConfig{}
|
|
||||||
// config.PeerNode = *peerNode
|
|
||||||
// config.PeerUser = *peerUser
|
|
||||||
// config.PeerPassword = *peerPassword
|
|
||||||
// config.DstHost = *dstIP
|
|
||||||
// config.DstPort = *dstPort
|
|
||||||
// config.SrcPort = 32
|
|
||||||
// config.Protocol = *protocol
|
|
||||||
// network.AddApp(config)
|
|
||||||
// // time.Sleep(time.Second * 30)
|
|
||||||
// // network.DeleteTunnel(config)
|
|
||||||
// // time.Sleep(time.Second * 30)
|
|
||||||
// // network.DeleteTunnel(config)
|
|
||||||
// }()
|
|
||||||
|
|
||||||
// // TODO: http api
|
|
||||||
// api := ClientAPI{}
|
|
||||||
// go api.run()
|
|
||||||
gLog.Println(LevelINFO, "waiting for connection...")
|
|
||||||
forever := make(chan bool)
|
forever := make(chan bool)
|
||||||
<-forever
|
<-forever
|
||||||
}
|
}
|
||||||
|
|||||||
150
overlay.go
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrDeadlineExceeded error = &DeadlineExceededError{}
|
||||||
|
|
||||||
|
// DeadlineExceededError is returned for an expired deadline.
|
||||||
|
type DeadlineExceededError struct{}
|
||||||
|
|
||||||
|
// Implement the net.Error interface.
|
||||||
|
// The string is "i/o timeout" because that is what was returned
|
||||||
|
// by earlier Go versions. Changing it may break programs that
|
||||||
|
// match on error strings.
|
||||||
|
func (e *DeadlineExceededError) Error() string { return "i/o timeout" }
|
||||||
|
func (e *DeadlineExceededError) Timeout() bool { return true }
|
||||||
|
func (e *DeadlineExceededError) Temporary() bool { return true }
|
||||||
|
|
||||||
|
// implement io.Writer
|
||||||
|
type overlayConn struct {
|
||||||
|
tunnel *P2PTunnel
|
||||||
|
connTCP net.Conn
|
||||||
|
id uint64
|
||||||
|
rtid uint64
|
||||||
|
running bool
|
||||||
|
isClient bool
|
||||||
|
appID uint64
|
||||||
|
appKey uint64
|
||||||
|
appKeyBytes []byte
|
||||||
|
// for udp
|
||||||
|
connUDP *net.UDPConn
|
||||||
|
remoteAddr net.Addr
|
||||||
|
udpRelayData chan []byte
|
||||||
|
lastReadUDPTs time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (oConn *overlayConn) run() {
|
||||||
|
gLog.Printf(LvDEBUG, "%d overlayConn run start", oConn.id)
|
||||||
|
defer gLog.Printf(LvDEBUG, "%d overlayConn run end", oConn.id)
|
||||||
|
oConn.running = true
|
||||||
|
oConn.lastReadUDPTs = time.Now()
|
||||||
|
buffer := make([]byte, ReadBuffLen+PaddingSize)
|
||||||
|
readBuf := buffer[:ReadBuffLen]
|
||||||
|
encryptData := make([]byte, ReadBuffLen+PaddingSize) // 16 bytes for padding
|
||||||
|
tunnelHead := new(bytes.Buffer)
|
||||||
|
relayHead := new(bytes.Buffer)
|
||||||
|
binary.Write(relayHead, binary.LittleEndian, oConn.rtid)
|
||||||
|
binary.Write(tunnelHead, binary.LittleEndian, oConn.id)
|
||||||
|
for oConn.running && oConn.tunnel.isRuning() {
|
||||||
|
buff, dataLen, err := oConn.Read(readBuf)
|
||||||
|
if err != nil {
|
||||||
|
if ne, ok := err.(net.Error); ok && ne.Timeout() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// overlay tcp connection normal close, debug log
|
||||||
|
gLog.Printf(LvDEBUG, "overlayConn %d read error:%s,close it", oConn.id, err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
payload := buff[:dataLen]
|
||||||
|
if oConn.appKey != 0 {
|
||||||
|
payload, _ = encryptBytes(oConn.appKeyBytes, encryptData, buffer[:dataLen], dataLen)
|
||||||
|
}
|
||||||
|
writeBytes := append(tunnelHead.Bytes(), payload...)
|
||||||
|
if oConn.rtid == 0 {
|
||||||
|
oConn.tunnel.conn.WriteBytes(MsgP2P, MsgOverlayData, writeBytes)
|
||||||
|
gLog.Printf(LvDEBUG, "write overlay data to %d:%d bodylen=%d", oConn.rtid, oConn.id, len(writeBytes))
|
||||||
|
} else {
|
||||||
|
// write raley data
|
||||||
|
all := append(relayHead.Bytes(), encodeHeader(MsgP2P, MsgOverlayData, uint32(len(writeBytes)))...)
|
||||||
|
all = append(all, writeBytes...)
|
||||||
|
oConn.tunnel.conn.WriteBytes(MsgP2P, MsgRelayData, all)
|
||||||
|
gLog.Printf(LvDEBUG, "write relay data to %d:%d bodylen=%d", oConn.rtid, oConn.id, len(writeBytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if oConn.connTCP != nil {
|
||||||
|
oConn.connTCP.Close()
|
||||||
|
}
|
||||||
|
if oConn.connUDP != nil {
|
||||||
|
oConn.connUDP.Close()
|
||||||
|
}
|
||||||
|
oConn.tunnel.overlayConns.Delete(oConn.id)
|
||||||
|
// notify peer disconnect
|
||||||
|
if oConn.isClient {
|
||||||
|
req := OverlayDisconnectReq{ID: oConn.id}
|
||||||
|
if oConn.rtid == 0 {
|
||||||
|
oConn.tunnel.conn.WriteMessage(MsgP2P, MsgOverlayDisconnectReq, &req)
|
||||||
|
} else {
|
||||||
|
// write relay data
|
||||||
|
msg, _ := newMessage(MsgP2P, MsgOverlayDisconnectReq, &req)
|
||||||
|
msgWithHead := append(relayHead.Bytes(), msg...)
|
||||||
|
oConn.tunnel.conn.WriteBytes(MsgP2P, MsgRelayData, msgWithHead)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (oConn *overlayConn) Read(reuseBuff []byte) (buff []byte, n int, err error) {
|
||||||
|
if oConn.connUDP != nil {
|
||||||
|
if time.Now().After(oConn.lastReadUDPTs.Add(time.Minute * 5)) {
|
||||||
|
err = errors.New("udp close")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if oConn.remoteAddr != nil { // as server
|
||||||
|
select {
|
||||||
|
case buff = <-oConn.udpRelayData:
|
||||||
|
n = len(buff)
|
||||||
|
oConn.lastReadUDPTs = time.Now()
|
||||||
|
case <-time.After(time.Second * 10):
|
||||||
|
err = ErrDeadlineExceeded
|
||||||
|
}
|
||||||
|
} else { // as client
|
||||||
|
oConn.connUDP.SetReadDeadline(time.Now().Add(5 * time.Second))
|
||||||
|
n, _, err = oConn.connUDP.ReadFrom(reuseBuff)
|
||||||
|
if err == nil {
|
||||||
|
oConn.lastReadUDPTs = time.Now()
|
||||||
|
}
|
||||||
|
buff = reuseBuff
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
oConn.connTCP.SetReadDeadline(time.Now().Add(time.Second * 5))
|
||||||
|
n, err = oConn.connTCP.Read(reuseBuff)
|
||||||
|
buff = reuseBuff
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// calling by p2pTunnel
|
||||||
|
func (oConn *overlayConn) Write(buff []byte) (n int, err error) {
|
||||||
|
// add mutex when multi-thread calling
|
||||||
|
if oConn.connUDP != nil {
|
||||||
|
if oConn.remoteAddr == nil {
|
||||||
|
n, err = oConn.connUDP.Write(buff)
|
||||||
|
} else {
|
||||||
|
n, err = oConn.connUDP.WriteTo(buff, oConn.remoteAddr)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
oConn.running = false
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n, err = oConn.connTCP.Write(buff)
|
||||||
|
if err != nil {
|
||||||
|
oConn.running = false
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// implement io.Writer
|
|
||||||
type overlayTCP struct {
|
|
||||||
tunnel *P2PTunnel
|
|
||||||
conn net.Conn
|
|
||||||
id uint64
|
|
||||||
rtid uint64
|
|
||||||
running bool
|
|
||||||
isClient bool
|
|
||||||
appID uint64
|
|
||||||
appKey uint64
|
|
||||||
appKeyBytes []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (otcp *overlayTCP) run() {
|
|
||||||
gLog.Printf(LevelINFO, "%d overlayTCP run start", otcp.id)
|
|
||||||
defer gLog.Printf(LevelINFO, "%d overlayTCP run end", otcp.id)
|
|
||||||
otcp.running = true
|
|
||||||
buffer := make([]byte, ReadBuffLen+PaddingSize)
|
|
||||||
readBuf := buffer[:ReadBuffLen]
|
|
||||||
encryptData := make([]byte, ReadBuffLen+PaddingSize) // 16 bytes for padding
|
|
||||||
tunnelHead := new(bytes.Buffer)
|
|
||||||
relayHead := new(bytes.Buffer)
|
|
||||||
binary.Write(relayHead, binary.LittleEndian, otcp.rtid)
|
|
||||||
binary.Write(tunnelHead, binary.LittleEndian, otcp.id)
|
|
||||||
for otcp.running && otcp.tunnel.isRuning() {
|
|
||||||
otcp.conn.SetReadDeadline(time.Now().Add(time.Second * 5))
|
|
||||||
dataLen, err := otcp.conn.Read(readBuf)
|
|
||||||
if err != nil {
|
|
||||||
if ne, ok := err.(net.Error); ok && ne.Timeout() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// overlay tcp connection normal close, debug log
|
|
||||||
gLog.Printf(LevelDEBUG, "overlayTCP %d read error:%s,close it", otcp.id, err)
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
payload := readBuf[:dataLen]
|
|
||||||
if otcp.appKey != 0 {
|
|
||||||
payload, _ = encryptBytes(otcp.appKeyBytes, encryptData, buffer[:dataLen], dataLen)
|
|
||||||
}
|
|
||||||
writeBytes := append(tunnelHead.Bytes(), payload...)
|
|
||||||
if otcp.rtid == 0 {
|
|
||||||
otcp.tunnel.conn.WriteBytes(MsgP2P, MsgOverlayData, writeBytes)
|
|
||||||
} else {
|
|
||||||
// write raley data
|
|
||||||
all := append(relayHead.Bytes(), encodeHeader(MsgP2P, MsgOverlayData, uint32(len(writeBytes)))...)
|
|
||||||
all = append(all, writeBytes...)
|
|
||||||
otcp.tunnel.conn.WriteBytes(MsgP2P, MsgRelayData, all)
|
|
||||||
gLog.Printf(LevelDEBUG, "write relay data to %d:%d bodylen=%d", otcp.rtid, otcp.id, len(writeBytes))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
otcp.conn.Close()
|
|
||||||
otcp.tunnel.overlayConns.Delete(otcp.id)
|
|
||||||
// notify peer disconnect
|
|
||||||
if otcp.isClient {
|
|
||||||
req := OverlayDisconnectReq{ID: otcp.id}
|
|
||||||
if otcp.rtid == 0 {
|
|
||||||
otcp.tunnel.conn.WriteMessage(MsgP2P, MsgOverlayDisconnectReq, &req)
|
|
||||||
} else {
|
|
||||||
// write relay data
|
|
||||||
msg, _ := newMessage(MsgP2P, MsgOverlayDisconnectReq, &req)
|
|
||||||
msgWithHead := append(relayHead.Bytes(), msg...)
|
|
||||||
otcp.tunnel.conn.WriteBytes(MsgP2P, MsgRelayData, msgWithHead)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// calling by p2pTunnel
|
|
||||||
func (otcp *overlayTCP) Write(buff []byte) (n int, err error) {
|
|
||||||
// add mutex when multi-thread calling
|
|
||||||
n, err = otcp.conn.Write(buff)
|
|
||||||
if err != nil {
|
|
||||||
otcp.tunnel.overlayConns.Delete(otcp.id)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
169
p2papp.go
@@ -6,21 +6,26 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type p2pApp struct {
|
type p2pApp struct {
|
||||||
config AppConfig
|
config AppConfig
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
tunnel *P2PTunnel
|
listenerUDP *net.UDPConn
|
||||||
rtid uint64
|
tunnel *P2PTunnel
|
||||||
hbTime time.Time
|
rtid uint64 // relay tunnelID
|
||||||
hbMtx sync.Mutex
|
relayNode string
|
||||||
running bool
|
relayMode string
|
||||||
id uint64
|
hbTime time.Time
|
||||||
key uint64
|
hbMtx sync.Mutex
|
||||||
wg sync.WaitGroup
|
running bool
|
||||||
|
id uint64
|
||||||
|
key uint64
|
||||||
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *p2pApp) isActive() bool {
|
func (app *p2pApp) isActive() bool {
|
||||||
@@ -43,40 +48,43 @@ func (app *p2pApp) updateHeartbeat() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (app *p2pApp) listenTCP() error {
|
func (app *p2pApp) listenTCP() error {
|
||||||
|
gLog.Printf(LvDEBUG, "tcp accept on port %d start", app.config.SrcPort)
|
||||||
|
defer gLog.Printf(LvDEBUG, "tcp accept on port %d end", app.config.SrcPort)
|
||||||
var err error
|
var err error
|
||||||
app.listener, err = net.Listen("tcp4", fmt.Sprintf("0.0.0.0:%d", app.config.SrcPort))
|
app.listener, err = net.Listen("tcp4", fmt.Sprintf("0.0.0.0:%d", app.config.SrcPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Printf(LevelERROR, "listen error:%s", err)
|
gLog.Printf(LvERROR, "listen error:%s", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for {
|
for app.running {
|
||||||
conn, err := app.listener.Accept()
|
conn, err := app.listener.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Printf(LevelERROR, "%d accept error:%s", app.tunnel.id, err)
|
if app.running {
|
||||||
|
gLog.Printf(LvERROR, "%d accept error:%s", app.id, err)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
otcp := overlayTCP{
|
oConn := overlayConn{
|
||||||
tunnel: app.tunnel,
|
tunnel: app.tunnel,
|
||||||
conn: conn,
|
connTCP: conn,
|
||||||
id: rand.Uint64(),
|
id: rand.Uint64(),
|
||||||
isClient: true,
|
isClient: true,
|
||||||
rtid: app.rtid,
|
rtid: app.rtid,
|
||||||
appID: app.id,
|
appID: app.id,
|
||||||
appKey: app.key,
|
appKey: app.key,
|
||||||
}
|
}
|
||||||
// calc key bytes for encrypt
|
// pre-calc key bytes for encrypt
|
||||||
if otcp.appKey != 0 {
|
if oConn.appKey != 0 {
|
||||||
encryptKey := make([]byte, AESKeySize)
|
encryptKey := make([]byte, AESKeySize)
|
||||||
binary.LittleEndian.PutUint64(encryptKey, otcp.appKey)
|
binary.LittleEndian.PutUint64(encryptKey, oConn.appKey)
|
||||||
binary.LittleEndian.PutUint64(encryptKey[8:], otcp.appKey)
|
binary.LittleEndian.PutUint64(encryptKey[8:], oConn.appKey)
|
||||||
otcp.appKeyBytes = encryptKey
|
oConn.appKeyBytes = encryptKey
|
||||||
}
|
}
|
||||||
app.tunnel.overlayConns.Store(otcp.id, &otcp)
|
app.tunnel.overlayConns.Store(oConn.id, &oConn)
|
||||||
gLog.Printf(LevelINFO, "Accept overlayID:%d", otcp.id)
|
gLog.Printf(LvDEBUG, "Accept TCP overlayID:%d", oConn.id)
|
||||||
// tell peer connect
|
// tell peer connect
|
||||||
req := OverlayConnectReq{ID: otcp.id,
|
req := OverlayConnectReq{ID: oConn.id,
|
||||||
User: app.config.PeerUser,
|
Token: app.tunnel.pn.config.Token,
|
||||||
Password: app.config.PeerPassword,
|
|
||||||
DstIP: app.config.DstHost,
|
DstIP: app.config.DstHost,
|
||||||
DstPort: app.config.DstPort,
|
DstPort: app.config.DstPort,
|
||||||
Protocol: app.config.Protocol,
|
Protocol: app.config.Protocol,
|
||||||
@@ -92,27 +100,117 @@ func (app *p2pApp) listenTCP() error {
|
|||||||
msgWithHead := append(relayHead.Bytes(), msg...)
|
msgWithHead := append(relayHead.Bytes(), msg...)
|
||||||
app.tunnel.conn.WriteBytes(MsgP2P, MsgRelayData, msgWithHead)
|
app.tunnel.conn.WriteBytes(MsgP2P, MsgRelayData, msgWithHead)
|
||||||
}
|
}
|
||||||
|
go oConn.run()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
go otcp.run()
|
func (app *p2pApp) listenUDP() error {
|
||||||
|
gLog.Printf(LvDEBUG, "udp accept on port %d start", app.config.SrcPort)
|
||||||
|
defer gLog.Printf(LvDEBUG, "udp accept on port %d end", app.config.SrcPort)
|
||||||
|
var err error
|
||||||
|
app.listenerUDP, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: app.config.SrcPort})
|
||||||
|
if err != nil {
|
||||||
|
gLog.Printf(LvERROR, "listen error:%s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buffer := make([]byte, 64*1024)
|
||||||
|
udpID := make([]byte, 8)
|
||||||
|
for {
|
||||||
|
app.listenerUDP.SetReadDeadline(time.Now().Add(time.Second * 10))
|
||||||
|
len, remoteAddr, err := app.listenerUDP.ReadFrom(buffer)
|
||||||
|
if err != nil {
|
||||||
|
if ne, ok := err.(net.Error); ok && ne.Timeout() {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
gLog.Printf(LvERROR, "udp read failed:%s", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
b := bytes.Buffer{}
|
||||||
|
b.Write(buffer[:len])
|
||||||
|
// load from app.tunnel.overlayConns by remoteAddr error, new udp connection
|
||||||
|
remoteIP := strings.Split(remoteAddr.String(), ":")[0]
|
||||||
|
port, _ := strconv.Atoi(strings.Split(remoteAddr.String(), ":")[1])
|
||||||
|
a := net.ParseIP(remoteIP)
|
||||||
|
udpID[0] = a[0]
|
||||||
|
udpID[1] = a[1]
|
||||||
|
udpID[2] = a[2]
|
||||||
|
udpID[3] = a[3]
|
||||||
|
udpID[4] = byte(port)
|
||||||
|
udpID[5] = byte(port >> 8)
|
||||||
|
id := binary.LittleEndian.Uint64(udpID)
|
||||||
|
s, ok := app.tunnel.overlayConns.Load(id)
|
||||||
|
if !ok {
|
||||||
|
oConn := overlayConn{
|
||||||
|
tunnel: app.tunnel,
|
||||||
|
connUDP: app.listenerUDP,
|
||||||
|
remoteAddr: remoteAddr,
|
||||||
|
udpRelayData: make(chan []byte, 1000),
|
||||||
|
id: id,
|
||||||
|
isClient: true,
|
||||||
|
rtid: app.rtid,
|
||||||
|
appID: app.id,
|
||||||
|
appKey: app.key,
|
||||||
|
}
|
||||||
|
// calc key bytes for encrypt
|
||||||
|
if oConn.appKey != 0 {
|
||||||
|
encryptKey := make([]byte, AESKeySize)
|
||||||
|
binary.LittleEndian.PutUint64(encryptKey, oConn.appKey)
|
||||||
|
binary.LittleEndian.PutUint64(encryptKey[8:], oConn.appKey)
|
||||||
|
oConn.appKeyBytes = encryptKey
|
||||||
|
}
|
||||||
|
app.tunnel.overlayConns.Store(oConn.id, &oConn)
|
||||||
|
gLog.Printf(LvDEBUG, "Accept UDP overlayID:%d", oConn.id)
|
||||||
|
// tell peer connect
|
||||||
|
req := OverlayConnectReq{ID: oConn.id,
|
||||||
|
Token: app.tunnel.pn.config.Token,
|
||||||
|
DstIP: app.config.DstHost,
|
||||||
|
DstPort: app.config.DstPort,
|
||||||
|
Protocol: app.config.Protocol,
|
||||||
|
AppID: app.id,
|
||||||
|
}
|
||||||
|
if app.rtid == 0 {
|
||||||
|
app.tunnel.conn.WriteMessage(MsgP2P, MsgOverlayConnectReq, &req)
|
||||||
|
} else {
|
||||||
|
req.RelayTunnelID = app.tunnel.id
|
||||||
|
relayHead := new(bytes.Buffer)
|
||||||
|
binary.Write(relayHead, binary.LittleEndian, app.rtid)
|
||||||
|
msg, _ := newMessage(MsgP2P, MsgOverlayConnectReq, &req)
|
||||||
|
msgWithHead := append(relayHead.Bytes(), msg...)
|
||||||
|
app.tunnel.conn.WriteBytes(MsgP2P, MsgRelayData, msgWithHead)
|
||||||
|
}
|
||||||
|
go oConn.run()
|
||||||
|
oConn.udpRelayData <- b.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// load from app.tunnel.overlayConns by remoteAddr ok, write relay data
|
||||||
|
overlayConn, ok := s.(*overlayConn)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
overlayConn.udpRelayData <- b.Bytes()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *p2pApp) listen() error {
|
func (app *p2pApp) listen() error {
|
||||||
gLog.Printf(LevelINFO, "LISTEN ON PORT %d START", app.config.SrcPort)
|
gLog.Printf(LvINFO, "LISTEN ON PORT %s:%d START", app.config.Protocol, app.config.SrcPort)
|
||||||
defer gLog.Printf(LevelINFO, "LISTEN ON PORT %d START", app.config.SrcPort)
|
defer gLog.Printf(LvINFO, "LISTEN ON PORT %s:%d END", app.config.Protocol, app.config.SrcPort)
|
||||||
app.wg.Add(1)
|
app.wg.Add(1)
|
||||||
defer app.wg.Done()
|
defer app.wg.Done()
|
||||||
app.running = true
|
app.running = true
|
||||||
if app.rtid != 0 {
|
if app.rtid != 0 {
|
||||||
go app.relayHeartbeatLoop()
|
go app.relayHeartbeatLoop()
|
||||||
}
|
}
|
||||||
for app.running {
|
for app.tunnel.isRuning() && app.running {
|
||||||
if app.config.Protocol == "tcp" {
|
if app.config.Protocol == "udp" {
|
||||||
|
app.listenUDP()
|
||||||
|
} else {
|
||||||
app.listenTCP()
|
app.listenTCP()
|
||||||
}
|
}
|
||||||
time.Sleep(time.Second * 5)
|
time.Sleep(time.Second * 10)
|
||||||
// TODO: listen UDP
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -122,6 +220,9 @@ func (app *p2pApp) close() {
|
|||||||
if app.listener != nil {
|
if app.listener != nil {
|
||||||
app.listener.Close()
|
app.listener.Close()
|
||||||
}
|
}
|
||||||
|
if app.listenerUDP != nil {
|
||||||
|
app.listenerUDP.Close()
|
||||||
|
}
|
||||||
if app.tunnel != nil {
|
if app.tunnel != nil {
|
||||||
app.tunnel.closeOverlayConns(app.id)
|
app.tunnel.closeOverlayConns(app.id)
|
||||||
}
|
}
|
||||||
@@ -132,8 +233,8 @@ func (app *p2pApp) close() {
|
|||||||
func (app *p2pApp) relayHeartbeatLoop() {
|
func (app *p2pApp) relayHeartbeatLoop() {
|
||||||
app.wg.Add(1)
|
app.wg.Add(1)
|
||||||
defer app.wg.Done()
|
defer app.wg.Done()
|
||||||
gLog.Printf(LevelDEBUG, "relayHeartbeat to %d start", app.rtid)
|
gLog.Printf(LvDEBUG, "relayHeartbeat to %d start", app.rtid)
|
||||||
defer gLog.Printf(LevelDEBUG, "relayHeartbeat to %d end", app.rtid)
|
defer gLog.Printf(LvDEBUG, "relayHeartbeat to %d end", app.rtid)
|
||||||
relayHead := new(bytes.Buffer)
|
relayHead := new(bytes.Buffer)
|
||||||
binary.Write(relayHead, binary.LittleEndian, app.rtid)
|
binary.Write(relayHead, binary.LittleEndian, app.rtid)
|
||||||
req := RelayHeartbeat{RelayTunnelID: app.tunnel.id,
|
req := RelayHeartbeat{RelayTunnelID: app.tunnel.id,
|
||||||
|
|||||||
477
p2pnetwork.go
@@ -7,10 +7,10 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -37,7 +37,7 @@ type P2PNetwork struct {
|
|||||||
msgMapMtx sync.Mutex
|
msgMapMtx sync.Mutex
|
||||||
config NetworkConfig
|
config NetworkConfig
|
||||||
allTunnels sync.Map
|
allTunnels sync.Map
|
||||||
apps sync.Map
|
apps sync.Map //key: protocol+srcport; value: p2pApp
|
||||||
limiter *BandwidthLimiter
|
limiter *BandwidthLimiter
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ func P2PNetworkInstance(config *NetworkConfig) *P2PNetwork {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pn *P2PNetwork) run() {
|
func (pn *P2PNetwork) run() {
|
||||||
go pn.autoReconnectApp()
|
go pn.autorunApp()
|
||||||
heartbeatTimer := time.NewTicker(NetworkHeartbeatTime)
|
heartbeatTimer := time.NewTicker(NetworkHeartbeatTime)
|
||||||
for pn.running {
|
for pn.running {
|
||||||
select {
|
select {
|
||||||
@@ -76,7 +76,7 @@ func (pn *P2PNetwork) run() {
|
|||||||
time.Sleep(NetworkHeartbeatTime)
|
time.Sleep(NetworkHeartbeatTime)
|
||||||
err := pn.init()
|
err := pn.init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, "P2PNetwork init error:", err)
|
gLog.Println(LvERROR, "P2PNetwork init error:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,149 +93,166 @@ func (pn *P2PNetwork) Connect(timeout int) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pn *P2PNetwork) autoReconnectApp() {
|
func (pn *P2PNetwork) runAll() {
|
||||||
gLog.Println(LevelINFO, "autoReconnectApp start")
|
gConf.mtx.Lock() // lock for copy gConf.Apps and the modification of config(it's pointer)
|
||||||
retryApps := make([]AppConfig, 0)
|
defer gConf.mtx.Unlock()
|
||||||
|
allApps := gConf.Apps // read a copy, other thread will modify the gConf.Apps
|
||||||
|
|
||||||
|
for _, config := range allApps {
|
||||||
|
if config.nextRetryTime.After(time.Now()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if config.Enabled == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if config.AppName == "" {
|
||||||
|
config.AppName = fmt.Sprintf("%s%d", config.Protocol, config.SrcPort)
|
||||||
|
}
|
||||||
|
appExist := false
|
||||||
|
var appID uint64
|
||||||
|
i, ok := pn.apps.Load(fmt.Sprintf("%s%d", config.Protocol, config.SrcPort))
|
||||||
|
if ok {
|
||||||
|
app := i.(*p2pApp)
|
||||||
|
appExist = true
|
||||||
|
appID = app.id
|
||||||
|
if app.isActive() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if appExist {
|
||||||
|
pn.DeleteApp(*config)
|
||||||
|
}
|
||||||
|
if config.retryNum > 0 {
|
||||||
|
gLog.Printf(LvINFO, "detect app %s(%d) disconnect, reconnecting the %d times...", config.AppName, appID, config.retryNum)
|
||||||
|
if time.Now().Add(-time.Minute * 15).After(config.retryTime) { // normal lasts 15min
|
||||||
|
config.retryNum = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.retryNum++
|
||||||
|
config.retryTime = time.Now()
|
||||||
|
increase := math.Pow(1.3, float64(config.retryNum))
|
||||||
|
if increase > 900 {
|
||||||
|
increase = 900
|
||||||
|
}
|
||||||
|
config.nextRetryTime = time.Now().Add(time.Second * time.Duration(increase)) // exponential increase retry time. 1.3^x
|
||||||
|
config.connectTime = time.Now()
|
||||||
|
gConf.mtx.Unlock() // AddApp will take a period of time
|
||||||
|
err := pn.AddApp(*config)
|
||||||
|
gConf.mtx.Lock()
|
||||||
|
if err != nil {
|
||||||
|
config.errMsg = err.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (pn *P2PNetwork) autorunApp() {
|
||||||
|
gLog.Println(LvINFO, "autorunApp start")
|
||||||
for pn.running {
|
for pn.running {
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
if !pn.online {
|
if !pn.online {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(retryApps) > 0 {
|
pn.runAll()
|
||||||
gLog.Printf(LevelINFO, "retryApps len=%d", len(retryApps))
|
time.Sleep(time.Second * 10)
|
||||||
thisRound := make([]AppConfig, 0)
|
|
||||||
for i := 0; i < len(retryApps); i++ {
|
|
||||||
// reset retryNum when running 15min continuously
|
|
||||||
if retryApps[i].retryTime.Add(time.Minute * 15).Before(time.Now()) {
|
|
||||||
retryApps[i].retryNum = 0
|
|
||||||
}
|
|
||||||
retryApps[i].retryNum++
|
|
||||||
retryApps[i].retryTime = time.Now()
|
|
||||||
if retryApps[i].retryNum > MaxRetry {
|
|
||||||
gLog.Printf(LevelERROR, "app %s%d retry more than %d times, exit.", retryApps[i].Protocol, retryApps[i].SrcPort, MaxRetry)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
pn.DeleteApp(retryApps[i])
|
|
||||||
if err := pn.AddApp(retryApps[i]); err != nil {
|
|
||||||
gLog.Printf(LevelERROR, "AddApp %s%d error:%s", retryApps[i].Protocol, retryApps[i].SrcPort, err)
|
|
||||||
thisRound = append(thisRound, retryApps[i])
|
|
||||||
time.Sleep(RetryInterval)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
retryApps = thisRound
|
|
||||||
}
|
|
||||||
pn.apps.Range(func(_, i interface{}) bool {
|
|
||||||
app := i.(*p2pApp)
|
|
||||||
if app.isActive() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
gLog.Printf(LevelINFO, "detect app %s%d disconnect,last hb %s reconnecting...", app.config.Protocol, app.config.SrcPort, app.hbTime)
|
|
||||||
config := app.config
|
|
||||||
// clear peerinfo
|
|
||||||
config.peerConeNatPort = 0
|
|
||||||
config.peerIP = ""
|
|
||||||
config.peerNatType = 0
|
|
||||||
config.peerToken = 0
|
|
||||||
pn.DeleteApp(config)
|
|
||||||
retryApps = append(retryApps, config)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
gLog.Println(LevelINFO, "autoReconnectApp end")
|
gLog.Println(LvINFO, "autorunApp end")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pn *P2PNetwork) addRelayTunnel(config AppConfig, appid uint64, appkey uint64) (*P2PTunnel, uint64, error) {
|
func (pn *P2PNetwork) addRelayTunnel(config AppConfig) (*P2PTunnel, uint64, string, error) {
|
||||||
gLog.Printf(LevelINFO, "addRelayTunnel to %s start", config.PeerNode)
|
gLog.Printf(LvINFO, "addRelayTunnel to %s start", config.PeerNode)
|
||||||
defer gLog.Printf(LevelINFO, "addRelayTunnel to %s end", config.PeerNode)
|
defer gLog.Printf(LvINFO, "addRelayTunnel to %s end", config.PeerNode)
|
||||||
pn.write(MsgRelay, MsgRelayNodeReq, &RelayNodeReq{config.PeerNode})
|
pn.write(MsgRelay, MsgRelayNodeReq, &RelayNodeReq{config.PeerNode})
|
||||||
head, body := pn.read("", MsgRelay, MsgRelayNodeRsp, time.Second*10)
|
head, body := pn.read("", MsgRelay, MsgRelayNodeRsp, time.Second*10)
|
||||||
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{}
|
||||||
err := json.Unmarshal(body, &rsp)
|
err := json.Unmarshal(body, &rsp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Printf(LevelERROR, "wrong RelayNodeRsp:%s", err)
|
gLog.Printf(LvERROR, "wrong RelayNodeRsp:%s", err)
|
||||||
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(LevelERROR, "MsgRelayNodeReq error")
|
gLog.Printf(LvERROR, "MsgRelayNodeReq error")
|
||||||
return nil, 0, errors.New("MsgRelayNodeReq error")
|
return nil, 0, "", errors.New("MsgRelayNodeReq error")
|
||||||
}
|
}
|
||||||
gLog.Printf(LevelINFO, "got relay node:%s", rsp.RelayName)
|
gLog.Printf(LvINFO, "got relay node:%s", rsp.RelayName)
|
||||||
relayConfig := config
|
relayConfig := config
|
||||||
relayConfig.PeerNode = rsp.RelayName
|
relayConfig.PeerNode = rsp.RelayName
|
||||||
relayConfig.peerToken = rsp.RelayToken
|
relayConfig.peerToken = rsp.RelayToken
|
||||||
t, err := pn.addDirectTunnel(relayConfig, 0)
|
t, err := pn.addDirectTunnel(relayConfig, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, "direct connect error:", err)
|
gLog.Println(LvERROR, "direct connect error:", err)
|
||||||
return nil, 0, err
|
return nil, 0, "", err
|
||||||
}
|
}
|
||||||
// notify peer addRelayTunnel
|
// notify peer addRelayTunnel
|
||||||
req := AddRelayTunnelReq{
|
req := AddRelayTunnelReq{
|
||||||
From: pn.config.Node,
|
From: pn.config.Node,
|
||||||
RelayName: rsp.RelayName,
|
RelayName: rsp.RelayName,
|
||||||
RelayToken: rsp.RelayToken,
|
RelayToken: rsp.RelayToken,
|
||||||
AppID: appid,
|
|
||||||
AppKey: appkey,
|
|
||||||
}
|
}
|
||||||
gLog.Printf(LevelINFO, "push relay %s---------%s", config.PeerNode, rsp.RelayName)
|
gLog.Printf(LvINFO, "push relay %s---------%s", config.PeerNode, rsp.RelayName)
|
||||||
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) // TODO: const value
|
head, body = pn.read(config.PeerNode, MsgPush, MsgPushAddRelayTunnelRsp, PeerAddRelayTimeount) // TODO: const value
|
||||||
if head == nil {
|
if head == nil {
|
||||||
gLog.Printf(LevelERROR, "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{}
|
||||||
err = json.Unmarshal(body, &rspID)
|
err = json.Unmarshal(body, &rspID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Printf(LevelERROR, "wrong RelayNodeRsp:%s", err)
|
gLog.Printf(LvERROR, "wrong RelayNodeRsp:%s", err)
|
||||||
return nil, 0, errors.New("unmarshal MsgRelayNodeRsp error")
|
return nil, 0, "", errors.New("unmarshal MsgRelayNodeRsp error")
|
||||||
}
|
}
|
||||||
return t, rspID.ID, err
|
return t, rspID.ID, rsp.Mode, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// use *AppConfig to save status
|
||||||
func (pn *P2PNetwork) AddApp(config AppConfig) error {
|
func (pn *P2PNetwork) AddApp(config AppConfig) error {
|
||||||
gLog.Printf(LevelINFO, "addApp %s%d to %s:%s:%d start", config.Protocol, config.SrcPort, config.PeerNode, config.DstHost, config.DstPort)
|
gLog.Printf(LvINFO, "addApp %s to %s:%s:%d start", config.AppName, config.PeerNode, config.DstHost, config.DstPort)
|
||||||
defer gLog.Printf(LevelINFO, "addApp %s%d to %s:%s:%d end", config.Protocol, config.SrcPort, config.PeerNode, config.DstHost, config.DstPort)
|
defer gLog.Printf(LvINFO, "addApp %s to %s:%s:%d end", config.AppName, config.PeerNode, config.DstHost, config.DstPort)
|
||||||
if !pn.online {
|
if !pn.online {
|
||||||
return errors.New("P2PNetwork offline")
|
return errors.New("P2PNetwork offline")
|
||||||
}
|
}
|
||||||
// check if app already exist?
|
// check if app already exist?
|
||||||
appExist := false
|
appExist := false
|
||||||
pn.apps.Range(func(_, i interface{}) bool {
|
_, ok := pn.apps.Load(fmt.Sprintf("%s%d", config.Protocol, config.SrcPort))
|
||||||
app := i.(*p2pApp)
|
if ok {
|
||||||
if app.config.Protocol == config.Protocol && app.config.SrcPort == config.SrcPort {
|
appExist = true
|
||||||
appExist = true
|
}
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
if appExist {
|
if appExist {
|
||||||
return errors.New("P2PApp already exist")
|
return errors.New("P2PApp already exist")
|
||||||
}
|
}
|
||||||
appID := rand.Uint64()
|
appID := rand.Uint64()
|
||||||
appKey := uint64(0)
|
appKey := uint64(0)
|
||||||
t, err := pn.addDirectTunnel(config, 0)
|
|
||||||
var rtid uint64
|
var rtid uint64
|
||||||
relayNode := ""
|
relayNode := ""
|
||||||
peerNatType := 100
|
relayMode := ""
|
||||||
|
peerNatType := NATUnknown
|
||||||
peerIP := ""
|
peerIP := ""
|
||||||
errMsg := ""
|
errMsg := ""
|
||||||
if err != nil && err == ErrorHandshake {
|
t, err := pn.addDirectTunnel(config, 0)
|
||||||
gLog.Println(LevelERROR, "direct connect failed, try to relay")
|
|
||||||
appKey = rand.Uint64()
|
|
||||||
t, rtid, err = pn.addRelayTunnel(config, appID, appKey)
|
|
||||||
if t != nil {
|
|
||||||
relayNode = t.config.PeerNode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if t != nil {
|
if t != nil {
|
||||||
peerNatType = t.config.peerNatType
|
peerNatType = t.config.peerNatType
|
||||||
peerIP = t.config.peerIP
|
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 && t.isSupportTCP() {
|
||||||
|
// t, err = pn.addDirectTunnel(config, 0)
|
||||||
|
// if t != nil {
|
||||||
|
// peerNatType = t.config.peerNatType
|
||||||
|
// peerIP = t.config.peerIP
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
if err != nil && err == ErrorHandshake {
|
||||||
|
gLog.Println(LvERROR, "direct connect failed, try to relay")
|
||||||
|
t, rtid, relayMode, err = pn.addRelayTunnel(config)
|
||||||
|
if t != nil {
|
||||||
|
relayNode = t.config.PeerNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errMsg = err.Error()
|
errMsg = err.Error()
|
||||||
}
|
}
|
||||||
@@ -247,7 +264,6 @@ func (pn *P2PNetwork) AddApp(config AppConfig) error {
|
|||||||
PeerNode: config.PeerNode,
|
PeerNode: config.PeerNode,
|
||||||
DstPort: config.DstPort,
|
DstPort: config.DstPort,
|
||||||
DstHost: config.DstHost,
|
DstHost: config.DstHost,
|
||||||
PeerUser: config.PeerUser,
|
|
||||||
PeerNatType: peerNatType,
|
PeerNatType: peerNatType,
|
||||||
PeerIP: peerIP,
|
PeerIP: peerIP,
|
||||||
ShareBandwidth: pn.config.ShareBandwidth,
|
ShareBandwidth: pn.config.ShareBandwidth,
|
||||||
@@ -255,15 +271,29 @@ func (pn *P2PNetwork) AddApp(config AppConfig) error {
|
|||||||
Version: OpenP2PVersion,
|
Version: OpenP2PVersion,
|
||||||
}
|
}
|
||||||
pn.write(MsgReport, MsgReportConnect, &req)
|
pn.write(MsgReport, MsgReportConnect, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rtid != 0 || t.conn.Protocol() == "tcp" {
|
||||||
|
// sync appkey
|
||||||
|
appKey = rand.Uint64()
|
||||||
|
req := APPKeySync{
|
||||||
|
AppID: appID,
|
||||||
|
AppKey: appKey,
|
||||||
|
}
|
||||||
|
gLog.Printf(LvINFO, "sync appkey to %s", config.PeerNode)
|
||||||
|
pn.push(config.PeerNode, MsgPushAPPKey, &req)
|
||||||
|
}
|
||||||
app := p2pApp{
|
app := p2pApp{
|
||||||
id: appID,
|
id: appID,
|
||||||
key: appKey,
|
key: appKey,
|
||||||
tunnel: t,
|
tunnel: t,
|
||||||
config: config,
|
config: config,
|
||||||
rtid: rtid,
|
rtid: rtid,
|
||||||
hbTime: time.Now()}
|
relayNode: relayNode,
|
||||||
pn.apps.Store(appID, &app)
|
relayMode: relayMode,
|
||||||
|
hbTime: time.Now()}
|
||||||
|
pn.apps.Store(fmt.Sprintf("%s%d", config.Protocol, config.SrcPort), &app)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
go app.listen()
|
go app.listen()
|
||||||
}
|
}
|
||||||
@@ -271,25 +301,21 @@ func (pn *P2PNetwork) AddApp(config AppConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pn *P2PNetwork) DeleteApp(config AppConfig) {
|
func (pn *P2PNetwork) DeleteApp(config AppConfig) {
|
||||||
gLog.Printf(LevelINFO, "DeleteApp %s%d start", config.Protocol, config.SrcPort)
|
gLog.Printf(LvINFO, "DeleteApp %s%d start", config.Protocol, config.SrcPort)
|
||||||
defer gLog.Printf(LevelINFO, "DeleteApp %s%d end", config.Protocol, config.SrcPort)
|
defer gLog.Printf(LvINFO, "DeleteApp %s%d end", config.Protocol, config.SrcPort)
|
||||||
// close the apps of this config
|
// close the apps of this config
|
||||||
pn.apps.Range(func(_, i interface{}) bool {
|
i, ok := pn.apps.Load(fmt.Sprintf("%s%d", config.Protocol, config.SrcPort))
|
||||||
|
if ok {
|
||||||
app := i.(*p2pApp)
|
app := i.(*p2pApp)
|
||||||
if app.config.Protocol == config.Protocol && app.config.SrcPort == config.SrcPort {
|
gLog.Printf(LvINFO, "app %s exist, delete it", fmt.Sprintf("%s%d", config.Protocol, config.SrcPort))
|
||||||
gLog.Printf(LevelINFO, "app %s exist, delete it", fmt.Sprintf("%s%d", config.Protocol, config.SrcPort))
|
app.close()
|
||||||
app := i.(*p2pApp)
|
pn.apps.Delete(fmt.Sprintf("%s%d", config.Protocol, config.SrcPort))
|
||||||
app.close()
|
}
|
||||||
pn.apps.Delete(app.id)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pn *P2PNetwork) addDirectTunnel(config AppConfig, tid uint64) (*P2PTunnel, error) {
|
func (pn *P2PNetwork) addDirectTunnel(config AppConfig, tid uint64) (*P2PTunnel, error) {
|
||||||
gLog.Printf(LevelINFO, "addDirectTunnel %s%d to %s:%s:%d start", config.Protocol, config.SrcPort, config.PeerNode, config.DstHost, config.DstPort)
|
gLog.Printf(LvDEBUG, "addDirectTunnel %s%d to %s:%s:%d start", config.Protocol, config.SrcPort, config.PeerNode, config.DstHost, config.DstPort)
|
||||||
defer gLog.Printf(LevelINFO, "addDirectTunnel %s%d to %s:%s:%d end", config.Protocol, config.SrcPort, config.PeerNode, config.DstHost, config.DstPort)
|
defer gLog.Printf(LvDEBUG, "addDirectTunnel %s%d to %s:%s:%d end", config.Protocol, config.SrcPort, config.PeerNode, config.DstHost, config.DstPort)
|
||||||
isClient := false
|
isClient := false
|
||||||
// client side tid=0, assign random uint64
|
// client side tid=0, assign random uint64
|
||||||
if tid == 0 {
|
if tid == 0 {
|
||||||
@@ -309,11 +335,11 @@ func (pn *P2PNetwork) addDirectTunnel(config AppConfig, tid uint64) (*P2PTunnel,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// client side checking
|
// client side checking
|
||||||
gLog.Println(LevelINFO, "tunnel already exist ", config.PeerNode)
|
gLog.Println(LvINFO, "tunnel already exist ", config.PeerNode)
|
||||||
isActive := t.checkActive()
|
isActive := t.checkActive()
|
||||||
// inactive, close it
|
// inactive, close it
|
||||||
if !isActive {
|
if !isActive {
|
||||||
gLog.Println(LevelINFO, "but it's not active, close it ", config.PeerNode)
|
gLog.Println(LvINFO, "but it's not active, close it ", config.PeerNode)
|
||||||
t.close()
|
t.close()
|
||||||
} else {
|
} else {
|
||||||
// active
|
// active
|
||||||
@@ -335,52 +361,45 @@ func (pn *P2PNetwork) addDirectTunnel(config AppConfig, tid uint64) (*P2PTunnel,
|
|||||||
t.init()
|
t.init()
|
||||||
if isClient {
|
if isClient {
|
||||||
if err := t.connect(); err != nil {
|
if err := t.connect(); err != nil {
|
||||||
gLog.Println(LevelERROR, "p2pTunnel connect error:", err)
|
gLog.Println(LvERROR, "p2pTunnel connect error:", err)
|
||||||
return t, err
|
return t, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rsp := PushConnectRsp{
|
|
||||||
Error: 0,
|
|
||||||
Detail: "connect ok",
|
|
||||||
To: t.config.PeerNode,
|
|
||||||
From: pn.config.Node,
|
|
||||||
NatType: pn.config.natType,
|
|
||||||
FromIP: pn.config.publicIP,
|
|
||||||
ConeNatPort: t.coneNatPort,
|
|
||||||
ID: t.id}
|
|
||||||
t.pn.push(t.config.PeerNode, MsgPushConnectRsp, rsp)
|
|
||||||
if err := t.listen(); err != nil {
|
if err := t.listen(); err != nil {
|
||||||
gLog.Println(LevelERROR, "p2pTunnel listen error:", err)
|
gLog.Println(LvERROR, "p2pTunnel listen error:", err)
|
||||||
return t, err
|
return t, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// store it when success
|
// store it when success
|
||||||
gLog.Printf(LevelDEBUG, "store tunnel %d", tid)
|
gLog.Printf(LvDEBUG, "store tunnel %d", tid)
|
||||||
pn.allTunnels.Store(tid, t)
|
pn.allTunnels.Store(tid, t)
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pn *P2PNetwork) init() error {
|
func (pn *P2PNetwork) init() error {
|
||||||
gLog.Println(LevelINFO, "init start")
|
gLog.Println(LvINFO, "init start")
|
||||||
var err error
|
var err error
|
||||||
for {
|
for {
|
||||||
pn.config.hostName, err = os.Hostname()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// detect nat type
|
// detect nat type
|
||||||
pn.config.publicIP, pn.config.natType, 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)
|
||||||
// TODO rm test s2s
|
// for testcase
|
||||||
if strings.Contains(pn.config.Node, "openp2pS2STest") {
|
if strings.Contains(pn.config.Node, "openp2pS2STest") {
|
||||||
pn.config.natType = NATSymmetric
|
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 {
|
if err != nil {
|
||||||
gLog.Println(LevelINFO, "detect NAT type error:", err)
|
gLog.Println(LvDEBUG, "detect NAT type error:", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
gLog.Println(LevelINFO, "detect NAT type:", pn.config.natType, " publicIP:", pn.config.publicIP)
|
gLog.Println(LvDEBUG, "detect NAT type:", pn.config.natType, " publicIP:", pn.config.publicIP)
|
||||||
gatewayURL := fmt.Sprintf("%s:%d", pn.config.ServerHost, pn.config.ServerPort)
|
gatewayURL := fmt.Sprintf("%s:%d", pn.config.ServerHost, pn.config.ServerPort)
|
||||||
forwardPath := "/openp2p/v1/login"
|
forwardPath := "/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
|
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
|
||||||
@@ -388,16 +407,10 @@ func (pn *P2PNetwork) init() error {
|
|||||||
u := url.URL{Scheme: "wss", Host: gatewayURL, Path: forwardPath}
|
u := url.URL{Scheme: "wss", Host: gatewayURL, Path: forwardPath}
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
q.Add("node", pn.config.Node)
|
q.Add("node", pn.config.Node)
|
||||||
q.Add("user", pn.config.User)
|
q.Add("token", fmt.Sprintf("%d", pn.config.Token))
|
||||||
q.Add("password", pn.config.Password)
|
|
||||||
q.Add("version", OpenP2PVersion)
|
q.Add("version", OpenP2PVersion)
|
||||||
q.Add("nattype", fmt.Sprintf("%d", pn.config.natType))
|
q.Add("nattype", fmt.Sprintf("%d", pn.config.natType))
|
||||||
|
q.Add("sharebandwidth", fmt.Sprintf("%d", pn.config.ShareBandwidth))
|
||||||
noShareStr := "false"
|
|
||||||
if pn.config.NoShare {
|
|
||||||
noShareStr = "true"
|
|
||||||
}
|
|
||||||
q.Add("noshare", noShareStr)
|
|
||||||
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)
|
||||||
@@ -419,28 +432,30 @@ func (pn *P2PNetwork) init() error {
|
|||||||
pn.config.os = getOsName()
|
pn.config.os = getOsName()
|
||||||
|
|
||||||
req := ReportBasic{
|
req := ReportBasic{
|
||||||
Mac: pn.config.mac,
|
Mac: pn.config.mac,
|
||||||
LanIP: pn.config.localIP,
|
LanIP: pn.config.localIP,
|
||||||
OS: pn.config.os,
|
OS: pn.config.os,
|
||||||
Version: OpenP2PVersion,
|
HasIPv4: pn.config.hasIPv4,
|
||||||
|
HasUPNPorNATPMP: pn.config.hasUPNPorNATPMP,
|
||||||
|
Version: OpenP2PVersion,
|
||||||
}
|
}
|
||||||
rsp := netInfo()
|
rsp := netInfo()
|
||||||
gLog.Println(LevelINFO, rsp)
|
gLog.Println(LvDEBUG, "netinfo:", rsp)
|
||||||
if rsp != nil && rsp.Country != "" {
|
if rsp != nil && rsp.Country != "" {
|
||||||
if len(rsp.IP) == net.IPv6len {
|
if IsIPv6(rsp.IP.String()) {
|
||||||
pn.config.ipv6 = rsp.IP.String()
|
pn.config.IPv6 = rsp.IP.String()
|
||||||
req.IPv6 = rsp.IP.String()
|
req.IPv6 = rsp.IP.String()
|
||||||
}
|
}
|
||||||
req.NetInfo = *rsp
|
req.NetInfo = *rsp
|
||||||
}
|
}
|
||||||
pn.write(MsgReport, MsgReportBasic, &req)
|
pn.write(MsgReport, MsgReportBasic, &req)
|
||||||
gLog.Println(LevelINFO, "P2PNetwork init ok")
|
gLog.Println(LvDEBUG, "P2PNetwork init ok")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// init failed, retry
|
// init failed, retry
|
||||||
pn.restartCh <- true
|
pn.restartCh <- true
|
||||||
gLog.Println(LevelERROR, "P2PNetwork init error:", err)
|
gLog.Println(LvERROR, "P2PNetwork init error:", err)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -449,7 +464,7 @@ func (pn *P2PNetwork) handleMessage(t int, 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(LevelERROR, "handleMessage error:", err)
|
gLog.Println(LvERROR, "handleMessage error:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch head.MainType {
|
switch head.MainType {
|
||||||
@@ -458,21 +473,26 @@ func (pn *P2PNetwork) handleMessage(t int, msg []byte) {
|
|||||||
rsp := LoginRsp{}
|
rsp := LoginRsp{}
|
||||||
err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp)
|
err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Printf(LevelERROR, "wrong login response:%s", err)
|
gLog.Printf(LvERROR, "wrong login response:%s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if rsp.Error != 0 {
|
if rsp.Error != 0 {
|
||||||
gLog.Printf(LevelERROR, "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 {
|
||||||
pn.serverTs = rsp.Ts
|
pn.serverTs = rsp.Ts
|
||||||
|
pn.config.Token = rsp.Token
|
||||||
|
pn.config.User = rsp.User
|
||||||
|
gConf.setToken(rsp.Token)
|
||||||
|
gConf.setUser(rsp.User)
|
||||||
|
gConf.save()
|
||||||
pn.localTs = time.Now().Unix()
|
pn.localTs = time.Now().Unix()
|
||||||
gLog.Printf(LevelINFO, "login ok. Server ts=%d, local ts=%d", rsp.Ts, pn.localTs)
|
gLog.Printf(LvINFO, "login ok. user=%s,Server ts=%d, local ts=%d", rsp.User, rsp.Ts, pn.localTs)
|
||||||
}
|
}
|
||||||
case MsgHeartbeat:
|
case MsgHeartbeat:
|
||||||
gLog.Printf(LevelDEBUG, "P2PNetwork heartbeat ok")
|
gLog.Printf(LvDEBUG, "P2PNetwork heartbeat ok")
|
||||||
case MsgPush:
|
case MsgPush:
|
||||||
pn.handlePush(head.SubType, msg)
|
handlePush(pn, head.SubType, msg)
|
||||||
default:
|
default:
|
||||||
pn.msgMapMtx.Lock()
|
pn.msgMapMtx.Lock()
|
||||||
ch := pn.msgMap[0]
|
ch := pn.msgMap[0]
|
||||||
@@ -483,21 +503,21 @@ func (pn *P2PNetwork) handleMessage(t int, msg []byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pn *P2PNetwork) readLoop() {
|
func (pn *P2PNetwork) readLoop() {
|
||||||
gLog.Printf(LevelINFO, "P2PNetwork readLoop start")
|
gLog.Printf(LvDEBUG, "P2PNetwork readLoop start")
|
||||||
pn.wg.Add(1)
|
pn.wg.Add(1)
|
||||||
defer pn.wg.Done()
|
defer pn.wg.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))
|
||||||
t, msg, err := pn.conn.ReadMessage()
|
t, msg, err := pn.conn.ReadMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Printf(LevelERROR, "P2PNetwork read error:%s", err)
|
gLog.Printf(LvERROR, "P2PNetwork read error:%s", err)
|
||||||
pn.conn.Close()
|
pn.conn.Close()
|
||||||
pn.restartCh <- true
|
pn.restartCh <- true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
pn.handleMessage(t, msg)
|
pn.handleMessage(t, msg)
|
||||||
}
|
}
|
||||||
gLog.Printf(LevelINFO, "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 {
|
||||||
@@ -511,14 +531,14 @@ func (pn *P2PNetwork) write(mainType uint16, subType uint16, packet interface{})
|
|||||||
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(LevelERROR, "write msgType %d,%d error:%s", mainType, subType, err)
|
gLog.Printf(LvERROR, "write msgType %d,%d error:%s", mainType, subType, err)
|
||||||
pn.conn.Close()
|
pn.conn.Close()
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pn *P2PNetwork) relay(to uint64, body []byte) error {
|
func (pn *P2PNetwork) relay(to uint64, body []byte) error {
|
||||||
gLog.Printf(LevelDEBUG, "relay data to %d", to)
|
gLog.Printf(LvDEBUG, "relay data to %d", to)
|
||||||
i, ok := pn.allTunnels.Load(to)
|
i, ok := pn.allTunnels.Load(to)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
@@ -532,7 +552,7 @@ func (pn *P2PNetwork) relay(to uint64, body []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pn *P2PNetwork) push(to string, subType uint16, packet interface{}) error {
|
func (pn *P2PNetwork) push(to string, subType uint16, packet interface{}) error {
|
||||||
gLog.Printf(LevelDEBUG, "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")
|
||||||
}
|
}
|
||||||
@@ -554,7 +574,7 @@ func (pn *P2PNetwork) push(to string, subType uint16, packet interface{}) error
|
|||||||
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(LevelERROR, "push to %s error:%s", to, err)
|
gLog.Printf(LvERROR, "push to %s error:%s", to, err)
|
||||||
pn.conn.Close()
|
pn.conn.Close()
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@@ -567,16 +587,19 @@ func (pn *P2PNetwork) read(node string, mainType uint16, subType uint16, timeout
|
|||||||
} else {
|
} else {
|
||||||
nodeID = nodeNameToID(node)
|
nodeID = nodeNameToID(node)
|
||||||
}
|
}
|
||||||
|
pn.msgMapMtx.Lock()
|
||||||
|
ch := pn.msgMap[nodeID]
|
||||||
|
pn.msgMapMtx.Unlock()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-time.After(timeout):
|
case <-time.After(timeout):
|
||||||
gLog.Printf(LevelERROR, "wait msg%d:%d timeout", mainType, subType)
|
gLog.Printf(LvERROR, "wait msg%d:%d timeout", mainType, subType)
|
||||||
return
|
return
|
||||||
case msg := <-pn.msgMap[nodeID]:
|
case msg := <-ch:
|
||||||
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(LevelERROR, "read msg error:", err)
|
gLog.Println(LvERROR, "read msg error:", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if head.MainType != mainType || head.SubType != subType {
|
if head.MainType != mainType || head.SubType != subType {
|
||||||
@@ -592,107 +615,33 @@ func (pn *P2PNetwork) read(node string, mainType uint16, subType uint16, timeout
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pn *P2PNetwork) handlePush(subType uint16, msg []byte) error {
|
|
||||||
pushHead := PushHeader{}
|
|
||||||
err := binary.Read(bytes.NewReader(msg[openP2PHeaderSize:openP2PHeaderSize+PushHeaderSize]), binary.LittleEndian, &pushHead)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
gLog.Printf(LevelDEBUG, "handle push msg type:%d, push header:%+v", subType, pushHead)
|
|
||||||
switch subType {
|
|
||||||
case MsgPushConnectReq:
|
|
||||||
req := PushConnectReq{}
|
|
||||||
err := json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req)
|
|
||||||
if err != nil {
|
|
||||||
gLog.Printf(LevelERROR, "wrong MsgPushConnectReq:%s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
gLog.Printf(LevelINFO, "%s is connecting...", req.From)
|
|
||||||
gLog.Println(LevelDEBUG, "push connect response to ", req.From)
|
|
||||||
// verify token or name&password
|
|
||||||
if VerifyTOTP(req.Token, pn.config.User, pn.config.Password, time.Now().Unix()+(pn.serverTs-pn.localTs)) || // localTs may behind, auto adjust ts
|
|
||||||
VerifyTOTP(req.Token, pn.config.User, pn.config.Password, time.Now().Unix()) ||
|
|
||||||
(req.User == pn.config.User && req.Password == pn.config.Password) {
|
|
||||||
gLog.Printf(LevelINFO, "Access Granted\n")
|
|
||||||
config := AppConfig{}
|
|
||||||
config.peerNatType = req.NatType
|
|
||||||
config.peerConeNatPort = req.ConeNatPort
|
|
||||||
config.peerIP = req.FromIP
|
|
||||||
config.PeerNode = req.From
|
|
||||||
// share relay node will limit bandwidth
|
|
||||||
if req.User != pn.config.User || req.Password != pn.config.Password {
|
|
||||||
gLog.Printf(LevelINFO, "set share bandwidth %d mbps", pn.config.ShareBandwidth)
|
|
||||||
config.shareBandwidth = pn.config.ShareBandwidth
|
|
||||||
}
|
|
||||||
// go pn.AddTunnel(config, req.ID)
|
|
||||||
go pn.addDirectTunnel(config, req.ID)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
gLog.Println(LevelERROR, "Access Denied:", req.From)
|
|
||||||
rsp := PushConnectRsp{
|
|
||||||
Error: 1,
|
|
||||||
Detail: fmt.Sprintf("connect to %s error: Access Denied", pn.config.Node),
|
|
||||||
To: req.From,
|
|
||||||
From: pn.config.Node,
|
|
||||||
}
|
|
||||||
pn.push(req.From, MsgPushConnectRsp, rsp)
|
|
||||||
case MsgPushRsp:
|
|
||||||
rsp := PushRsp{}
|
|
||||||
err := json.Unmarshal(msg[openP2PHeaderSize:], &rsp)
|
|
||||||
if err != nil {
|
|
||||||
gLog.Printf(LevelERROR, "wrong pushRsp:%s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rsp.Error == 0 {
|
|
||||||
gLog.Printf(LevelDEBUG, "push ok, detail:%s", rsp.Detail)
|
|
||||||
} else {
|
|
||||||
gLog.Printf(LevelERROR, "push error:%d, detail:%s", rsp.Error, rsp.Detail)
|
|
||||||
}
|
|
||||||
case MsgPushAddRelayTunnelReq:
|
|
||||||
req := AddRelayTunnelReq{}
|
|
||||||
err := json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req)
|
|
||||||
if err != nil {
|
|
||||||
gLog.Printf(LevelERROR, "wrong RelayNodeRsp:%s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
config := AppConfig{}
|
|
||||||
config.PeerNode = req.RelayName
|
|
||||||
config.peerToken = req.RelayToken
|
|
||||||
// set user password, maybe the relay node is your private node
|
|
||||||
config.PeerUser = pn.config.User
|
|
||||||
config.PeerPassword = pn.config.Password
|
|
||||||
go func(r AddRelayTunnelReq) {
|
|
||||||
t, errDt := pn.addDirectTunnel(config, 0)
|
|
||||||
if errDt == nil {
|
|
||||||
// notify peer relay ready
|
|
||||||
msg := TunnelMsg{ID: t.id}
|
|
||||||
pn.push(r.From, MsgPushAddRelayTunnelRsp, msg)
|
|
||||||
SaveKey(req.AppID, req.AppKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
}(req)
|
|
||||||
case MsgPushUpdate:
|
|
||||||
update()
|
|
||||||
if gConf.daemonMode {
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
pn.msgMapMtx.Lock()
|
|
||||||
ch := pn.msgMap[pushHead.From]
|
|
||||||
pn.msgMapMtx.Unlock()
|
|
||||||
ch <- msg
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
||||||
key := id.(uint64)
|
app := i.(*p2pApp)
|
||||||
if key != appID {
|
if app.id != appID {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
app := i.(*p2pApp)
|
|
||||||
app.updateHeartbeat()
|
app.updateHeartbeat()
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pn *P2PNetwork) refreshIPv6() {
|
||||||
|
if !IsIPv6(pn.config.IPv6) { // 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.IPv6 = string(buf[:n])
|
||||||
|
}
|
||||||
|
|||||||
441
p2ptunnel.go
@@ -14,7 +14,7 @@ import (
|
|||||||
|
|
||||||
type P2PTunnel struct {
|
type P2PTunnel struct {
|
||||||
pn *P2PNetwork
|
pn *P2PNetwork
|
||||||
conn p2pConn
|
conn underlay
|
||||||
hbTime time.Time
|
hbTime time.Time
|
||||||
hbMtx sync.Mutex
|
hbMtx sync.Mutex
|
||||||
hbTimeRelay time.Time
|
hbTimeRelay time.Time
|
||||||
@@ -25,9 +25,10 @@ type P2PTunnel struct {
|
|||||||
id uint64
|
id uint64
|
||||||
running bool
|
running bool
|
||||||
runMtx sync.Mutex
|
runMtx sync.Mutex
|
||||||
isServer bool // 0:server 1:client
|
tunnelServer bool // different from underlayServer
|
||||||
coneLocalPort int
|
coneLocalPort int
|
||||||
coneNatPort int
|
coneNatPort int
|
||||||
|
linkMode string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *P2PTunnel) init() {
|
func (t *P2PTunnel) init() {
|
||||||
@@ -39,30 +40,50 @@ func (t *P2PTunnel) init() {
|
|||||||
localPort := int(rand.Uint32()%10000 + 50000)
|
localPort := int(rand.Uint32()%10000 + 50000)
|
||||||
if t.pn.config.natType == NATCone {
|
if t.pn.config.natType == NATCone {
|
||||||
// prepare one random cone hole
|
// prepare one random cone hole
|
||||||
_, _, port1, _ := natTest(t.pn.config.ServerHost, t.pn.config.UDPPort1, localPort, 0)
|
_, _, _, port1, _ := natTest(t.pn.config.ServerHost, t.pn.config.UDPPort1, localPort, 0)
|
||||||
t.coneLocalPort = localPort
|
t.coneLocalPort = localPort
|
||||||
t.coneNatPort = port1
|
t.coneNatPort = port1
|
||||||
t.la = &net.UDPAddr{IP: net.ParseIP(t.pn.config.localIP), Port: t.coneLocalPort}
|
t.la = &net.UDPAddr{IP: net.ParseIP(t.pn.config.localIP), Port: t.coneLocalPort}
|
||||||
} else {
|
} else {
|
||||||
t.coneLocalPort = localPort
|
t.coneLocalPort = localPort
|
||||||
t.coneNatPort = localPort // NATNONE or symmetric doesn't need coneNatPort
|
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}
|
t.la = &net.UDPAddr{IP: net.ParseIP(t.pn.config.localIP), Port: t.coneLocalPort}
|
||||||
}
|
}
|
||||||
gLog.Printf(LevelDEBUG, "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(LevelINFO, "start p2pTunnel to %s ", t.config.PeerNode)
|
gLog.Printf(LvDEBUG, "start p2pTunnel to %s ", t.config.PeerNode)
|
||||||
t.isServer = false
|
t.tunnelServer = false
|
||||||
|
t.pn.refreshIPv6()
|
||||||
|
appKey := uint64(0)
|
||||||
req := PushConnectReq{
|
req := PushConnectReq{
|
||||||
User: t.config.PeerUser,
|
Token: t.config.peerToken,
|
||||||
Password: t.config.PeerPassword,
|
From: t.pn.config.Node,
|
||||||
Token: t.config.peerToken,
|
FromIP: t.pn.config.publicIP,
|
||||||
From: t.pn.config.Node,
|
ConeNatPort: t.coneNatPort,
|
||||||
FromIP: t.pn.config.publicIP,
|
NatType: t.pn.config.natType,
|
||||||
ConeNatPort: t.coneNatPort,
|
HasIPv4: t.pn.config.hasIPv4,
|
||||||
NatType: t.pn.config.natType,
|
IPv6: t.pn.config.IPv6,
|
||||||
ID: t.id}
|
HasUPNPorNATPMP: t.pn.config.hasUPNPorNATPMP,
|
||||||
|
ID: t.id,
|
||||||
|
AppKey: appKey,
|
||||||
|
Version: OpenP2PVersion,
|
||||||
|
}
|
||||||
|
if req.Token == 0 { // no relay token
|
||||||
|
req.Token = t.pn.config.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, time.Second*10)
|
head, body := t.pn.read(t.config.PeerNode, MsgPush, MsgPushConnectRsp, time.Second*10)
|
||||||
if head == nil {
|
if head == nil {
|
||||||
@@ -71,19 +92,23 @@ func (t *P2PTunnel) connect() error {
|
|||||||
rsp := PushConnectRsp{}
|
rsp := PushConnectRsp{}
|
||||||
err := json.Unmarshal(body, &rsp)
|
err := json.Unmarshal(body, &rsp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Printf(LevelERROR, "wrong MsgPushConnectRsp:%s", err)
|
gLog.Printf(LvERROR, "wrong MsgPushConnectRsp:%s", 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 = int(rsp.NatType)
|
t.config.peerNatType = rsp.NatType
|
||||||
|
t.config.hasIPv4 = rsp.HasIPv4
|
||||||
|
t.config.IPv6 = rsp.IPv6
|
||||||
|
t.config.hasUPNPorNATPMP = rsp.HasUPNPorNATPMP
|
||||||
|
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
|
||||||
err = t.handshake()
|
err = t.start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, "handshake error:", err)
|
gLog.Println(LvERROR, "handshake error:", err)
|
||||||
err = ErrorHandshake
|
err = ErrorHandshake
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@@ -136,6 +161,20 @@ func (t *P2PTunnel) close() {
|
|||||||
t.pn.allTunnels.Delete(t.id)
|
t.pn.allTunnels.Delete(t.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *P2PTunnel) start() error {
|
||||||
|
if !t.config.isSupportTCP(t.pn.config) {
|
||||||
|
if err := t.handshake(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := t.connectUnderlay()
|
||||||
|
if err != nil {
|
||||||
|
gLog.Println(LvERROR, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (t *P2PTunnel) handshake() error {
|
func (t *P2PTunnel) handshake() error {
|
||||||
if t.config.peerConeNatPort > 0 {
|
if t.config.peerConeNatPort > 0 {
|
||||||
var err error
|
var err error
|
||||||
@@ -144,10 +183,10 @@ func (t *P2PTunnel) handshake() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gLog.Println(LevelINFO, "handshake to ", t.config.PeerNode)
|
gLog.Println(LvDEBUG, "handshake to ", t.config.PeerNode)
|
||||||
var err error
|
var err error
|
||||||
// TODO: handle NATNone, nodes with public ip has no punching
|
// 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)
|
err = handshakeC2C(t)
|
||||||
} else if t.config.peerNatType == NATSymmetric && t.pn.config.natType == NATSymmetric {
|
} else if t.config.peerNatType == NATSymmetric && t.pn.config.natType == NATSymmetric {
|
||||||
err = ErrorS2S
|
err = ErrorS2S
|
||||||
@@ -160,50 +199,65 @@ func (t *P2PTunnel) handshake() error {
|
|||||||
return errors.New("unknown error")
|
return errors.New("unknown error")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, "punch handshake error:", err)
|
gLog.Println(LvERROR, "punch handshake error:", err)
|
||||||
return err
|
|
||||||
}
|
|
||||||
gLog.Printf(LevelINFO, "handshake to %s ok", t.config.PeerNode)
|
|
||||||
err = t.run()
|
|
||||||
if err != nil {
|
|
||||||
gLog.Println(LevelERROR, err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
gLog.Printf(LvDEBUG, "handshake to %s ok", t.config.PeerNode)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *P2PTunnel) run() error {
|
func (t *P2PTunnel) connectUnderlay() (err error) {
|
||||||
if t.isServer {
|
if !t.config.isSupportTCP(t.pn.config) {
|
||||||
qConn, e := listenQuic(t.la.String(), TunnelIdleTimeout)
|
t.conn, err = t.connectUnderlayQuic()
|
||||||
if e != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelINFO, "listen quic error:", e, ", retry...")
|
return err
|
||||||
time.Sleep(time.Millisecond * 10)
|
}
|
||||||
qConn, e = listenQuic(t.la.String(), TunnelIdleTimeout)
|
} else {
|
||||||
if e != nil {
|
if IsIPv6(t.pn.config.IPv6) && IsIPv6(t.config.IPv6) { // both have ipv6
|
||||||
return fmt.Errorf("listen quic error:%s", e)
|
t.conn, err = t.connectUnderlayTCP6()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else { // hasipv4 or upnp
|
||||||
|
t.conn, err = t.connectUnderlayTCP()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.pn.push(t.config.PeerNode, MsgPushQuicConnect, nil)
|
}
|
||||||
e = qConn.Accept()
|
t.setRun(true)
|
||||||
if e != nil {
|
go t.readLoop()
|
||||||
qConn.CloseListener()
|
go t.heartbeatLoop()
|
||||||
return fmt.Errorf("accept quic error:%s", e)
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
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 {
|
||||||
|
gLog.Println(LvINFO, "listen quic error:", err, ", retry...")
|
||||||
}
|
}
|
||||||
_, buff, err := qConn.ReadMessage()
|
t.pn.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
|
||||||
if e != nil {
|
err = qConn.Accept()
|
||||||
|
if err != nil {
|
||||||
|
qConn.CloseListener()
|
||||||
|
return nil, fmt.Errorf("accept quic error:%s", err)
|
||||||
|
}
|
||||||
|
_, buff, err := qConn.ReadBuffer()
|
||||||
|
if err != nil {
|
||||||
qConn.listener.Close()
|
qConn.listener.Close()
|
||||||
return 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(LevelDEBUG, string(buff))
|
gLog.Println(LvDEBUG, string(buff))
|
||||||
}
|
}
|
||||||
qConn.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, []byte("OpenP2P,hello2"))
|
qConn.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, []byte("OpenP2P,hello2"))
|
||||||
gLog.Println(LevelINFO, "quic connection ok")
|
gLog.Println(LvDEBUG, "quic connection ok")
|
||||||
t.conn = qConn
|
return qConn, nil
|
||||||
t.setRun(true)
|
|
||||||
go t.readLoop()
|
|
||||||
go t.writeLoop()
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//else
|
//else
|
||||||
@@ -212,44 +266,136 @@ func (t *P2PTunnel) run() error {
|
|||||||
time.Sleep(time.Millisecond * 10)
|
time.Sleep(time.Millisecond * 10)
|
||||||
conn, e = net.ListenUDP("udp", t.la)
|
conn, e = net.ListenUDP("udp", t.la)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return fmt.Errorf("quic listen error:%s", e)
|
return nil, fmt.Errorf("quic listen error:%s", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.pn.read(t.config.PeerNode, MsgPush, MsgPushQuicConnect, time.Second*5)
|
t.pn.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, time.Second*5)
|
||||||
gLog.Println(LevelINFO, "quic dial to ", t.ra.String())
|
gLog.Println(LvDEBUG, "quic dial to ", t.ra.String())
|
||||||
qConn, e := dialQuic(conn, t.ra, TunnelIdleTimeout)
|
qConn, e = dialQuic(conn, t.ra, TunnelIdleTimeout)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return fmt.Errorf("quic dial to %s error:%s", t.ra.String(), e)
|
return nil, fmt.Errorf("quic dial to %s error:%s", t.ra.String(), e)
|
||||||
}
|
}
|
||||||
handshakeBegin := time.Now()
|
handshakeBegin := time.Now()
|
||||||
qConn.WriteBytes(MsgP2P, MsgTunnelHandshake, []byte("OpenP2P,hello"))
|
qConn.WriteBytes(MsgP2P, MsgTunnelHandshake, []byte("OpenP2P,hello"))
|
||||||
_, buff, err := qConn.ReadMessage()
|
_, buff, err := qConn.ReadBuffer()
|
||||||
if e != nil {
|
if e != nil {
|
||||||
qConn.listener.Close()
|
qConn.listener.Close()
|
||||||
return fmt.Errorf("read MsgTunnelHandshake error:%s", err)
|
return nil, fmt.Errorf("read MsgTunnelHandshake error:%s", err)
|
||||||
}
|
}
|
||||||
if buff != nil {
|
if buff != nil {
|
||||||
gLog.Println(LevelDEBUG, string(buff))
|
gLog.Println(LvDEBUG, string(buff))
|
||||||
}
|
}
|
||||||
|
|
||||||
gLog.Println(LevelINFO, "rtt=", time.Since(handshakeBegin))
|
gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin))
|
||||||
gLog.Println(LevelINFO, "quic connection ok")
|
gLog.Println(LvDEBUG, "quic connection ok")
|
||||||
t.conn = qConn
|
t.linkMode = LinkModeUDPPunch
|
||||||
t.setRun(true)
|
return qConn, nil
|
||||||
go t.readLoop()
|
}
|
||||||
go t.writeLoop()
|
|
||||||
return nil
|
// websocket
|
||||||
|
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() {
|
||||||
|
t.pn.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
|
||||||
|
qConn, err = listenTCP(t.coneNatPort, TunnelIdleTimeout)
|
||||||
|
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")
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("TCP dial to %s error:%s", t.ra.String(), 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 {
|
||||||
|
gLog.Println(LvDEBUG, string(buff))
|
||||||
|
}
|
||||||
|
|
||||||
|
gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin))
|
||||||
|
gLog.Println(LvDEBUG, "TCP connection ok")
|
||||||
|
t.linkMode = LinkModeIPv4
|
||||||
|
return qConn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *P2PTunnel) connectUnderlayTCP6() (c underlay, err error) {
|
||||||
|
gLog.Println(LvINFO, "connectUnderlayTCP6 start")
|
||||||
|
defer gLog.Println(LvINFO, "connectUnderlayTCP6 end")
|
||||||
|
var qConn *underlayTCP6
|
||||||
|
if t.isUnderlayServer() {
|
||||||
|
t.pn.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
|
||||||
|
qConn, err = listenTCP6(t.coneNatPort, TunnelIdleTimeout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("listen TCP6 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, "TCP6 connection ok")
|
||||||
|
return qConn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//else
|
||||||
|
t.pn.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, time.Second*5)
|
||||||
|
gLog.Println(LvDEBUG, "TCP6 dial to ", t.config.IPv6)
|
||||||
|
qConn, err = dialTCP6(t.config.IPv6, t.config.peerConeNatPort)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("TCP6 dial to %s:%d error:%s", t.config.IPv6, 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 {
|
||||||
|
gLog.Println(LvDEBUG, string(buff))
|
||||||
|
}
|
||||||
|
|
||||||
|
gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin))
|
||||||
|
gLog.Println(LvDEBUG, "TCP6 connection ok")
|
||||||
|
t.linkMode = LinkModeIPv6
|
||||||
|
return qConn, 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(LevelINFO, "%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(TunnelIdleTimeout))
|
t.conn.SetReadDeadline(time.Now().Add(TunnelIdleTimeout))
|
||||||
head, body, err := t.conn.ReadMessage()
|
head, body, err := t.conn.ReadBuffer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if t.isRuning() {
|
if t.isRuning() {
|
||||||
gLog.Printf(LevelERROR, "%d tunnel read error:%s", t.id, err)
|
gLog.Printf(LvERROR, "%d tunnel read error:%s", t.id, err)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -259,25 +405,25 @@ func (t *P2PTunnel) readLoop() {
|
|||||||
switch head.SubType {
|
switch head.SubType {
|
||||||
case MsgTunnelHeartbeat:
|
case MsgTunnelHeartbeat:
|
||||||
t.conn.WriteBytes(MsgP2P, MsgTunnelHeartbeatAck, nil)
|
t.conn.WriteBytes(MsgP2P, MsgTunnelHeartbeatAck, nil)
|
||||||
gLog.Printf(LevelDEBUG, "%d read tunnel heartbeat", t.id)
|
gLog.Printf(LvDEBUG, "%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(LevelDEBUG, "%d read tunnel heartbeat ack", t.id)
|
gLog.Printf(LvDEBUG, "%d read tunnel heartbeat ack", t.id)
|
||||||
case MsgOverlayData:
|
case MsgOverlayData:
|
||||||
if len(body) < overlayHeaderSize {
|
if len(body) < overlayHeaderSize {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
overlayID := binary.LittleEndian.Uint64(body[:8])
|
overlayID := binary.LittleEndian.Uint64(body[:8])
|
||||||
gLog.Printf(LevelDEBUG, "%d tunnel read overlay data %d", t.id, overlayID)
|
gLog.Printf(LvDEBUG, "%d tunnel read overlay data %d", t.id, overlayID)
|
||||||
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(LevelDEBUG, "%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.(*overlayTCP)
|
overlayConn, ok := s.(*overlayConn)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -288,10 +434,10 @@ func (t *P2PTunnel) readLoop() {
|
|||||||
}
|
}
|
||||||
_, err = overlayConn.Write(payload)
|
_, err = overlayConn.Write(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, "overlay write error:", err)
|
gLog.Println(LvERROR, "overlay write error:", err)
|
||||||
}
|
}
|
||||||
case MsgRelayData:
|
case MsgRelayData:
|
||||||
gLog.Printf(LevelDEBUG, "got relay data datalen=%d", head.DataLen)
|
gLog.Printf(LvDEBUG, "got relay data datalen=%d", head.DataLen)
|
||||||
if len(body) < 8 {
|
if len(body) < 8 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -301,10 +447,10 @@ func (t *P2PTunnel) readLoop() {
|
|||||||
req := RelayHeartbeat{}
|
req := RelayHeartbeat{}
|
||||||
err := json.Unmarshal(body, &req)
|
err := json.Unmarshal(body, &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Printf(LevelERROR, "wrong RelayHeartbeat:%s", err)
|
gLog.Printf(LvERROR, "wrong RelayHeartbeat:%s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
gLog.Printf(LevelDEBUG, "got MsgRelayHeartbeat from %d:%d", req.RelayTunnelID, req.AppID)
|
gLog.Printf(LvDEBUG, "got MsgRelayHeartbeat from %d:%d", req.RelayTunnelID, req.AppID)
|
||||||
relayHead := new(bytes.Buffer)
|
relayHead := new(bytes.Buffer)
|
||||||
binary.Write(relayHead, binary.LittleEndian, req.RelayTunnelID)
|
binary.Write(relayHead, binary.LittleEndian, req.RelayTunnelID)
|
||||||
msg, _ := newMessage(MsgP2P, MsgRelayHeartbeatAck, &req)
|
msg, _ := newMessage(MsgP2P, MsgRelayHeartbeatAck, &req)
|
||||||
@@ -314,105 +460,148 @@ func (t *P2PTunnel) readLoop() {
|
|||||||
req := RelayHeartbeat{}
|
req := RelayHeartbeat{}
|
||||||
err := json.Unmarshal(body, &req)
|
err := json.Unmarshal(body, &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Printf(LevelERROR, "wrong RelayHeartbeat:%s", err)
|
gLog.Printf(LvERROR, "wrong RelayHeartbeat:%s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
gLog.Printf(LevelDEBUG, "got MsgRelayHeartbeatAck to %d", req.AppID)
|
gLog.Printf(LvDEBUG, "got MsgRelayHeartbeatAck to %d", req.AppID)
|
||||||
t.pn.updateAppHeartbeat(req.AppID)
|
t.pn.updateAppHeartbeat(req.AppID)
|
||||||
case MsgOverlayConnectReq:
|
case MsgOverlayConnectReq:
|
||||||
req := OverlayConnectReq{}
|
req := OverlayConnectReq{}
|
||||||
err := json.Unmarshal(body, &req)
|
err := json.Unmarshal(body, &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Printf(LevelERROR, "wrong MsgOverlayConnectReq:%s", err)
|
gLog.Printf(LvERROR, "wrong MsgOverlayConnectReq:%s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// app connect only accept user/password, 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.User != t.pn.config.User || req.Password != t.pn.config.Password {
|
if req.Token != t.pn.config.Token {
|
||||||
gLog.Println(LevelERROR, "Access Denied:", req.User)
|
gLog.Println(LvERROR, "Access Denied:", req.Token)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
overlayID := req.ID
|
overlayID := req.ID
|
||||||
gLog.Printf(LevelINFO, "App:%d overlayID:%d connect %+v", req.AppID, overlayID, req)
|
gLog.Printf(LvDEBUG, "App:%d overlayID:%d connect %+v", req.AppID, overlayID, req)
|
||||||
if req.Protocol == "tcp" {
|
oConn := overlayConn{
|
||||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", req.DstIP, req.DstPort), time.Second*5)
|
tunnel: t,
|
||||||
if err != nil {
|
id: overlayID,
|
||||||
gLog.Println(LevelERROR, err)
|
isClient: false,
|
||||||
continue
|
rtid: req.RelayTunnelID,
|
||||||
}
|
appID: req.AppID,
|
||||||
otcp := overlayTCP{
|
appKey: GetKey(req.AppID),
|
||||||
tunnel: t,
|
|
||||||
conn: conn,
|
|
||||||
id: overlayID,
|
|
||||||
isClient: false,
|
|
||||||
rtid: req.RelayTunnelID,
|
|
||||||
appID: req.AppID,
|
|
||||||
appKey: GetKey(req.AppID),
|
|
||||||
}
|
|
||||||
// calc key bytes for encrypt
|
|
||||||
if otcp.appKey != 0 {
|
|
||||||
encryptKey := make([]byte, 16)
|
|
||||||
binary.LittleEndian.PutUint64(encryptKey, otcp.appKey)
|
|
||||||
binary.LittleEndian.PutUint64(encryptKey[8:], otcp.appKey)
|
|
||||||
otcp.appKeyBytes = encryptKey
|
|
||||||
}
|
|
||||||
|
|
||||||
t.overlayConns.Store(otcp.id, &otcp)
|
|
||||||
go otcp.run()
|
|
||||||
}
|
}
|
||||||
|
if req.Protocol == "udp" {
|
||||||
|
oConn.connUDP, err = net.DialUDP("udp", nil, &net.UDPAddr{IP: net.ParseIP(req.DstIP), Port: req.DstPort})
|
||||||
|
} else {
|
||||||
|
oConn.connTCP, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", req.DstIP, req.DstPort), time.Second*5)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
gLog.Println(LvERROR, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// calc key bytes for encrypt
|
||||||
|
if oConn.appKey != 0 {
|
||||||
|
encryptKey := make([]byte, 16)
|
||||||
|
binary.LittleEndian.PutUint64(encryptKey, oConn.appKey)
|
||||||
|
binary.LittleEndian.PutUint64(encryptKey[8:], oConn.appKey)
|
||||||
|
oConn.appKeyBytes = encryptKey
|
||||||
|
}
|
||||||
|
|
||||||
|
t.overlayConns.Store(oConn.id, &oConn)
|
||||||
|
go oConn.run()
|
||||||
case MsgOverlayDisconnectReq:
|
case MsgOverlayDisconnectReq:
|
||||||
req := OverlayDisconnectReq{}
|
req := OverlayDisconnectReq{}
|
||||||
err := json.Unmarshal(body, &req)
|
err := json.Unmarshal(body, &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Printf(LevelERROR, "wrong OverlayDisconnectRequest:%s", err)
|
gLog.Printf(LvERROR, "wrong OverlayDisconnectRequest:%s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
overlayID := req.ID
|
overlayID := req.ID
|
||||||
gLog.Printf(LevelINFO, "%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 {
|
||||||
otcp := i.(*overlayTCP)
|
oConn := i.(*overlayConn)
|
||||||
otcp.running = false
|
oConn.running = false
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.setRun(false)
|
t.setRun(false)
|
||||||
t.conn.Close()
|
t.conn.Close()
|
||||||
gLog.Printf(LevelINFO, "%d tunnel readloop end", t.id)
|
gLog.Printf(LvDEBUG, "%d tunnel readloop end", t.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *P2PTunnel) writeLoop() {
|
func (t *P2PTunnel) heartbeatLoop() {
|
||||||
tc := time.NewTicker(TunnelHeartbeatTime)
|
tc := time.NewTicker(TunnelHeartbeatTime)
|
||||||
defer tc.Stop()
|
defer tc.Stop()
|
||||||
defer gLog.Printf(LevelINFO, "%d tunnel writeloop end", t.id)
|
gLog.Printf(LvDEBUG, "%d tunnel heartbeatLoop start", t.id)
|
||||||
|
defer gLog.Printf(LvDEBUG, "%d tunnel heartbeatLoop end", t.id)
|
||||||
for t.isRuning() {
|
for t.isRuning() {
|
||||||
select {
|
select {
|
||||||
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(LevelERROR, "%d write tunnel heartbeat error %s", t.id, err)
|
gLog.Printf(LvERROR, "%d write tunnel heartbeat error %s", t.id, err)
|
||||||
t.setRun(false)
|
t.setRun(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
gLog.Printf(LevelDEBUG, "%d write tunnel heartbeat ok", t.id)
|
gLog.Printf(LvDEBUG, "%d write tunnel heartbeat ok", t.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *P2PTunnel) listen() error {
|
func (t *P2PTunnel) listen() error {
|
||||||
gLog.Printf(LevelINFO, "p2ptunnel wait for connecting")
|
// notify client to connect
|
||||||
t.isServer = true
|
rsp := PushConnectRsp{
|
||||||
return t.handshake()
|
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.IPv6
|
||||||
|
}
|
||||||
|
|
||||||
|
t.pn.push(t.config.PeerNode, MsgPushConnectRsp, rsp)
|
||||||
|
gLog.Printf(LvDEBUG, "p2ptunnel wait for connecting")
|
||||||
|
t.tunnelServer = true
|
||||||
|
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 {
|
||||||
otcp := i.(*overlayTCP)
|
oConn := i.(*overlayConn)
|
||||||
if otcp.appID == appID {
|
if oConn.appID == appID {
|
||||||
otcp.conn.Close()
|
if oConn.connTCP != nil {
|
||||||
|
oConn.connTCP.Close()
|
||||||
|
oConn.connTCP = nil
|
||||||
|
}
|
||||||
|
if oConn.connUDP != nil {
|
||||||
|
oConn.connUDP.Close()
|
||||||
|
oConn.connUDP = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *P2PTunnel) isUnderlayServer() bool {
|
||||||
|
if (t.pn.config.hasIPv4 == 1 || t.pn.config.hasUPNPorNATPMP == 1) && (t.config.hasIPv4 != 1 || t.config.hasUPNPorNATPMP != 1) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (t.pn.config.hasIPv4 != 1 || t.pn.config.hasUPNPorNATPMP != 1) && (t.config.hasIPv4 == 1 || t.config.hasUPNPorNATPMP == 1) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// NAT or both has public IP
|
||||||
|
return t.tunnelServer
|
||||||
|
}
|
||||||
|
|||||||
147
protocol.go
@@ -10,8 +10,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const OpenP2PVersion = "0.97.1"
|
const OpenP2PVersion = "2.0.1"
|
||||||
const ProducnName string = "openp2p"
|
const ProducnName string = "openp2p"
|
||||||
|
const LeastSupportTCPVersion = "1.5.0"
|
||||||
|
|
||||||
type openP2PHeader struct {
|
type openP2PHeader struct {
|
||||||
DataLen uint32
|
DataLen uint32
|
||||||
@@ -69,6 +70,7 @@ const (
|
|||||||
MsgReport = 6
|
MsgReport = 6
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: seperate node push and web push.
|
||||||
const (
|
const (
|
||||||
MsgPushRsp = 0
|
MsgPushRsp = 0
|
||||||
MsgPushConnectReq = 1
|
MsgPushConnectReq = 1
|
||||||
@@ -78,7 +80,12 @@ const (
|
|||||||
MsgPushAddRelayTunnelRsp = 5
|
MsgPushAddRelayTunnelRsp = 5
|
||||||
MsgPushUpdate = 6
|
MsgPushUpdate = 6
|
||||||
MsgPushReportApps = 7
|
MsgPushReportApps = 7
|
||||||
MsgPushQuicConnect = 8
|
MsgPushUnderlayConnect = 8
|
||||||
|
MsgPushEditApp = 9
|
||||||
|
MsgPushSwitchApp = 10
|
||||||
|
MsgPushRestart = 11
|
||||||
|
MsgPushEditNode = 12
|
||||||
|
MsgPushAPPKey = 13
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgP2P sub type message
|
// MsgP2P sub type message
|
||||||
@@ -109,10 +116,11 @@ const (
|
|||||||
MsgReportBasic = iota
|
MsgReportBasic = iota
|
||||||
MsgReportQuery
|
MsgReportQuery
|
||||||
MsgReportConnect
|
MsgReportConnect
|
||||||
|
MsgReportApps
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ReadBuffLen = 1024
|
ReadBuffLen = 4096 // for UDP maybe not enough
|
||||||
NetworkHeartbeatTime = time.Second * 30 // TODO: server no response hb, save flow
|
NetworkHeartbeatTime = time.Second * 30 // TODO: server no response hb, save flow
|
||||||
TunnelHeartbeatTime = time.Second * 15
|
TunnelHeartbeatTime = time.Second * 15
|
||||||
TunnelIdleTimeout = time.Minute
|
TunnelIdleTimeout = time.Minute
|
||||||
@@ -126,8 +134,10 @@ const (
|
|||||||
AESKeySize = 16
|
AESKeySize = 16
|
||||||
MaxRetry = 10
|
MaxRetry = 10
|
||||||
RetryInterval = time.Second * 30
|
RetryInterval = time.Second * 30
|
||||||
PublicIPEchoTimeout = time.Second * 3
|
PublicIPEchoTimeout = time.Second * 1
|
||||||
NatTestTimeout = time.Second * 10
|
NatTestTimeout = time.Second * 10
|
||||||
|
ClientAPITimeout = time.Second * 10
|
||||||
|
MaxDirectTry = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
// NATNone has public ip
|
// NATNone has public ip
|
||||||
@@ -135,6 +145,21 @@ const (
|
|||||||
NATNone = 0
|
NATNone = 0
|
||||||
NATCone = 1
|
NATCone = 1
|
||||||
NATSymmetric = 2
|
NATSymmetric = 2
|
||||||
|
NATUnknown = 314
|
||||||
|
)
|
||||||
|
|
||||||
|
// underlay protocol
|
||||||
|
const (
|
||||||
|
UderlayAuto = "auto"
|
||||||
|
UderlayQUIC = "quic"
|
||||||
|
UderlayTCP = "tcp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// linkmode
|
||||||
|
const (
|
||||||
|
LinkModeUDPPunch = "udppunch"
|
||||||
|
LinkModeIPv4 = "ipv4"
|
||||||
|
LinkModeIPv6 = "ipv6"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newMessage(mainType uint16, subType uint16, packet interface{}) ([]byte, error) {
|
func newMessage(mainType uint16, subType uint16, packet interface{}) ([]byte, error) {
|
||||||
@@ -162,24 +187,32 @@ func nodeNameToID(name string) uint64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PushConnectReq struct {
|
type PushConnectReq struct {
|
||||||
From string `json:"from,omitempty"`
|
From string `json:"from,omitempty"`
|
||||||
User string `json:"user,omitempty"`
|
FromToken uint64 `json:"fromToken,omitempty"` // deprecated
|
||||||
Password string `json:"password,omitempty"`
|
Version string `json:"version,omitempty"`
|
||||||
Token uint64 `json:"token,omitempty"`
|
Token uint64 `json:"token,omitempty"` // if public totp token
|
||||||
ConeNatPort int `json:"coneNatPort,omitempty"`
|
ConeNatPort int `json:"coneNatPort,omitempty"` // if isPublic, is public port
|
||||||
NatType int `json:"natType,omitempty"`
|
NatType int `json:"natType,omitempty"`
|
||||||
FromIP string `json:"fromIP,omitempty"`
|
HasIPv4 int `json:"hasIPv4,omitempty"`
|
||||||
ID uint64 `json:"id,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
|
||||||
}
|
}
|
||||||
type PushConnectRsp struct {
|
type PushConnectRsp struct {
|
||||||
Error int `json:"error,omitempty"`
|
Error int `json:"error,omitempty"`
|
||||||
From string `json:"from,omitempty"`
|
From string `json:"from,omitempty"`
|
||||||
To string `json:"to,omitempty"`
|
To string `json:"to,omitempty"`
|
||||||
Detail string `json:"detail,omitempty"`
|
Detail string `json:"detail,omitempty"`
|
||||||
NatType int `json:"natType,omitempty"`
|
NatType int `json:"natType,omitempty"`
|
||||||
ConeNatPort int `json:"coneNatPort,omitempty"`
|
HasIPv4 int `json:"hasIPv4,omitempty"`
|
||||||
FromIP string `json:"fromIP,omitempty"`
|
IPv6 string `json:"IPv6,omitempty"` // if public relay node, ipv6 not set
|
||||||
ID uint64 `json:"id,omitempty"`
|
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"`
|
||||||
|
ID uint64 `json:"id,omitempty"`
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
}
|
}
|
||||||
type PushRsp struct {
|
type PushRsp struct {
|
||||||
Error int `json:"error,omitempty"`
|
Error int `json:"error,omitempty"`
|
||||||
@@ -189,6 +222,8 @@ type PushRsp struct {
|
|||||||
type LoginRsp struct {
|
type LoginRsp struct {
|
||||||
Error int `json:"error,omitempty"`
|
Error int `json:"error,omitempty"`
|
||||||
Detail string `json:"detail,omitempty"`
|
Detail string `json:"detail,omitempty"`
|
||||||
|
User string `json:"user,omitempty"`
|
||||||
|
Token uint64 `json:"token,omitempty"`
|
||||||
Ts int64 `json:"ts,omitempty"`
|
Ts int64 `json:"ts,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,8 +244,7 @@ type P2PHandshakeReq struct {
|
|||||||
|
|
||||||
type OverlayConnectReq struct {
|
type OverlayConnectReq struct {
|
||||||
ID uint64 `json:"id,omitempty"`
|
ID uint64 `json:"id,omitempty"`
|
||||||
User string `json:"user,omitempty"`
|
Token uint64 `json:"token,omitempty"` // not totp token
|
||||||
Password string `json:"password,omitempty"`
|
|
||||||
DstIP string `json:"dstIP,omitempty"`
|
DstIP string `json:"dstIP,omitempty"`
|
||||||
DstPort int `json:"dstPort,omitempty"`
|
DstPort int `json:"dstPort,omitempty"`
|
||||||
Protocol string `json:"protocol,omitempty"`
|
Protocol string `json:"protocol,omitempty"`
|
||||||
@@ -229,6 +263,7 @@ type RelayNodeReq struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RelayNodeRsp struct {
|
type RelayNodeRsp struct {
|
||||||
|
Mode string `json:"mode,omitempty"` // private,public
|
||||||
RelayName string `json:"relayName,omitempty"`
|
RelayName string `json:"relayName,omitempty"`
|
||||||
RelayToken uint64 `json:"relayToken,omitempty"`
|
RelayToken uint64 `json:"relayToken,omitempty"`
|
||||||
}
|
}
|
||||||
@@ -237,8 +272,13 @@ type AddRelayTunnelReq struct {
|
|||||||
From string `json:"from,omitempty"`
|
From string `json:"from,omitempty"`
|
||||||
RelayName string `json:"relayName,omitempty"`
|
RelayName string `json:"relayName,omitempty"`
|
||||||
RelayToken uint64 `json:"relayToken,omitempty"`
|
RelayToken uint64 `json:"relayToken,omitempty"`
|
||||||
AppID uint64 `json:"appID,omitempty"`
|
AppID uint64 `json:"appID,omitempty"` // deprecated
|
||||||
AppKey uint64 `json:"appKey,omitempty"`
|
AppKey uint64 `json:"appKey,omitempty"` // deprecated
|
||||||
|
}
|
||||||
|
|
||||||
|
type APPKeySync struct {
|
||||||
|
AppID uint64 `json:"appID,omitempty"`
|
||||||
|
AppKey uint64 `json:"appKey,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RelayHeartbeat struct {
|
type RelayHeartbeat struct {
|
||||||
@@ -247,12 +287,14 @@ type RelayHeartbeat struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ReportBasic struct {
|
type ReportBasic struct {
|
||||||
OS string `json:"os,omitempty"`
|
OS string `json:"os,omitempty"`
|
||||||
Mac string `json:"mac,omitempty"`
|
Mac string `json:"mac,omitempty"`
|
||||||
LanIP string `json:"lanIP,omitempty"`
|
LanIP string `json:"lanIP,omitempty"`
|
||||||
IPv6 string `json:"IPv6,omitempty"`
|
HasIPv4 int `json:"hasIPv4,omitempty"`
|
||||||
Version string `json:"version,omitempty"`
|
IPv6 string `json:"IPv6,omitempty"`
|
||||||
NetInfo NetInfo `json:"netInfo,omitempty"`
|
HasUPNPorNATPMP int `json:"hasUPNPorNATPMP,omitempty"`
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
NetInfo NetInfo `json:"netInfo,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReportConnect struct {
|
type ReportConnect struct {
|
||||||
@@ -262,7 +304,7 @@ type ReportConnect struct {
|
|||||||
NatType int `json:"natType,omitempty"`
|
NatType int `json:"natType,omitempty"`
|
||||||
PeerNode string `json:"peerNode,omitempty"`
|
PeerNode string `json:"peerNode,omitempty"`
|
||||||
DstPort int `json:"dstPort,omitempty"`
|
DstPort int `json:"dstPort,omitempty"`
|
||||||
DstHost string `json:"dsdtHost,omitempty"`
|
DstHost string `json:"dstHost,omitempty"`
|
||||||
PeerUser string `json:"peerUser,omitempty"`
|
PeerUser string `json:"peerUser,omitempty"`
|
||||||
PeerNatType int `json:"peerNatType,omitempty"`
|
PeerNatType int `json:"peerNatType,omitempty"`
|
||||||
PeerIP string `json:"peerIP,omitempty"`
|
PeerIP string `json:"peerIP,omitempty"`
|
||||||
@@ -271,6 +313,35 @@ type ReportConnect struct {
|
|||||||
Version string `json:"version,omitempty"`
|
Version string `json:"version,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AppInfo struct {
|
||||||
|
AppName string `json:"appName,omitempty"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
Protocol string `json:"protocol,omitempty"`
|
||||||
|
SrcPort int `json:"srcPort,omitempty"`
|
||||||
|
Protocol0 string `json:"protocol0,omitempty"`
|
||||||
|
SrcPort0 int `json:"srcPort0,omitempty"`
|
||||||
|
NatType int `json:"natType,omitempty"`
|
||||||
|
PeerNode string `json:"peerNode,omitempty"`
|
||||||
|
DstPort int `json:"dstPort,omitempty"`
|
||||||
|
DstHost string `json:"dstHost,omitempty"`
|
||||||
|
PeerUser string `json:"peerUser,omitempty"`
|
||||||
|
PeerNatType int `json:"peerNatType,omitempty"`
|
||||||
|
PeerIP string `json:"peerIP,omitempty"`
|
||||||
|
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"`
|
||||||
|
IsActive int `json:"isActive,omitempty"`
|
||||||
|
Enabled int `json:"enabled,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReportApps struct {
|
||||||
|
Apps []AppInfo
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateInfo struct {
|
type UpdateInfo struct {
|
||||||
Error int `json:"error,omitempty"`
|
Error int `json:"error,omitempty"`
|
||||||
ErrorDetail string `json:"errorDetail,omitempty"`
|
ErrorDetail string `json:"errorDetail,omitempty"`
|
||||||
@@ -295,3 +366,17 @@ type NetInfo struct {
|
|||||||
ASNOrg string `json:"asn_org,omitempty"`
|
ASNOrg string `json:"asn_org,omitempty"`
|
||||||
Hostname string `json:"hostname,omitempty"`
|
Hostname string `json:"hostname,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ProfileInfo struct {
|
||||||
|
User string `json:"user,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
Email string `json:"email,omitempty"`
|
||||||
|
Phone string `json:"phone,omitempty"`
|
||||||
|
Token string `json:"token,omitempty"`
|
||||||
|
Addtime string `json:"addtime,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type EditNode struct {
|
||||||
|
NewName string `json:"newName,omitempty"`
|
||||||
|
Bandwidth int `json:"bandwidth,omitempty"`
|
||||||
|
}
|
||||||
|
|||||||
2
quic.go
@@ -99,7 +99,7 @@ func (conn *quicConn) Accept() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func listenQuic(addr string, idleTimeout time.Duration) (*quicConn, error) {
|
func listenQuic(addr string, idleTimeout time.Duration) (*quicConn, error) {
|
||||||
gLog.Println(LevelINFO, "quic listen on ", addr)
|
gLog.Println(LevelDEBUG, "quic listen on ", addr)
|
||||||
listener, err := quic.ListenAddr(addr, generateTLSConfig(),
|
listener, err := quic.ListenAddr(addr, generateTLSConfig(),
|
||||||
&quic.Config{Versions: quicVersion, MaxIdleTimeout: idleTimeout, DisablePathMTUDiscovery: true})
|
&quic.Config{Versions: quicVersion, MaxIdleTimeout: idleTimeout, DisablePathMTUDiscovery: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
13
totp.go
@@ -8,9 +8,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const TOTPStep = 30 // 30s
|
const TOTPStep = 30 // 30s
|
||||||
func GenTOTP(user string, password string, ts int64) uint64 {
|
func GenTOTP(token uint64, ts int64) uint64 {
|
||||||
step := ts / TOTPStep
|
step := ts / TOTPStep
|
||||||
mac := hmac.New(sha256.New, []byte(user+password))
|
tbuff := make([]byte, 8)
|
||||||
|
binary.LittleEndian.PutUint64(tbuff, token)
|
||||||
|
mac := hmac.New(sha256.New, tbuff)
|
||||||
b := make([]byte, 8)
|
b := make([]byte, 8)
|
||||||
binary.LittleEndian.PutUint64(b, uint64(step))
|
binary.LittleEndian.PutUint64(b, uint64(step))
|
||||||
mac.Write(b)
|
mac.Write(b)
|
||||||
@@ -19,11 +21,14 @@ func GenTOTP(user string, password string, ts int64) uint64 {
|
|||||||
return num
|
return num
|
||||||
}
|
}
|
||||||
|
|
||||||
func VerifyTOTP(code uint64, user string, password string, ts int64) bool {
|
func VerifyTOTP(code uint64, token uint64, ts int64) bool {
|
||||||
if code == 0 {
|
if code == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if code == GenTOTP(user, password, ts) || code == GenTOTP(user, password, ts-TOTPStep) || code == GenTOTP(user, password, ts+TOTPStep) {
|
if code == token {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if code == GenTOTP(token, ts) || code == GenTOTP(token, ts-TOTPStep) || code == GenTOTP(token, ts+TOTPStep) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|||||||
14
totp_test.go
@@ -9,24 +9,24 @@ import (
|
|||||||
func TestTOTP(t *testing.T) {
|
func TestTOTP(t *testing.T) {
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
ts := time.Now().Unix()
|
ts := time.Now().Unix()
|
||||||
code := GenTOTP("testuser1", "testpassword1", ts)
|
code := GenTOTP(13666999958022769123, ts)
|
||||||
t.Log(code)
|
t.Log(code)
|
||||||
if !VerifyTOTP(code, "testuser1", "testpassword1", ts) {
|
if !VerifyTOTP(code, 13666999958022769123, ts) {
|
||||||
t.Error("TOTP error")
|
t.Error("TOTP error")
|
||||||
}
|
}
|
||||||
if !VerifyTOTP(code, "testuser1", "testpassword1", ts-10) {
|
if !VerifyTOTP(code, 13666999958022769123, ts-10) {
|
||||||
t.Error("TOTP error")
|
t.Error("TOTP error")
|
||||||
}
|
}
|
||||||
if !VerifyTOTP(code, "testuser1", "testpassword1", ts+10) {
|
if !VerifyTOTP(code, 13666999958022769123, ts+10) {
|
||||||
t.Error("TOTP error")
|
t.Error("TOTP error")
|
||||||
}
|
}
|
||||||
if VerifyTOTP(code, "testuser1", "testpassword1", ts+60) {
|
if VerifyTOTP(code, 13666999958022769123, ts+60) {
|
||||||
t.Error("TOTP error")
|
t.Error("TOTP error")
|
||||||
}
|
}
|
||||||
if VerifyTOTP(code, "testuser2", "testpassword1", ts+1) {
|
if VerifyTOTP(code, 13666999958022769124, ts+1) {
|
||||||
t.Error("TOTP error")
|
t.Error("TOTP error")
|
||||||
}
|
}
|
||||||
if VerifyTOTP(code, "testuser1", "testpassword2", ts+1) {
|
if VerifyTOTP(code, 13666999958022769125, ts+1) {
|
||||||
t.Error("TOTP error")
|
t.Error("TOTP error")
|
||||||
}
|
}
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
|
|||||||
4
udp.go
@@ -23,7 +23,7 @@ func UDPRead(conn *net.UDPConn, timeout int) (ra net.Addr, head *openP2PHeader,
|
|||||||
deadline := time.Now().Add(time.Millisecond * time.Duration(timeout))
|
deadline := time.Now().Add(time.Millisecond * time.Duration(timeout))
|
||||||
err = conn.SetReadDeadline(deadline)
|
err = conn.SetReadDeadline(deadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, "SetReadDeadline error")
|
gLog.Println(LvERROR, "SetReadDeadline error")
|
||||||
return nil, nil, nil, 0, err
|
return nil, nil, nil, 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ func UDPRead(conn *net.UDPConn, timeout int) (ra net.Addr, head *openP2PHeader,
|
|||||||
head = &openP2PHeader{}
|
head = &openP2PHeader{}
|
||||||
err = binary.Read(bytes.NewReader(result[:openP2PHeaderSize]), binary.LittleEndian, head)
|
err = binary.Read(bytes.NewReader(result[:openP2PHeaderSize]), binary.LittleEndian, head)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, "parse p2pheader error:", err)
|
gLog.Println(LvERROR, "parse p2pheader error:", err)
|
||||||
return nil, nil, nil, 0, err
|
return nil, nil, nil, 0, err
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|||||||
16
underlay.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type underlay interface {
|
||||||
|
ReadBuffer() (*openP2PHeader, []byte, error)
|
||||||
|
WriteBytes(uint16, uint16, []byte) error
|
||||||
|
WriteBuffer([]byte) error
|
||||||
|
WriteMessage(uint16, uint16, interface{}) error
|
||||||
|
Close() error
|
||||||
|
SetReadDeadline(t time.Time) error
|
||||||
|
SetWriteDeadline(t time.Time) error
|
||||||
|
Protocol() string
|
||||||
|
}
|
||||||
155
underlay_quic.go
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
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 underlayQUIC struct {
|
||||||
|
listener quic.Listener
|
||||||
|
writeMtx *sync.Mutex
|
||||||
|
quic.Stream
|
||||||
|
quic.Connection
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *underlayQUIC) Protocol() string {
|
||||||
|
return "quic"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *underlayQUIC) ReadBuffer() (*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 *underlayQUIC) 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 *underlayQUIC) WriteBuffer(data []byte) error {
|
||||||
|
conn.writeMtx.Lock()
|
||||||
|
_, err := conn.Write(data)
|
||||||
|
conn.writeMtx.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *underlayQUIC) 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 *underlayQUIC) Close() error {
|
||||||
|
conn.Stream.CancelRead(1)
|
||||||
|
conn.Connection.CloseWithError(0, "")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (conn *underlayQUIC) CloseListener() {
|
||||||
|
if conn.listener != nil {
|
||||||
|
conn.listener.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *underlayQUIC) 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.Connection = sess
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func listenQuic(addr string, idleTimeout time.Duration) (*underlayQUIC, error) {
|
||||||
|
gLog.Println(LvDEBUG, "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 &underlayQUIC{listener: listener, writeMtx: &sync.Mutex{}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dialQuic(conn *net.UDPConn, remoteAddr *net.UDPAddr, idleTimeout time.Duration) (*underlayQUIC, error) {
|
||||||
|
tlsConf := &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
NextProtos: []string{"openp2pv1"},
|
||||||
|
}
|
||||||
|
Connection, err := quic.DialContext(context.Background(), conn, remoteAddr, conn.LocalAddr().String(), tlsConf,
|
||||||
|
&quic.Config{Versions: quicVersion, MaxIdleTimeout: idleTimeout, DisablePathMTUDiscovery: true})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("quic.DialContext error:%s", err)
|
||||||
|
}
|
||||||
|
stream, err := Connection.OpenStreamSync(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("OpenStreamSync error:%s", err)
|
||||||
|
}
|
||||||
|
qConn := &underlayQUIC{nil, &sync.Mutex{}, stream, Connection}
|
||||||
|
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"},
|
||||||
|
}
|
||||||
|
}
|
||||||
92
underlay_tcp.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type underlayTCP struct {
|
||||||
|
listener net.Listener
|
||||||
|
writeMtx *sync.Mutex
|
||||||
|
net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *underlayTCP) Protocol() string {
|
||||||
|
return "tcp"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *underlayTCP) ReadBuffer() (*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 *underlayTCP) 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 *underlayTCP) WriteBuffer(data []byte) error {
|
||||||
|
conn.writeMtx.Lock()
|
||||||
|
_, err := conn.Write(data)
|
||||||
|
conn.writeMtx.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *underlayTCP) 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 *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))
|
||||||
|
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()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Dial %s:%d error:%s", host, port, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &underlayTCP{writeMtx: &sync.Mutex{}, Conn: c}, nil
|
||||||
|
}
|
||||||
92
underlay_tcp6.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type underlayTCP6 struct {
|
||||||
|
listener net.Listener
|
||||||
|
writeMtx *sync.Mutex
|
||||||
|
net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *underlayTCP6) Protocol() string {
|
||||||
|
return "tcp6"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *underlayTCP6) ReadBuffer() (*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 *underlayTCP6) 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 *underlayTCP6) WriteBuffer(data []byte) error {
|
||||||
|
conn.writeMtx.Lock()
|
||||||
|
_, err := conn.Write(data)
|
||||||
|
conn.writeMtx.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *underlayTCP6) 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 *underlayTCP6) Close() error {
|
||||||
|
return conn.Conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func listenTCP6(port int, idleTimeout time.Duration) (*underlayTCP6, error) {
|
||||||
|
addr, _ := net.ResolveTCPAddr("tcp6", fmt.Sprintf("[::]:%d", port))
|
||||||
|
l, err := net.ListenTCP("tcp6", addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
l.SetDeadline(time.Now().Add(SymmetricHandshakeAckTimeout))
|
||||||
|
c, err := l.Accept()
|
||||||
|
defer l.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &underlayTCP6{writeMtx: &sync.Mutex{}, Conn: c}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dialTCP6(host string, port int) (*underlayTCP6, error) {
|
||||||
|
c, err := net.DialTimeout("tcp6", fmt.Sprintf("[%s]:%d", host, port), SymmetricHandshakeAckTimeout)
|
||||||
|
if err != nil {
|
||||||
|
gLog.Printf(LvERROR, "Dial %s:%d error:%s", host, port, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &underlayTCP6{writeMtx: &sync.Mutex{}, Conn: c}, nil
|
||||||
|
}
|
||||||
52
update.go
@@ -16,18 +16,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// type updateFileInfo struct {
|
|
||||||
// Name string `json:"name,omitempty"`
|
|
||||||
// RelativePath string `json:"relativePath,omitempty"`
|
|
||||||
// Length int64 `json:"length,omitempty"`
|
|
||||||
// URL string `json:"url,omitempty"`
|
|
||||||
// Hash string `json:"hash,omitempty"`
|
|
||||||
// }
|
|
||||||
|
|
||||||
func update() {
|
func update() {
|
||||||
gLog.Println(LevelINFO, "update start")
|
gLog.Println(LvINFO, "update start")
|
||||||
defer gLog.Println(LevelINFO, "update end")
|
defer gLog.Println(LvINFO, "update end")
|
||||||
// TODO: download from gitee. save flow
|
|
||||||
c := http.Client{
|
c := http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
@@ -36,46 +27,45 @@ func update() {
|
|||||||
}
|
}
|
||||||
goos := runtime.GOOS
|
goos := runtime.GOOS
|
||||||
goarch := runtime.GOARCH
|
goarch := runtime.GOARCH
|
||||||
rsp, err := c.Get(fmt.Sprintf("https://openp2p.cn:27182/api/v1/update?fromver=%s&os=%s&arch=%s", OpenP2PVersion, goos, goarch))
|
rsp, err := c.Get(fmt.Sprintf("https://api.openp2p.cn:27183/api/v1/update?fromver=%s&os=%s&arch=%s", OpenP2PVersion, goos, goarch))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, "update:query update list failed:", err)
|
gLog.Println(LvERROR, "update:query update list failed:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer rsp.Body.Close()
|
defer rsp.Body.Close()
|
||||||
if rsp.StatusCode != http.StatusOK {
|
if rsp.StatusCode != http.StatusOK {
|
||||||
gLog.Println(LevelERROR, "get update info error:", rsp.Status)
|
gLog.Println(LvERROR, "get update info error:", rsp.Status)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rspBuf, err := ioutil.ReadAll(rsp.Body)
|
rspBuf, err := ioutil.ReadAll(rsp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, "update:read update list failed:", err)
|
gLog.Println(LvERROR, "update:read update list failed:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
updateInfo := UpdateInfo{}
|
updateInfo := UpdateInfo{}
|
||||||
err = json.Unmarshal(rspBuf, &updateInfo)
|
err = json.Unmarshal(rspBuf, &updateInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, rspBuf, " update info decode error:", err)
|
gLog.Println(LvERROR, rspBuf, " update info decode error:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if updateInfo.Error != 0 {
|
if updateInfo.Error != 0 {
|
||||||
gLog.Println(LevelERROR, "update error:", updateInfo.Error, updateInfo.ErrorDetail)
|
gLog.Println(LvERROR, "update error:", updateInfo.Error, updateInfo.ErrorDetail)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
os.MkdirAll("download", 0666)
|
|
||||||
err = updateFile(updateInfo.Url, "", "openp2p")
|
err = updateFile(updateInfo.Url, "", "openp2p")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Println(LevelERROR, "update: download failed:", err)
|
gLog.Println(LvERROR, "update: download failed:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo rollback on error
|
// todo rollback on error
|
||||||
func updateFile(url string, checksum string, dst string) error {
|
func updateFile(url string, checksum string, dst string) error {
|
||||||
gLog.Println(LevelINFO, "download ", url)
|
gLog.Println(LvINFO, "download ", url)
|
||||||
tmpFile := filepath.Dir(os.Args[0]) + "/openp2p.tmp"
|
tmpFile := filepath.Dir(os.Args[0]) + "/openp2p.tmp"
|
||||||
output, err := os.OpenFile(tmpFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0776)
|
output, err := os.OpenFile(tmpFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0776)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gLog.Printf(LevelERROR, "OpenFile %s error:%s", tmpFile, err)
|
gLog.Printf(LvERROR, "OpenFile %s error:%s", tmpFile, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
tr := &http.Transport{
|
tr := &http.Transport{
|
||||||
@@ -84,34 +74,35 @@ func updateFile(url string, checksum string, dst string) error {
|
|||||||
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(LevelERROR, "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(LevelERROR, "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(LevelINFO, "download ", url, " ok")
|
gLog.Println(LvINFO, "download ", url, " ok")
|
||||||
gLog.Printf(LevelINFO, "size: %d bytes", n)
|
gLog.Printf(LvINFO, "size: %d bytes", n)
|
||||||
|
|
||||||
err = os.Rename(os.Args[0], os.Args[0]+"0")
|
err = os.Rename(os.Args[0], os.Args[0]+"0")
|
||||||
if err != nil && os.IsExist(err) {
|
if err != nil && os.IsExist(err) {
|
||||||
gLog.Printf(LevelINFO, " rename %s error:%s", os.Args[0], err)
|
gLog.Printf(LvINFO, " rename %s error:%s", os.Args[0], err)
|
||||||
}
|
}
|
||||||
// extract
|
// extract
|
||||||
gLog.Println(LevelINFO, "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(LevelERROR, "extract error:%s. revert rename", err)
|
gLog.Printf(LvERROR, "extract error:%s. revert rename", err)
|
||||||
os.Rename(os.Args[0]+"0", os.Args[0])
|
os.Rename(os.Args[0]+"0", os.Args[0])
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
os.Remove(tmpFile)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,11 +124,6 @@ func unzip(dst, src string) (err error) {
|
|||||||
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 !strings.HasPrefix(filePath, filepath.Clean(dst)+string(os.PathSeparator)) {
|
|
||||||
// fmt.Println("invalid file path")
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
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)
|
||||||
|
|||||||
404
upnp.go
Normal file
@@ -0,0 +1,404 @@
|
|||||||
|
/*
|
||||||
|
Taken from taipei-torrent
|
||||||
|
|
||||||
|
Just enough UPnP to be able to forward ports
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
// BUG(jae): TODO: use syscalls to get actual ourIP. http://pastebin.com/9exZG4rh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type upnpNAT struct {
|
||||||
|
serviceURL string
|
||||||
|
ourIP string
|
||||||
|
urnDomain string
|
||||||
|
}
|
||||||
|
|
||||||
|
// protocol is either "udp" or "tcp"
|
||||||
|
type NAT interface {
|
||||||
|
GetExternalAddress() (addr net.IP, err error)
|
||||||
|
AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error)
|
||||||
|
DeletePortMapping(protocol string, externalPort, internalPort int) (err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Discover() (nat NAT, err error) {
|
||||||
|
ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
conn, err := net.ListenPacket("udp4", ":0")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
socket := conn.(*net.UDPConn)
|
||||||
|
defer socket.Close() // nolint: errcheck
|
||||||
|
|
||||||
|
if err := socket.SetDeadline(time.Now().Add(3 * time.Second)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
st := "InternetGatewayDevice:1"
|
||||||
|
|
||||||
|
buf := bytes.NewBufferString(
|
||||||
|
"M-SEARCH * HTTP/1.1\r\n" +
|
||||||
|
"HOST: 239.255.255.250:1900\r\n" +
|
||||||
|
"ST: ssdp:all\r\n" +
|
||||||
|
"MAN: \"ssdp:discover\"\r\n" +
|
||||||
|
"MX: 2\r\n\r\n")
|
||||||
|
message := buf.Bytes()
|
||||||
|
answerBytes := make([]byte, 1024)
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
_, err = socket.WriteToUDP(message, ssdp)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var n int
|
||||||
|
_, _, err = socket.ReadFromUDP(answerBytes)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
n, _, err = socket.ReadFromUDP(answerBytes)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
answer := string(answerBytes[0:n])
|
||||||
|
if !strings.Contains(answer, st) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// HTTP header field names are case-insensitive.
|
||||||
|
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
||||||
|
locString := "\r\nlocation:"
|
||||||
|
answer = strings.ToLower(answer)
|
||||||
|
locIndex := strings.Index(answer, locString)
|
||||||
|
if locIndex < 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
loc := answer[locIndex+len(locString):]
|
||||||
|
endIndex := strings.Index(loc, "\r\n")
|
||||||
|
if endIndex < 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
locURL := strings.TrimSpace(loc[0:endIndex])
|
||||||
|
var serviceURL, urnDomain string
|
||||||
|
serviceURL, urnDomain, err = getServiceURL(locURL)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var ourIP net.IP
|
||||||
|
ourIP, err = localIPv4()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP.String(), urnDomain: urnDomain}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = errors.New("UPnP port discovery failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type Envelope struct {
|
||||||
|
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
|
||||||
|
Soap *SoapBody
|
||||||
|
}
|
||||||
|
type SoapBody struct {
|
||||||
|
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
|
||||||
|
ExternalIP *ExternalIPAddressResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExternalIPAddressResponse struct {
|
||||||
|
XMLName xml.Name `xml:"GetExternalIPAddressResponse"`
|
||||||
|
IPAddress string `xml:"NewExternalIPAddress"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExternalIPAddress struct {
|
||||||
|
XMLName xml.Name `xml:"NewExternalIPAddress"`
|
||||||
|
IP string
|
||||||
|
}
|
||||||
|
|
||||||
|
type UPNPService struct {
|
||||||
|
ServiceType string `xml:"serviceType"`
|
||||||
|
ControlURL string `xml:"controlURL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceList struct {
|
||||||
|
Device []Device `xml:"device"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServiceList struct {
|
||||||
|
Service []UPNPService `xml:"service"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Device struct {
|
||||||
|
XMLName xml.Name `xml:"device"`
|
||||||
|
DeviceType string `xml:"deviceType"`
|
||||||
|
DeviceList DeviceList `xml:"deviceList"`
|
||||||
|
ServiceList ServiceList `xml:"serviceList"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Root struct {
|
||||||
|
Device Device
|
||||||
|
}
|
||||||
|
|
||||||
|
func getChildDevice(d *Device, deviceType string) *Device {
|
||||||
|
dl := d.DeviceList.Device
|
||||||
|
for i := 0; i < len(dl); i++ {
|
||||||
|
if strings.Contains(dl[i].DeviceType, deviceType) {
|
||||||
|
return &dl[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getChildService(d *Device, serviceType string) *UPNPService {
|
||||||
|
sl := d.ServiceList.Service
|
||||||
|
for i := 0; i < len(sl); i++ {
|
||||||
|
if strings.Contains(sl[i].ServiceType, serviceType) {
|
||||||
|
return &sl[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func localIPv4() (net.IP, error) {
|
||||||
|
tt, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getServiceURL(rootURL string) (url, urnDomain string, err error) {
|
||||||
|
r, err := http.Get(rootURL)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer r.Body.Close() // nolint: errcheck
|
||||||
|
|
||||||
|
if r.StatusCode >= 400 {
|
||||||
|
err = errors.New(fmt.Sprint(r.StatusCode))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var root Root
|
||||||
|
err = xml.NewDecoder(r.Body).Decode(&root)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a := &root.Device
|
||||||
|
if !strings.Contains(a.DeviceType, "InternetGatewayDevice:1") {
|
||||||
|
err = errors.New("No InternetGatewayDevice")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b := getChildDevice(a, "WANDevice:1")
|
||||||
|
if b == nil {
|
||||||
|
err = errors.New("No WANDevice")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c := getChildDevice(b, "WANConnectionDevice:1")
|
||||||
|
if c == nil {
|
||||||
|
err = errors.New("No WANConnectionDevice")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d := getChildService(c, "WANIPConnection:1")
|
||||||
|
if d == nil {
|
||||||
|
// Some routers don't follow the UPnP spec, and put WanIPConnection under WanDevice,
|
||||||
|
// instead of under WanConnectionDevice
|
||||||
|
d = getChildService(b, "WANIPConnection:1")
|
||||||
|
|
||||||
|
if d == nil {
|
||||||
|
d = getChildService(c, "WANPPPConnection:1")
|
||||||
|
if d == nil {
|
||||||
|
err = errors.New("No WANIPConnection or WANPPPConnection")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Extract the domain name, which isn't always 'schemas-upnp-org'
|
||||||
|
urnDomain = strings.Split(d.ServiceType, ":")[1]
|
||||||
|
url = combineURL(rootURL, d.ControlURL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func combineURL(rootURL, subURL string) string {
|
||||||
|
protocolEnd := "://"
|
||||||
|
protoEndIndex := strings.Index(rootURL, protocolEnd)
|
||||||
|
a := rootURL[protoEndIndex+len(protocolEnd):]
|
||||||
|
rootIndex := strings.Index(a, "/")
|
||||||
|
return rootURL[0:protoEndIndex+len(protocolEnd)+rootIndex] + subURL
|
||||||
|
}
|
||||||
|
|
||||||
|
func soapRequest(url, function, message, domain string) (r *http.Response, err error) {
|
||||||
|
fullMessage := "<?xml version=\"1.0\" ?>" +
|
||||||
|
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" +
|
||||||
|
"<s:Body>" + message + "</s:Body></s:Envelope>"
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", url, strings.NewReader(fullMessage))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "text/xml ; charset=\"utf-8\"")
|
||||||
|
req.Header.Set("User-Agent", "Darwin/10.0.0, UPnP/1.0, MiniUPnPc/1.3")
|
||||||
|
//req.Header.Set("Transfer-Encoding", "chunked")
|
||||||
|
req.Header.Set("SOAPAction", "\"urn:"+domain+":service:WANIPConnection:1#"+function+"\"")
|
||||||
|
req.Header.Set("Connection", "Close")
|
||||||
|
req.Header.Set("Cache-Control", "no-cache")
|
||||||
|
req.Header.Set("Pragma", "no-cache")
|
||||||
|
|
||||||
|
// log.Stderr("soapRequest ", req)
|
||||||
|
|
||||||
|
r, err = http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
/*if r.Body != nil {
|
||||||
|
defer r.Body.Close()
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if r.StatusCode >= 400 {
|
||||||
|
// log.Stderr(function, r.StatusCode)
|
||||||
|
err = errors.New("Error " + strconv.Itoa(r.StatusCode) + " for " + function)
|
||||||
|
r = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type statusInfo struct {
|
||||||
|
externalIpAddress string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {
|
||||||
|
|
||||||
|
message := "<u:GetExternalIPAddress xmlns:u=\"urn:" + n.urnDomain + ":service:WANIPConnection:1\">\r\n" +
|
||||||
|
"</u:GetExternalIPAddress>"
|
||||||
|
|
||||||
|
var response *http.Response
|
||||||
|
response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)
|
||||||
|
if response != nil {
|
||||||
|
defer response.Body.Close() // nolint: errcheck
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var envelope Envelope
|
||||||
|
data, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
reader := bytes.NewReader(data)
|
||||||
|
err = xml.NewDecoder(reader).Decode(&envelope)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
info = statusInfo{envelope.Soap.ExternalIP.IPAddress}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExternalAddress returns an external IP. If GetExternalIPAddress action
|
||||||
|
// fails or IP returned is invalid, GetExternalAddress returns an error.
|
||||||
|
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
|
||||||
|
info, err := n.getExternalIPAddress()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addr = net.ParseIP(info.externalIpAddress)
|
||||||
|
if addr == nil {
|
||||||
|
err = fmt.Errorf("Failed to parse IP: %v", info.externalIpAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
|
||||||
|
// A single concatenation would break ARM compilation.
|
||||||
|
message := "<u:AddPortMapping xmlns:u=\"urn:" + n.urnDomain + ":service:WANIPConnection:1\">\r\n" +
|
||||||
|
"<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort)
|
||||||
|
message += "</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>"
|
||||||
|
message += "<NewInternalPort>" + strconv.Itoa(internalPort) + "</NewInternalPort>" +
|
||||||
|
"<NewInternalClient>" + n.ourIP + "</NewInternalClient>" +
|
||||||
|
"<NewEnabled>1</NewEnabled><NewPortMappingDescription>"
|
||||||
|
message += description +
|
||||||
|
"</NewPortMappingDescription><NewLeaseDuration>" + strconv.Itoa(timeout) +
|
||||||
|
"</NewLeaseDuration></u:AddPortMapping>"
|
||||||
|
|
||||||
|
var response *http.Response
|
||||||
|
response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain)
|
||||||
|
if response != nil {
|
||||||
|
defer response.Body.Close() // nolint: errcheck
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check response to see if the port was forwarded
|
||||||
|
// log.Println(message, response)
|
||||||
|
// JAE:
|
||||||
|
// body, err := ioutil.ReadAll(response.Body)
|
||||||
|
// fmt.Println(string(body), err)
|
||||||
|
mappedExternalPort = externalPort
|
||||||
|
_ = response
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
|
||||||
|
|
||||||
|
message := "<u:DeletePortMapping xmlns:u=\"urn:" + n.urnDomain + ":service:WANIPConnection:1\">\r\n" +
|
||||||
|
"<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort) +
|
||||||
|
"</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>" +
|
||||||
|
"</u:DeletePortMapping>"
|
||||||
|
|
||||||
|
var response *http.Response
|
||||||
|
response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain)
|
||||||
|
if response != nil {
|
||||||
|
defer response.Body.Close() // nolint: errcheck
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check response to see if the port was deleted
|
||||||
|
// log.Println(message, response)
|
||||||
|
_ = response
|
||||||
|
return
|
||||||
|
}
|
||||||
32
util_darwin.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
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() {
|
||||||
|
}
|
||||||
72
util_linux.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
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() {
|
||||||
|
}
|
||||||
53
util_windows.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
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(LvERROR, "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()
|
||||||
|
}
|
||||||
|
}
|
||||||