├── .gitignore
├── LICENSE
├── Makefile
├── README-ZH.md
├── README.md
├── USAGE-ZH.md
├── USAGE.md
├── app
├── .gitignore
├── .idea
│ ├── .gitignore
│ ├── .name
│ ├── compiler.xml
│ ├── gradle.xml
│ ├── jarRepositories.xml
│ ├── misc.xml
│ └── vcs.xml
├── README.md
├── app
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── cn
│ │ │ └── openp2p
│ │ │ └── ExampleInstrumentedTest.kt
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── cn
│ │ │ └── openp2p
│ │ │ ├── BootReceiver.kt
│ │ │ ├── OpenP2PService.kt
│ │ │ ├── data
│ │ │ ├── LoginDataSource.kt
│ │ │ ├── LoginRepository.kt
│ │ │ ├── Result.kt
│ │ │ └── model
│ │ │ │ └── LoggedInUser.kt
│ │ │ ├── log.kt
│ │ │ └── ui
│ │ │ └── login
│ │ │ ├── LoggedInUserView.kt
│ │ │ ├── LoginActivity.kt
│ │ │ ├── LoginFormState.kt
│ │ │ ├── LoginResult.kt
│ │ │ ├── LoginViewModel.kt
│ │ │ └── LoginViewModelFactory.kt
│ │ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── ic_launcher_background.xml
│ │ └── icon.xml
│ │ ├── layout
│ │ └── activity_login.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── values-night
│ │ └── themes.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── themes.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── openp2p.jks
└── settings.gradle
├── cmd
└── openp2p.go
├── config.json
├── core
├── common.go
├── common_test.go
├── config.go
├── config_test.go
├── daemon.go
├── errorcode.go
├── handlepush.go
├── holepunch.go
├── install.go
├── iptables.go
├── iptree.go
├── iptree_test.go
├── log.go
├── nat.go
├── openp2p.go
├── optun.go
├── optun_android.go
├── optun_darwin.go
├── optun_linux.go
├── optun_other.go
├── optun_windows.go
├── overlay.go
├── p2papp.go
├── p2pappkeys.go
├── p2pnetwork.go
├── p2ptunnel.go
├── p2ptunnel_test.go
├── ping.go
├── protocol.go
├── sdwan.go
├── speedlimiter.go
├── speedlimiter_test.go
├── udp.go
├── underlay.go
├── underlay_kcp.go
├── underlay_quic.go
├── underlay_tcp.go
├── underlay_tcp6.go
├── update.go
├── upnp.go
├── util_darwin.go
├── util_freebsd.go
├── util_linux.go
├── util_windows.go
└── v4listener.go
├── doc
├── images
│ ├── afterconnect.png
│ ├── afterconnect_en.png
│ ├── afterconnect_linux.png
│ ├── appdetail.png
│ ├── architecture.png
│ ├── devices.png
│ ├── devices_en.png
│ ├── homeconnect.png
│ ├── homeconnect_windows.png
│ ├── install.png
│ ├── install_en.png
│ ├── mem.png
│ ├── mstscconnect.png
│ ├── mstscconnect_en.png
│ ├── newapp.png
│ ├── newapp_en.png
│ ├── newappedit.png
│ ├── newappedit_en.png
│ ├── officeexecute_linux.png
│ ├── officelisten.png
│ ├── officelisten_2_linux.png
│ ├── officelisten_linux.png
│ ├── p2p-debug.png
│ ├── p2pappok.png
│ ├── p2pappok_en.png
│ ├── prototype.png
│ ├── register.png
│ ├── register_en.png
│ ├── release-debug.png
│ ├── sshconnect.png
│ ├── stillrun.png
│ ├── stillrun_en.png
│ ├── vs2022-remote-debug-attach.png
│ ├── win10warn.png
│ ├── win10warn_en.png
│ └── winscpconnect.png
├── remote-debug-golang.md
└── remote-debug-vscpp.md
├── docker
├── Dockerfile
└── get-client.sh
├── example
├── dll
│ └── dll.cpp
└── echo
│ ├── echo-client.go
│ └── echo-server.go
├── go.mod
└── lib
└── openp2p.go
/.gitignore:
--------------------------------------------------------------------------------
1 | __debug_bin
2 | __debug_bin.exe
3 | # .vscode
4 | test/
5 | openp2p.exe*
6 | *.log*
7 | go.sum
8 | *.tar.gz
9 | *.zip
10 | *.exe
11 | config.json
12 | libs/
13 | */app/.idea/
14 | */app/release/
15 | openp2p.app.jks
16 | openp2p.aar
17 | openp2p-sources.jar
18 | wintun/
19 | wintun.dll
20 | .vscode/
21 | app/.idea/
22 | *_debug_bin*
23 | cmd/openp2p
24 | vendor/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 OpenP2P.cn
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | export GOPROXY=https://goproxy.io,direct
3 | go mod tidy
4 | go build cmd/openp2p.go
5 | .PHONY: build
6 |
7 | .DEFAULT_GOAL := build
8 |
--------------------------------------------------------------------------------
/README-ZH.md:
--------------------------------------------------------------------------------
1 | # [English](/README.md)|中文
2 | 网站: [openp2p.cn](https://openp2p.cn)
3 | ## OpenP2P是什么
4 | 它是一个开源、免费、轻量级的P2P共享网络。你的设备将组成一个私有P2P网络,里面的设备可以直接访问其它成员,或者通过其它成员转发数据间接访问。如果私有网络无法完成通信,将会到公有P2P网络寻找共享节点协助通信。
5 | 相比BT网络用来共享文件,OpenP2P网络用来共享带宽。
6 | 我们的目标是:充分利用带宽,利用共享节点转发数据,建设一个远程连接的通用基础设施。
7 |
8 | ## 为什么选择OpenP2P
9 | ### 1. 免费
10 | 完全免费,满足大部分用户的核心白票需求。不像其它类似的产品,OpenP2P不需要有公网IP的服务器,不需要花钱买服务。了解它原理即可理解为什么能做到免费。
11 | ### 2. 共享
12 | 你的设备会形成一个私有P2P网络,它们之间共享带宽,提供网络数据转发服务。
13 | 当你的私有P2P网络下没有可以提供转发服务的节点时,会尝试在公共P2P网络寻找转发节点。
14 | 默认会开启共享限速10mbps,只有你用户下提供了共享节点才能使用别人的共享节点。这非常公平,也是这个项目的初衷。
15 | 我们建议你在带宽足够的地方(比如办公室,家里的百兆光纤)加入共享网络。
16 | 如果你不想共享任何节点,或设置共享带宽,请查看[详细使用说明](/USAGE-ZH.md)
17 | ### 3. 安全
18 | 代码开源,P2P隧道使用TLS1.3+AES双重加密,共享节点临时授权使用TOTP一次性密码
19 |
20 | [查看详细](#安全性)
21 | ### 4. 轻量
22 | 文件大小2MB+,运行内存2MB+;它可以仅跑在应用层,或者配合wintun驱动使用组网功能
23 | ### 5. 跨平台
24 | 因为轻量,所以很容易支持各个平台。支持主流的操作系统:Windows,Linux,MacOS;和主流的cpu架构:386、amd64、arm、arm64、mipsle、mipsle64、mips、mips64、s390x、ppc64le
25 | ### 6. 高效
26 | P2P直连可以让你的设备跑满带宽。不论你的设备在任何网络环境,无论NAT1-4(Cone或Symmetric),UDP或TCP打洞,UPNP,IPv6都支持。依靠Quic协议优秀的拥塞算法,能在糟糕的网络环境获得高带宽低延时。
27 |
28 | ### 7. 二次开发
29 | 基于OpenP2P只需数行代码,就能让原来只能局域网通信的程序,变成任何内网都能通信
30 |
31 | ## 快速入门
32 | 仅需简单4步就能用起来。
33 | 下面是一个远程办公例子:在家里连入办公室Windows电脑。
34 | (另外一个快速入门视频 )
35 | ### 1.注册
36 | 前往 注册新用户,暂无需任何认证
37 |
38 | 
39 | ### 2.安装
40 | 分别在本地和远程电脑下载后双击运行,一键安装
41 |
42 | 
43 |
44 | Windows默认会阻止没有花钱买它家证书签名过的程序,选择“仍要运行”即可。
45 |
46 | 
47 |
48 | 
49 | ### 3.新建P2P应用
50 |
51 | 
52 |
53 | 
54 |
55 | 
56 |
57 | ### 4.使用P2P应用
58 | 在“MyHomePC”设备上能看到刚才创建的P2P应用,连接下图显示的“本地监听端口”即可。
59 |
60 | 
61 |
62 | 在家里Windows电脑,按Win+R输入mstsc打开远程桌面,输入127.0.0.1:23389 /admin
63 |
64 | 
65 |
66 | 
67 |
68 | ## 详细使用说明
69 | [这里](/USAGE-ZH.md)介绍如何手动运行
70 |
71 | ## 典型应用场景
72 | 特别适合大流量的内网访问
73 | >* 远程办公: Windows MSTSC、VNC等远程桌面,SSH,内网各种ERP系统
74 | >* 远程访问内网ERP系统
75 | >* 远程访问NAS: 管理大量视频、图片
76 | >* 远程监控摄像头
77 | >* 远程刷机
78 | >* 远程数据备份
79 | ---
80 | ## 概要设计
81 | ### 原型
82 | 
83 | ### 客户端架构
84 | 
85 | ### P2PApp
86 | 它是项目里最重要的概念,一个P2PApp就是把远程的一个服务(mstsc/ssh等)通过P2P网络映射到本地监听。二次开发或者我们提供的Restful API,主要工作就是管理P2PApp
87 | 
88 | ## 安全性
89 | 加入OpenP2P共享网络的节点,只能凭授权访问。共享节点只会中转数据,别人无法访问内网任何资源。
90 | ### 1. TLS1.3+AES
91 | 两个节点间通信数据走业界最安全的TLS1.3通道。通信内容还会使用AES加密,双重安全,密钥是通过服务端作换。有效阻止中间人攻击
92 | ### 2. 共享的中转节点是否会获得我的数据
93 | 没错,中转节点天然就是一个中间人,所以才加上AES加密通信内容保证安全。中转节点是无法获取明文的
94 |
95 | ### 3. 中转节点是如何校验权限的
96 | 服务端有个调度模型,根据带宽、ping值、稳定性、服务时长,尽可能地使共享节点均匀地提供服务。连接共享节点使用TOTP密码,hmac-sha256算法校验,它是一次性密码,和我们平时使用的手机验证码或银行密码器一样的原理。
97 |
98 | ## 编译
99 | go version 1.20 only (支持win7)
100 | cd到代码根目录,执行
101 | ```
102 | make
103 | ```
104 | 手动编译特定系统和架构
105 | All GOOS values:
106 | ```
107 | "aix", "android", "darwin", "dragonfly", "freebsd", "hurd", "illumos", "ios", "js", "linux", "nacl", "netbsd", "openbsd", "plan9", "solaris", "windows", "zos"
108 | ```
109 | All GOARCH values:
110 | ```
111 | "386", "amd64", "amd64p32", "arm", "arm64", "arm64be", "armbe", "loong64", "mips", "mips64", "mips64le", "mips64p32", "mips64p32le", "mipsle", "ppc", "ppc64", "ppc64le", "riscv", "riscv64", "s390", "s390x", "sparc", "sparc64", "wasm"
112 | ```
113 |
114 | 比如linux+amd64
115 | ```
116 | export GOPROXY=https://goproxy.io,direct
117 | go mod tidy
118 | CGO_ENABLED=0 env GOOS=linux GOARCH=amd64 go build -o openp2p --ldflags '-s -w ' -gcflags '-l' -p 8 -installsuffix cgo ./cmd
119 | ```
120 |
121 | ## RoadMap
122 | 近期计划:
123 | 1. ~~支持IPv6~~(100%)
124 | 2. ~~支持随系统自动启动,安装成系统服务~~(100%)
125 | 3. ~~提供一些免费服务器给特别差的网络,如广电网络~~(100%)
126 | 4. ~~建立网站,用户可以在网站管理所有P2PApp和设备。查看设备在线状态,升级,增删查改重启P2PApp等~~(100%)
127 | 5. 建立公众号,用户可在微信公众号管理所有P2PApp和设备
128 | 6. 客户端提供WebUI
129 | 7. ~~支持自有服务器,开源服务器程序~~(100%)
130 | 8. 共享节点调度模型优化,对不同的运营商优化
131 | 9. ~~方便二次开发,提供API和lib~~(100%)
132 | 10. ~~应用层支持UDP协议,实现很简单,但UDP应用较少暂不急~~(100%)
133 | 11. ~~底层通信支持KCP协议,目前仅支持Quic;KCP专门对延时优化,被游戏加速器广泛使用,可以牺牲一定的带宽降低延时~~(100%)
134 | 12. ~~支持Android系统,让旧手机焕发青春变成移动网关~~(100%)
135 | 13. ~~支持Windows网上邻居共享文件~~(100%)
136 | 14. ~~内网直连优化~~(100%)
137 | 15. ~~支持UPNP~~(100%)
138 | 16. ~~支持Android~~(100%)
139 | 17. 支持IOS
140 |
141 | 远期计划:
142 | 1. 利用区块链技术去中心化,让共享设备的用户有收益,从而促进更多用户共享,达到正向闭环。
143 | 2. 企业级支持,可以更好地管理大量设备,和更安全更细的权限控制
144 |
145 | ## 参与贡献
146 | TODO或ISSUE里如果有你擅长的领域,或者你有特别好的主意,可以加入OpenP2P项目,贡献你的代码。待项目茁壮成长后,你们就是知名开源项目的主要代码贡献者,岂不快哉。
147 |
148 | ## 技术交流
149 | QQ群:16947733
150 | 邮箱:openp2p.cn@gmail.com tenderiron@139.com
151 |
152 | ## 免责声明
153 | 本项目开源供大家学习和免费使用,禁止用于非法用途,任何不当使用本项目或意外造成的损失,本项目及相关人员不会承担任何责任。
154 |
--------------------------------------------------------------------------------
/USAGE-ZH.md:
--------------------------------------------------------------------------------
1 | # 手动运行说明
2 | 大部分情况通过 操作即可。有些情况需要手动运行
3 | > :warning: 本文所有命令, Windows环境使用"openp2p.exe", Linux环境使用"./openp2p"
4 |
5 |
6 | ## 安装和监听
7 | ```
8 | ./openp2p install -node OFFICEPC1 -token TOKEN
9 | 或
10 | ./openp2p -d -node OFFICEPC1 -token TOKEN
11 | # 注意Windows系统把“./openp2p” 换成“openp2p.exe”
12 | ```
13 | >* install: 安装模式【推荐】,会安装成系统服务,这样它就能随系统自动启动
14 | >* -d: daemon模式。发现worker进程意外退出就会自动启动新的worker进程
15 | >* -node: 独一无二的节点名字,唯一标识
16 | >* -token: 在“我的”里面找到
17 | >* -sharebandwidth: 作为共享节点时提供带宽,默认10mbps. 如果是光纤大带宽,设置越大效果越好. 0表示不共享,该节点只在私有的P2P网络使用。不加入共享的P2P网络,这样也意味着无法使用别人的共享节点
18 | >* -loglevel: 需要查看更多调试日志,设置0;默认是1
19 |
20 | ### 在docker容器里运行openp2p
21 | 我们暂时还没提供官方docker镜像,你可以在随便一个容器里运行
22 | ```
23 | nohup ./openp2p -d -node OFFICEPC1 -token TOKEN &
24 | #这里由于一般的镜像都精简过,install系统服务会失败,所以使用直接daemon模式后台运行
25 | ```
26 | ## 连接
27 | ```
28 | ./openp2p -d -node HOMEPC123 -token TOKEN -appname OfficeWindowsRemote -peernode OFFICEPC1 -dstip 127.0.0.1 -dstport 3389 -srcport 23389
29 | 使用配置文件,建立多个P2PApp
30 | ./openp2p -d
31 | ```
32 | >* -appname: 这个P2P应用名字
33 | >* -peernode: 目标节点名字
34 | >* -dstip: 目标服务地址,默认本机127.0.0.1
35 | >* -dstport: 目标服务端口,常见的如windows远程桌面3389,Linux ssh 22
36 | >* -protocol: 目标服务协议 tcp、udp
37 |
38 | ## 配置文件
39 | 一般保存在当前目录,安装模式下会保存到 `C:\Program Files\OpenP2P\config.json` 或 `/usr/local/openp2p/config.json`
40 | 希望修改参数,或者配置多个P2PApp可手动修改配置文件
41 |
42 | 配置实例
43 | ```
44 | {
45 | "network": {
46 | "Node": "YOUR-NODE-NAME",
47 | "Token": "TOKEN",
48 | "ShareBandwidth": 0,
49 | "ServerHost": "api.openp2p.cn",
50 | "ServerPort": 27183,
51 | "UDPPort1": 27182,
52 | "UDPPort2": 27183
53 | },
54 | "apps": [
55 | {
56 | "AppName": "OfficeWindowsPC",
57 | "Protocol": "tcp",
58 | "SrcPort": 23389,
59 | "PeerNode": "OFFICEPC1",
60 | "DstPort": 3389,
61 | "DstHost": "localhost",
62 | },
63 | {
64 | "AppName": "OfficeServerSSH",
65 | "Protocol": "tcp",
66 | "SrcPort": 22,
67 | "PeerNode": "OFFICEPC1",
68 | "DstPort": 22,
69 | "DstHost": "192.168.1.5",
70 | }
71 | ]
72 | }
73 | ```
74 |
75 | ## 升级客户端
76 | ```
77 | # update local client
78 | ./openp2p update
79 | # update remote client
80 | curl --insecure 'https://api.openp2p.cn:27183/api/v1/device/YOUR-NODE-NAME/update?user=&password='
81 | ```
82 |
83 | Windows系统需要设置防火墙放行本程序,程序会自动设置,如果设置失败会影响连接功能。
84 | Linux系统(Ubuntu和CentOS7)的防火墙默认配置均不会有影响,如果不行可尝试关闭防火墙
85 | ```
86 | systemctl stop firewalld.service
87 | systemctl start firewalld.service
88 | firewall-cmd --state
89 | ```
90 | ## 停止
91 | TODO: windows linux macos
92 | ## 卸载
93 | ```
94 | ./openp2p uninstall
95 | # 已安装时
96 | # windows
97 | C:\Program Files\OpenP2P\openp2p.exe uninstall
98 | # linux,macos
99 | sudo /usr/local/openp2p/openp2p uninstall
100 | ```
101 |
102 | ## Docker运行
103 | ```
104 | # 把YOUR-TOKEN和YOUR-NODE-NAME替换成自己的
105 | docker run -d --restart=always --net host --name openp2p-client -e OPENP2P_TOKEN=YOUR-TOKEN -e OPENP2P_NODE=YOUR-NODE-NAME openp2pcn/openp2p-client:latest
106 | OR
107 | docker run -d --restart=always --net host --name openp2p-client openp2pcn/openp2p-client:latest -token YOUR-TOKEN -node YOUR-NODE-NAME
108 | ```
109 |
--------------------------------------------------------------------------------
/USAGE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Parameters details
4 | In most cases, you can operate it through . In some cases it is necessary to run manually
5 | > :warning: all commands in this doc, Windows env uses "openp2p.exe", Linux env uses "./openp2p"
6 |
7 |
8 | ## Install and Listen
9 | ```
10 | ./openp2p install -node OFFICEPC1 -token TOKEN
11 | Or
12 | ./openp2p -d -node OFFICEPC1 -token TOKEN
13 |
14 | ```
15 | >* install: [recommand] will install as system service. So it will autorun when system booting.
16 | >* -d: daemon mode run once. When the worker process is found to exit unexpectedly, a new worker process will be automatically started
17 | >* -node: Unique node name, unique identification
18 | >* -token: See "Profile"
19 | >* -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
20 | >* -loglevel: Need to view more debug logs, set 0; the default is 1
21 |
22 | ### Run in Docker container
23 | We don't provide official docker image yet, you can run it in any container
24 | ```
25 | nohup ./openp2p -d -node OFFICEPC1 -token TOKEN &
26 | # Since many docker images have been simplified, the install system service will fail, so the daemon mode is used to run in the background
27 | ```
28 |
29 | ## Connect
30 | ```
31 | ./openp2p -d -node HOMEPC123 -token TOKEN -appname OfficeWindowsRemote -peernode OFFICEPC1 -dstip 127.0.0.1 -dstport 3389 -srcport 23389
32 | Create multiple P2PApp by config file
33 | ./openp2p -d
34 | ```
35 | >* -appname: This P2PApp name
36 | >* -peernode: Target node name
37 | >* -dstip: Target service address, default local 127.0.0.1
38 | >* -dstport: Target service port, such as windows remote desktop 3389, Linux ssh 22
39 | >* -protocol: Target service protocol tcp, udp
40 |
41 | ## Config file
42 | 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`
43 | If you want to modify the parameters, or configure multiple P2PApps, you can manually modify the configuration file
44 |
45 | Configuration example
46 | ```
47 | {
48 | "network": {
49 | "Node": "YOUR-NODE-NAME",
50 | "Token": "TOKEN",
51 | "ShareBandwidth": 0,
52 | "ServerHost": "api.openp2p.cn",
53 | "ServerPort": 27183,
54 | "UDPPort1": 27182,
55 | "UDPPort2": 27183
56 | },
57 | "apps": [
58 | {
59 | "AppName": "OfficeWindowsPC",
60 | "Protocol": "tcp",
61 | "SrcPort": 23389,
62 | "PeerNode": "OFFICEPC1",
63 | "DstPort": 3389,
64 | "DstHost": "localhost",
65 | },
66 | {
67 | "AppName": "OfficeServerSSH",
68 | "Protocol": "tcp",
69 | "SrcPort": 22,
70 | "PeerNode": "OFFICEPC1",
71 | "DstPort": 22,
72 | "DstHost": "192.168.1.5",
73 | }
74 | ]
75 | }
76 | ```
77 | ## Client update
78 | ```
79 | # update local client
80 | ./openp2p update
81 | # update remote client
82 | curl --insecure 'https://api.openp2p.cn:27183/api/v1/device/YOUR-NODE-NAME/update?user=&password='
83 | ```
84 |
85 | 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.
86 | The default firewall configuration of Linux system (Ubuntu and CentOS7) will not have any effect, if not, you can try to turn off the firewall
87 | ```
88 | systemctl stop firewalld.service
89 | systemctl start firewalld.service
90 | firewall-cmd --state
91 | ```
92 |
93 | ## Uninstall
94 | ```
95 | ./openp2p uninstall
96 | # when already installed
97 | # windows
98 | C:\Program Files\OpenP2P\openp2p.exe uninstall
99 | # linux,macos
100 | sudo /usr/local/openp2p/openp2p uninstall
101 | ```
102 |
103 | ## Run with Docker
104 | ```
105 | # Replace YOUR-TOKEN and YOUR-NODE-NAME with yours
106 | docker run -d --net host --name openp2p-client -e OPENP2P_TOKEN=YOUR-TOKEN -e OPENP2P_NODE=YOUR-NODE-NAME openp2pcn/openp2p-client:latest
107 | OR
108 | docker run -d --net host --name openp2p-client openp2pcn/openp2p-client:latest -token YOUR-TOKEN -node YOUR-NODE-NAME
109 | ```
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/app/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/app/.idea/.name:
--------------------------------------------------------------------------------
1 | OpenP2P
--------------------------------------------------------------------------------
/app/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
19 |
--------------------------------------------------------------------------------
/app/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/README.md:
--------------------------------------------------------------------------------
1 | ## Build
2 | depends on openjdk 11, gradle 8.1.3, ndk 21
3 | ```
4 |
5 | # latest version not support go1.20
6 | go install golang.org/x/mobile/cmd/gomobile@7c4916698cc93475ebfea76748ee0faba2deb2a5
7 | gomobile init
8 | go get -v golang.org/x/mobile/bind@7c4916698cc93475ebfea76748ee0faba2deb2a5
9 | cd core
10 | gomobile bind -target android -v
11 | if [[ $? -ne 0 ]]; then
12 | echo "build error"
13 | exit 9
14 | fi
15 | echo "build ok"
16 | cp openp2p.aar openp2p-sources.jar ../app/app/libs
17 | echo "copy to APP libs"
18 |
19 | edit app/app/build.gradle
20 | ```
21 | signingConfigs {
22 | release {
23 | storeFile file('YOUR-JKS-PATH')
24 | storePassword 'YOUR-PASSWORD'
25 | keyAlias 'openp2p.keys'
26 | keyPassword 'YOUR-PASSWORD'
27 | }
28 | }
29 | ```
30 | cd ../app
31 | ./gradlew build
32 |
33 | ```
--------------------------------------------------------------------------------
/app/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | }
5 |
6 | android {
7 | signingConfigs {
8 | release {
9 | storeFile file('C:\\work\\src\\openp2p-client\\app\\openp2p.jks')
10 | storePassword 'YOUR-PASSWORD'
11 | keyAlias 'openp2p.keys'
12 | keyPassword 'YOUR-PASSWORD'
13 | }
14 | }
15 | compileSdkVersion 31
16 | buildToolsVersion "30.0.3"
17 |
18 | defaultConfig {
19 | applicationId "cn.openp2p"
20 | minSdkVersion 16
21 | targetSdkVersion 31
22 | versionCode 1
23 | versionName "2718281828"
24 |
25 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
26 | namespace "cn.openp2p"
27 | }
28 |
29 | buildTypes {
30 | release {
31 | minifyEnabled true
32 | shrinkResources true
33 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
34 | signingConfig signingConfigs.release
35 | }
36 | }
37 | compileOptions {
38 | sourceCompatibility JavaVersion.VERSION_1_8
39 | targetCompatibility JavaVersion.VERSION_1_8
40 | }
41 | kotlinOptions {
42 | jvmTarget = '1.8'
43 | }
44 | buildFeatures {
45 | viewBinding true
46 | }
47 | }
48 |
49 | dependencies {
50 | implementation fileTree(dir: "libs", include: ["*.jar", "*.aar"])
51 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
52 | implementation 'androidx.core:core-ktx:1.3.1'
53 | implementation 'androidx.appcompat:appcompat:1.2.0'
54 | implementation 'com.google.android.material:material:1.2.1'
55 | implementation 'androidx.annotation:annotation:1.1.0'
56 | implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
57 | implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
58 | implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
59 | testImplementation 'junit:junit:4.+'
60 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
61 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
62 | implementation files('libs\\openp2p-sources.jar')
63 | }
--------------------------------------------------------------------------------
/app/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/app/src/androidTest/java/cn/openp2p/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package cn.openp2p
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("cn.openp2p", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
17 |
22 |
23 |
24 |
25 |
26 |
27 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/app/app/src/main/java/cn/openp2p/BootReceiver.kt:
--------------------------------------------------------------------------------
1 | package cn.openp2p
2 |
3 | import android.content.BroadcastReceiver
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.net.VpnService
7 | import android.os.Build
8 | import android.os.Bundle
9 | import android.util.Log
10 | import cn.openp2p.ui.login.LoginActivity
11 | class BootReceiver : BroadcastReceiver() {
12 | override fun onReceive(context: Context, intent: Intent) {
13 | // Logger.log("pp onReceive "+intent.action.toString())
14 | Log.i("onReceive","start "+intent.action.toString())
15 | // if (Intent.ACTION_BOOT_COMPLETED == intent.action) {
16 | // Log.i("onReceive","match "+intent.action.toString())
17 | // VpnService.prepare(context)
18 | // val intent = Intent(context, OpenP2PService::class.java)
19 | // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
20 | // context.startForegroundService(intent)
21 | // } else {
22 | // context.startService(intent)
23 | // }
24 | // }
25 | Log.i("onReceive","end "+intent.action.toString())
26 | }
27 | }
--------------------------------------------------------------------------------
/app/app/src/main/java/cn/openp2p/data/LoginDataSource.kt:
--------------------------------------------------------------------------------
1 | package cn.openp2p.data
2 |
3 | import cn.openp2p.data.model.LoggedInUser
4 | import java.io.IOException
5 |
6 | /**
7 | * Class that handles authentication w/ login credentials and retrieves user information.
8 | */
9 | class LoginDataSource {
10 |
11 | fun login(username: String, password: String): Result {
12 | try {
13 | // TODO: handle loggedInUser authentication
14 | val fakeUser = LoggedInUser(java.util.UUID.randomUUID().toString(), "Jane Doe")
15 | return Result.Success(fakeUser)
16 | } catch (e: Throwable) {
17 | return Result.Error(IOException("Error logging in", e))
18 | }
19 | }
20 |
21 | fun logout() {
22 | // TODO: revoke authentication
23 | }
24 | }
--------------------------------------------------------------------------------
/app/app/src/main/java/cn/openp2p/data/LoginRepository.kt:
--------------------------------------------------------------------------------
1 | package cn.openp2p.data
2 |
3 | import cn.openp2p.data.model.LoggedInUser
4 |
5 | /**
6 | * Class that requests authentication and user information from the remote data source and
7 | * maintains an in-memory cache of login status and user credentials information.
8 | */
9 |
10 | class LoginRepository(val dataSource: LoginDataSource) {
11 |
12 | // in-memory cache of the loggedInUser object
13 | var user: LoggedInUser? = null
14 | private set
15 |
16 | val isLoggedIn: Boolean
17 | get() = user != null
18 |
19 | init {
20 | // If user credentials will be cached in local storage, it is recommended it be encrypted
21 | // @see https://developer.android.com/training/articles/keystore
22 | user = null
23 | }
24 |
25 | fun logout() {
26 | user = null
27 | dataSource.logout()
28 | }
29 |
30 | fun login(username: String, password: String): Result {
31 | // handle login
32 | val result = dataSource.login(username, password)
33 |
34 | if (result is Result.Success) {
35 | setLoggedInUser(result.data)
36 | }
37 |
38 | return result
39 | }
40 |
41 | private fun setLoggedInUser(loggedInUser: LoggedInUser) {
42 | this.user = loggedInUser
43 | // If user credentials will be cached in local storage, it is recommended it be encrypted
44 | // @see https://developer.android.com/training/articles/keystore
45 | }
46 | }
--------------------------------------------------------------------------------
/app/app/src/main/java/cn/openp2p/data/Result.kt:
--------------------------------------------------------------------------------
1 | package cn.openp2p.data
2 |
3 | /**
4 | * A generic class that holds a value with its loading status.
5 | * @param
6 | */
7 | sealed class Result {
8 |
9 | data class Success(val data: T) : Result()
10 | data class Error(val exception: Exception) : Result()
11 |
12 | override fun toString(): String {
13 | return when (this) {
14 | is Success<*> -> "Success[data=$data]"
15 | is Error -> "Error[exception=$exception]"
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/app/app/src/main/java/cn/openp2p/data/model/LoggedInUser.kt:
--------------------------------------------------------------------------------
1 | package cn.openp2p.data.model
2 |
3 | /**
4 | * Data class that captures user information for logged in users retrieved from LoginRepository
5 | */
6 | data class LoggedInUser(
7 | val userId: String,
8 | val displayName: String
9 | )
--------------------------------------------------------------------------------
/app/app/src/main/java/cn/openp2p/log.kt:
--------------------------------------------------------------------------------
1 | package cn.openp2p
2 | import android.content.*
3 | import java.io.File
4 | import java.io.FileWriter
5 | import java.text.SimpleDateFormat
6 | import java.util.*
7 | import android.app.*
8 | import android.content.Context
9 | import android.content.Intent
10 | import android.graphics.Color
11 | import java.io.IOException
12 | import android.net.VpnService
13 | import android.os.Binder
14 | import android.os.Build
15 | import android.os.IBinder
16 | import android.os.ParcelFileDescriptor
17 | import android.util.Log
18 | import androidx.annotation.RequiresApi
19 | import androidx.core.app.NotificationCompat
20 | import cn.openp2p.ui.login.LoginActivity
21 | import kotlinx.coroutines.GlobalScope
22 | import kotlinx.coroutines.launch
23 | import openp2p.Openp2p
24 | import java.io.FileInputStream
25 | import java.io.FileOutputStream
26 | import java.nio.ByteBuffer
27 | import kotlinx.coroutines.*
28 | import org.json.JSONObject
29 |
30 | object Logger {
31 | private val logFile: File = File("app.log")
32 |
33 | fun log(message: String) {
34 | val timestamp = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date())
35 | val logMessage = "$timestamp: $message\n"
36 |
37 | try {
38 | val fileWriter = FileWriter(logFile, true)
39 | fileWriter.append(logMessage)
40 | fileWriter.close()
41 | } catch (e: Exception) {
42 | e.printStackTrace()
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/app/src/main/java/cn/openp2p/ui/login/LoggedInUserView.kt:
--------------------------------------------------------------------------------
1 | package cn.openp2p.ui.login
2 |
3 | /**
4 | * User details post authentication that is exposed to the UI
5 | */
6 | data class LoggedInUserView(
7 | val displayName: String
8 | //... other data fields that may be accessible to the UI
9 | )
--------------------------------------------------------------------------------
/app/app/src/main/java/cn/openp2p/ui/login/LoginActivity.kt:
--------------------------------------------------------------------------------
1 | package cn.openp2p.ui.login
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.Activity
5 | import android.app.ActivityManager
6 | import android.app.Notification
7 | import android.app.PendingIntent
8 | import android.content.BroadcastReceiver
9 | import android.content.ComponentName
10 | import android.content.Context
11 | import android.content.Intent
12 | import android.content.ServiceConnection
13 | import android.net.Uri
14 | import android.net.VpnService
15 | import android.os.Build
16 | import android.os.Bundle
17 | import android.os.IBinder
18 | import android.text.Editable
19 | import android.text.TextWatcher
20 | import android.util.Log
21 | import android.view.View
22 | import android.widget.EditText
23 | import android.widget.Toast
24 | import androidx.annotation.RequiresApi
25 | import androidx.annotation.StringRes
26 | import androidx.appcompat.app.AppCompatActivity
27 | import androidx.lifecycle.Observer
28 | import androidx.lifecycle.ViewModelProvider
29 | import cn.openp2p.Logger
30 | import cn.openp2p.OpenP2PService
31 | import cn.openp2p.R
32 | import cn.openp2p.databinding.ActivityLoginBinding
33 | import openp2p.Openp2p
34 | import kotlin.concurrent.thread
35 | import kotlin.system.exitProcess
36 |
37 |
38 | class LoginActivity : AppCompatActivity() {
39 | companion object {
40 | private val LOG_TAG = LoginActivity::class.simpleName
41 | }
42 |
43 | private val connection = object : ServiceConnection {
44 | override fun onServiceConnected(className: ComponentName, service: IBinder) {
45 | val binder = service as OpenP2PService.LocalBinder
46 | mService = binder.getService()
47 | }
48 |
49 | override fun onServiceDisconnected(className: ComponentName) {
50 |
51 | }
52 | }
53 | private lateinit var loginViewModel: LoginViewModel
54 | private lateinit var binding: ActivityLoginBinding
55 | private lateinit var mService: OpenP2PService
56 | @RequiresApi(Build.VERSION_CODES.O)
57 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
58 | super.onActivityResult(requestCode, resultCode, data)
59 | if (requestCode == 0 && resultCode == Activity.RESULT_OK) {
60 | startService(Intent(this, OpenP2PService::class.java))
61 | }
62 | }
63 |
64 | @RequiresApi(Build.VERSION_CODES.O)
65 | override fun onCreate(savedInstanceState: Bundle?) {
66 | super.onCreate(savedInstanceState)
67 |
68 | binding = ActivityLoginBinding.inflate(layoutInflater)
69 | setContentView(binding.root)
70 |
71 | val token = binding.token
72 | val login = binding.login
73 | val onlineState = binding.onlineState
74 | val openp2pLog = binding.openp2pLog
75 | val profile = binding.profile
76 | val loading = binding.loading
77 |
78 | loginViewModel = ViewModelProvider(this, LoginViewModelFactory())
79 | .get(LoginViewModel::class.java)
80 |
81 | loginViewModel.loginFormState.observe(this@LoginActivity, Observer {
82 | val loginState = it ?: return@Observer
83 |
84 | // disable login button unless both username / password is valid
85 | login.isEnabled = loginState.isDataValid
86 |
87 | if (loginState.passwordError != null) {
88 | token.error = getString(loginState.passwordError)
89 | }
90 | })
91 | openp2pLog.setText(R.string.phone_setting)
92 | val intent = VpnService.prepare(this)
93 | if (intent != null)
94 | {
95 | Log.i("openp2p", "VpnService.prepare need permission");
96 | startActivityForResult(intent, 0)
97 | }
98 | else {
99 | Log.i("openp2p", "VpnService.prepare ready");
100 | onActivityResult(0, Activity.RESULT_OK, null)
101 | }
102 |
103 |
104 | profile.setOnClickListener {
105 | val url = "https://console.openp2p.cn/profile"
106 | val i = Intent(Intent.ACTION_VIEW)
107 | i.data = Uri.parse(url)
108 | startActivity(i)
109 | }
110 | token.apply {
111 | afterTextChanged {
112 | loginViewModel.loginDataChanged(
113 | "username.text.toString()",
114 | token.text.toString()
115 | )
116 | }
117 |
118 | openp2pLog.setText(R.string.phone_setting)
119 |
120 | login.setOnClickListener {
121 | if (login.text.toString()=="退出"){
122 | // val intent = Intent(this@LoginActivity, OpenP2PService::class.java)
123 | // stopService(intent)
124 | Log.i(LOG_TAG, "quit")
125 | mService.stop()
126 |
127 | val intent = Intent(this@LoginActivity, OpenP2PService::class.java)
128 | stopService(intent)
129 | // 解绑服务
130 | unbindService(connection)
131 |
132 | // 结束当前 Activity
133 | finish() // 或者使用 finishAffinity() 来结束整个应用程序
134 | exitAPP()
135 | // finishAffinity()
136 |
137 | }
138 | login.setText("退出")
139 | Log.i(LOG_TAG, "start")
140 | val intent = Intent(this@LoginActivity, OpenP2PService::class.java)
141 | intent.putExtra("token", token.text.toString())
142 | bindService(intent, connection, Context.BIND_AUTO_CREATE)
143 | startService(intent)
144 | thread {
145 | do {
146 | Thread.sleep(1000)
147 | if (!::mService.isInitialized) continue
148 | val isConnect = mService.isConnected()
149 | // Log.i(LOG_TAG, "mService.isConnected() = " + isConnect.toString())
150 | runOnUiThread {
151 | if (isConnect) {
152 | onlineState.setText("在线")
153 | } else {
154 | onlineState.setText("正在登录")
155 | }
156 | }
157 | } while (true)
158 | }
159 |
160 | }
161 | val tokenText = Openp2p.getToken(getExternalFilesDir(null).toString())
162 | token.setText(tokenText.toString())
163 | // Check token length and automatically click login if length > 10
164 | if (tokenText.length > 10) {
165 | // Logger.log("performClick ")
166 | login.performClick()
167 | }
168 | }
169 | }
170 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
171 | @SuppressLint("ServiceCast")
172 | fun exitAPP() {
173 | val activityManager =
174 | applicationContext?.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
175 | val appTaskList = activityManager.appTasks
176 |
177 | for (i in appTaskList.indices) {
178 | appTaskList[i].finishAndRemoveTask()
179 | }
180 | exitProcess(0)
181 | }
182 |
183 | private fun updateUiWithUser(model: LoggedInUserView) {
184 | val welcome = getString(R.string.welcome)
185 | val displayName = model.displayName
186 | // TODO : initiate successful logged in experience
187 | Toast.makeText(
188 | applicationContext,
189 | "$welcome $displayName",
190 | Toast.LENGTH_LONG
191 | ).show()
192 | }
193 |
194 | private fun showLoginFailed(@StringRes errorString: Int) {
195 | Toast.makeText(applicationContext, errorString, Toast.LENGTH_SHORT).show()
196 | }
197 | }
198 |
199 | /**
200 | * Extension function to simplify setting an afterTextChanged action to EditText components.
201 | */
202 | fun EditText.afterTextChanged(afterTextChanged: (String) -> Unit) {
203 | this.addTextChangedListener(object : TextWatcher {
204 | override fun afterTextChanged(editable: Editable?) {
205 | afterTextChanged.invoke(editable.toString())
206 | }
207 |
208 | override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
209 |
210 | override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
211 | })
212 | }
--------------------------------------------------------------------------------
/app/app/src/main/java/cn/openp2p/ui/login/LoginFormState.kt:
--------------------------------------------------------------------------------
1 | package cn.openp2p.ui.login
2 |
3 | /**
4 | * Data validation state of the login form.
5 | */
6 | data class LoginFormState(
7 | val usernameError: Int? = null,
8 | val passwordError: Int? = null,
9 | val isDataValid: Boolean = false
10 | )
--------------------------------------------------------------------------------
/app/app/src/main/java/cn/openp2p/ui/login/LoginResult.kt:
--------------------------------------------------------------------------------
1 | package cn.openp2p.ui.login
2 |
3 | /**
4 | * Authentication result : success (user details) or error message.
5 | */
6 | data class LoginResult(
7 | val success: LoggedInUserView? = null,
8 | val error: Int? = null
9 | )
--------------------------------------------------------------------------------
/app/app/src/main/java/cn/openp2p/ui/login/LoginViewModel.kt:
--------------------------------------------------------------------------------
1 | package cn.openp2p.ui.login
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 | import android.util.Patterns
7 | import cn.openp2p.data.LoginRepository
8 | import cn.openp2p.data.Result
9 |
10 | import cn.openp2p.R
11 |
12 | class LoginViewModel(private val loginRepository: LoginRepository) : ViewModel() {
13 |
14 | private val _loginForm = MutableLiveData()
15 | val loginFormState: LiveData = _loginForm
16 |
17 | private val _loginResult = MutableLiveData()
18 | val loginResult: LiveData = _loginResult
19 |
20 | fun login(username: String, password: String) {
21 | // can be launched in a separate asynchronous job
22 | val result = loginRepository.login(username, password)
23 |
24 | if (result is Result.Success) {
25 | _loginResult.value =
26 | LoginResult(success = LoggedInUserView(displayName = result.data.displayName))
27 | } else {
28 | _loginResult.value = LoginResult(error = R.string.login_failed)
29 | }
30 | }
31 |
32 | fun loginDataChanged(username: String, password: String) {
33 | // if (!isUserNameValid(username)) {
34 | // _loginForm.value = LoginFormState(usernameError = R.string.invalid_username)
35 | // } else
36 | if (!isPasswordValid(password)) {
37 | _loginForm.value = LoginFormState(passwordError = R.string.invalid_password)
38 | } else {
39 | _loginForm.value = LoginFormState(isDataValid = true)
40 | }
41 | }
42 |
43 | // A placeholder username validation check
44 | private fun isUserNameValid(username: String): Boolean {
45 | return true
46 | return if (username.contains('@')) {
47 | Patterns.EMAIL_ADDRESS.matcher(username).matches()
48 | } else {
49 | username.isNotBlank()
50 | }
51 | }
52 |
53 | // A placeholder password validation check
54 | private fun isPasswordValid(password: String): Boolean {
55 | return password.length > 5
56 | }
57 | }
--------------------------------------------------------------------------------
/app/app/src/main/java/cn/openp2p/ui/login/LoginViewModelFactory.kt:
--------------------------------------------------------------------------------
1 | package cn.openp2p.ui.login
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.ViewModelProvider
5 | import cn.openp2p.data.LoginDataSource
6 | import cn.openp2p.data.LoginRepository
7 |
8 | /**
9 | * ViewModel provider factory to instantiate LoginViewModel.
10 | * Required given LoginViewModel has a non-empty constructor
11 | */
12 | class LoginViewModelFactory : ViewModelProvider.Factory {
13 |
14 | @Suppress("UNCHECKED_CAST")
15 | override fun create(modelClass: Class): T {
16 | if (modelClass.isAssignableFrom(LoginViewModel::class.java)) {
17 | return LoginViewModel(
18 | loginRepository = LoginRepository(
19 | dataSource = LoginDataSource()
20 | )
21 | ) as T
22 | }
23 | throw IllegalArgumentException("Unknown ViewModel class")
24 | }
25 | }
--------------------------------------------------------------------------------
/app/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/app/src/main/res/drawable/icon.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/app/src/main/res/layout/activity_login.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
24 |
25 |
37 |
38 |
39 |
40 |
55 |
56 |
69 |
70 |
78 |
79 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/app/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/app/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/app/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/app/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/app/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/app/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/app/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/app/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/app/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/app/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/app/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
--------------------------------------------------------------------------------
/app/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | OpenP2P
3 |
4 | Email
5 | Password
6 | 登录
7 | Sign in
8 | "Welcome !"
9 | Not a valid username
10 | Token可以在 https://console.openp2p.cn/profile 获得
11 | "Login failed"
12 | "安卓系统默认设置的”杀后台进程“会导致 OpenP2P 在后台运行一会后,被系统杀死进程,导致您的体验受到影响。您可以通过以下方式修改几个设置,解决此问题:
13 | 华为鸿蒙:
14 | 1. 允许应用后台运行:进入设置 → 搜索进入 应用启动管理 → 关闭 OpenP2P 的 自动管理 开关 → 在弹框中勾选 允许后台活动
15 | 2. 避免应用被电池优化程序清理:进入设置 → 搜索进入电池优化 → 不允许 →选择所有应用 → 找到无法后台运行的应用 → 设置为不允许
16 | 3. 关闭省电模式:进入设置 → 电池 → 关闭 省电模式 开关
17 | 4. 保持设备网络连接:进入设置 → 电池 → 更多电池设置 → 开启 休眠时始终保持网络连接 开关。
18 | 5. 给后台运行的应用加锁:打开应用后 → 进入多任务界面 → 下拉选中的卡片进行加锁 → 然后点击清理图标清理其他不经常使用的应用
19 | 6. 设置开发人员选项中相关开关:进入设置 → 搜索进入 开发人员选项 → 找到 不保留活动 开关后关闭 → 并在 后台进程限制 选择 标准限制
20 |
21 | 华为手机:
22 | 进入”设置“,搜索并进入“电池优化“界面,选中 OpenP2P 程序,不允许系统对其进行电池优化;
23 | 进入”设置“,进入”应用管理“界面,选中 OpenP2P 程序,点击”耗电情况“,开启”允许后台活动“即可;
24 |
25 | 小米手机:
26 | 进入”设置“,进入”更多应用“界面,选中 OpenP2P 程序,点击”省电策略“,设置为”无限制“;
27 | 进入”设置“,进入”特色功能“界面,选中”游戏加速“,将其关闭即可;
28 |
29 | OPPO手机:
30 | 进入”设置“,进入”应用管理“界面,选中 OpenP2P 程序,点击”耗电保护“,关闭所有设置项即可;
31 |
32 | VIVO手机:
33 | 进入”设置“,进入”电池“界面,点击”后台高耗电“,将 OpenP2P 设为允许高耗电时继续运行即可;
34 |
35 | 魅族手机:
36 | 进入”设置“,进入”应用管理“界面,选中 OpenP2P 程序,点击”权限管理“,点击”后台管理“,选择”允许后台运行“即可;
37 |
38 | 一加手机:
39 | 进入”设置“,进入”电池“界面,点击”电池优化“,将 OpenP2P 设为”不优化“即可;
40 |
41 | 三星手机:
42 | 进入”智能管理器“,进入”应用程序管理“界面,点击”管理自动运行“,将 OpenP2P 设为”允许后台运行“即可;"
43 |
--------------------------------------------------------------------------------
/app/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | ext.kotlin_version = "1.8.20"
6 | repositories {
7 | google()
8 | mavenCentral()
9 | }
10 | dependencies {
11 | classpath "com.android.tools.build:gradle:8.1.3"
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 |
20 | allprojects {
21 | repositories {
22 | google()
23 | mavenCentral()
24 | jcenter() // Warning: this repository is going to shut down soon
25 | }
26 | }
27 |
28 | task clean(type: Delete) {
29 | delete rootProject.buildDir
30 | }
--------------------------------------------------------------------------------
/app/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
--------------------------------------------------------------------------------
/app/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/app/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Dec 05 16:04:08 CST 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/app/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/app/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/app/openp2p.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/app/openp2p.jks
--------------------------------------------------------------------------------
/app/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = "OpenP2P"
2 | include ':app'
3 |
--------------------------------------------------------------------------------
/cmd/openp2p.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | op "openp2p/core"
5 | )
6 |
7 | func main() {
8 | op.Run()
9 | }
10 |
--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "network": {
3 | "Node": "YOUR_NODE_NAME",
4 | "Token": "YOUR_TOKEN",
5 | "ServerHost": "api.openp2p.cn",
6 | "ServerPort": 27183,
7 | "UDPPort1": 27182,
8 | "UDPPort2": 27183
9 | },
10 | "apps": [
11 | {
12 | "Protocol": "tcp",
13 | "SrcPort": 22,
14 | "PeerNode": "YOURNODE1",
15 | "DstPort": 22,
16 | "DstHost": "127.0.0.1",
17 | "PeerUser": "",
18 | "PeerPassword": ""
19 | },
20 | {
21 | "Protocol": "tcp",
22 | "SrcPort": 50022,
23 | "PeerNode": "YOURNODE2",
24 | "DstPort": 50022,
25 | "DstHost": "127.0.0.1",
26 | "PeerUser": "",
27 | "PeerPassword": ""
28 | }
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/core/common.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "bytes"
5 | "crypto/aes"
6 | "crypto/cipher"
7 | "crypto/tls"
8 | "encoding/binary"
9 | "encoding/json"
10 | "fmt"
11 | "math/big"
12 | "math/rand"
13 | "net"
14 | "net/http"
15 | "os"
16 | "os/exec"
17 | "strconv"
18 | "strings"
19 | "time"
20 | )
21 |
22 | const MinNodeNameLen = 8
23 |
24 | func getmac(ip string) string {
25 | ifaces, err := net.Interfaces()
26 | if err != nil {
27 | return ""
28 | }
29 | firstMac := ""
30 | for _, iface := range ifaces {
31 | addrs, _ := iface.Addrs()
32 | for _, addr := range addrs {
33 | if firstMac == "" {
34 | firstMac = iface.HardwareAddr.String()
35 | }
36 | if ipNet, ok := addr.(*net.IPNet); ok && ipNet.IP.String() == ip {
37 | if iface.HardwareAddr.String() != "" {
38 | return iface.HardwareAddr.String()
39 | }
40 | return firstMac
41 | }
42 | }
43 | }
44 | return firstMac
45 | }
46 |
47 | var cbcIVBlock = []byte("UHNJUSBACIJFYSQN")
48 |
49 | var paddingArray = [][]byte{
50 | {0},
51 | {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
52 | {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
53 | {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
54 | {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4},
55 | {5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5},
56 | {6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6},
57 | {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7},
58 | {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
59 | {9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9},
60 | {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
61 | {11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11},
62 | {12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
63 | {13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13},
64 | {14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14},
65 | {15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15},
66 | {16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16},
67 | }
68 |
69 | func pkcs7Padding(plainData []byte, dataLen, blockSize int) int {
70 | padLen := blockSize - dataLen%blockSize
71 | pPadding := plainData[dataLen : dataLen+padLen]
72 |
73 | copy(pPadding, paddingArray[padLen][:padLen])
74 | return padLen
75 | }
76 |
77 | func pkcs7UnPadding(origData []byte, dataLen int) ([]byte, error) {
78 | unPadLen := int(origData[dataLen-1])
79 | if unPadLen <= 0 || unPadLen > 16 {
80 | return nil, fmt.Errorf("wrong pkcs7 padding head size:%d", unPadLen)
81 | }
82 | return origData[:(dataLen - unPadLen)], nil
83 | }
84 |
85 | // AES-CBC
86 | func encryptBytes(key []byte, out, in []byte, plainLen int) ([]byte, error) {
87 | if len(key) == 0 {
88 | return in[:plainLen], nil
89 | }
90 | block, err := aes.NewCipher(key)
91 | if err != nil {
92 | return nil, err
93 | }
94 | //iv := out[:aes.BlockSize]
95 | //if _, err := io.ReadFull(rand.Reader, iv); err != nil {
96 | // return nil, err
97 | //}
98 | mode := cipher.NewCBCEncrypter(block, cbcIVBlock)
99 | total := pkcs7Padding(in, plainLen, aes.BlockSize) + plainLen
100 | mode.CryptBlocks(out[:total], in[:total])
101 | return out[:total], nil
102 | }
103 |
104 | func decryptBytes(key []byte, out, in []byte, dataLen int) ([]byte, error) {
105 | if len(key) == 0 {
106 | return in[:dataLen], nil
107 | }
108 | block, err := aes.NewCipher(key)
109 | if err != nil {
110 | return nil, err
111 | }
112 | mode := cipher.NewCBCDecrypter(block, cbcIVBlock)
113 | mode.CryptBlocks(out[:dataLen], in[:dataLen])
114 | return pkcs7UnPadding(out, dataLen)
115 | }
116 |
117 | // {240e:3b7:622:3440:59ad:7fa1:170c:ef7f 47924975352157270363627191692449083263 China CN 0xc0000965c8 Guangdong GD 0 Guangzhou 23.1167 113.25 Asia/Shanghai AS4134 Chinanet }
118 | func netInfo() *NetInfo {
119 | tr := &http.Transport{
120 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
121 | // DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
122 | // var d net.Dialer
123 | // return d.DialContext(ctx, "tcp6", addr)
124 | // },
125 | }
126 | // sometime will be failed, retry
127 | for i := 0; i < 2; i++ {
128 | client := &http.Client{Transport: tr, Timeout: time.Second * 10}
129 | r, err := client.Get("https://ifconfig.co/json")
130 | if err != nil {
131 | gLog.Println(LvDEBUG, "netInfo error:", err)
132 | continue
133 | }
134 | defer r.Body.Close()
135 | buf := make([]byte, 1024*64)
136 | n, err := r.Body.Read(buf)
137 | if err != nil {
138 | gLog.Println(LvDEBUG, "netInfo error:", err)
139 | continue
140 | }
141 | rsp := NetInfo{}
142 | if err = json.Unmarshal(buf[:n], &rsp); err != nil {
143 | gLog.Printf(LvERROR, "wrong NetInfo:%s", err)
144 | continue
145 | }
146 | return &rsp
147 | }
148 | return nil
149 | }
150 |
151 | func execOutput(name string, args ...string) string {
152 | cmdGetOsName := exec.Command(name, args...)
153 | var cmdOut bytes.Buffer
154 | cmdGetOsName.Stdout = &cmdOut
155 | cmdGetOsName.Run()
156 | return cmdOut.String()
157 | }
158 |
159 | func defaultNodeName() string {
160 | name, _ := os.Hostname()
161 | for len(name) < MinNodeNameLen {
162 | name = fmt.Sprintf("%s%d", name, rand.Int()%10)
163 | }
164 | return name
165 | }
166 |
167 | const EQUAL int = 0
168 | const GREATER int = 1
169 | const LESS int = -1
170 |
171 | func compareVersion(v1, v2 string) int {
172 | if v1 == v2 {
173 | return EQUAL
174 | }
175 | v1Arr := strings.Split(v1, ".")
176 | v2Arr := strings.Split(v2, ".")
177 | for i, subVer := range v1Arr {
178 | if len(v2Arr) <= i {
179 | return GREATER
180 | }
181 | subv1, _ := strconv.Atoi(subVer)
182 | subv2, _ := strconv.Atoi(v2Arr[i])
183 | if subv1 > subv2 {
184 | return GREATER
185 | }
186 | if subv1 < subv2 {
187 | return LESS
188 | }
189 | }
190 | return LESS
191 | }
192 |
193 | func parseMajorVer(ver string) int {
194 | v1Arr := strings.Split(ver, ".")
195 | if len(v1Arr) > 0 {
196 | n, _ := strconv.ParseInt(v1Arr[0], 10, 32)
197 | return int(n)
198 | }
199 | return 0
200 | }
201 |
202 | func IsIPv6(ipStr string) bool {
203 | ip := net.ParseIP(ipStr)
204 | if ip == nil {
205 | return false
206 | }
207 | return ip.To16() != nil && ip.To4() == nil
208 | }
209 |
210 | var letters = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-")
211 |
212 | func randStr(n int) string {
213 | b := make([]byte, n)
214 | for i := range b {
215 | b[i] = letters[rand.Intn(len(letters))]
216 | }
217 | return string(b)
218 | }
219 |
220 | func execCommand(commandPath string, wait bool, arg ...string) (err error) {
221 | command := exec.Command(commandPath, arg...)
222 | err = command.Start()
223 | if err != nil {
224 | return
225 | }
226 | if wait {
227 | err = command.Wait()
228 | }
229 | return
230 | }
231 |
232 | func sanitizeFileName(fileName string) string {
233 | validFileName := fileName
234 | invalidChars := []string{"\\", "/", ":", "*", "?", "\"", "<", ">", "|"}
235 | for _, char := range invalidChars {
236 | validFileName = strings.ReplaceAll(validFileName, char, " ")
237 | }
238 | return validFileName
239 | }
240 |
241 | func prettyJson(s interface{}) string {
242 | jsonData, err := json.MarshalIndent(s, "", " ")
243 | if err != nil {
244 | fmt.Println("Error marshalling JSON:", err)
245 | return ""
246 | }
247 | return string(jsonData)
248 | }
249 |
250 | func inetAtoN(ipstr string) (uint32, error) { // support both ipnet or single ip
251 | i, _, err := net.ParseCIDR(ipstr)
252 | if err != nil {
253 | i = net.ParseIP(ipstr)
254 | if i == nil {
255 | return 0, err
256 | }
257 | }
258 | ret := big.NewInt(0)
259 | ret.SetBytes(i.To4())
260 | return uint32(ret.Int64()), nil
261 | }
262 |
263 | func calculateChecksum(data []byte) uint16 {
264 | length := len(data)
265 | sum := uint32(0)
266 |
267 | // Calculate the sum of 16-bit words
268 | for i := 0; i < length-1; i += 2 {
269 | sum += uint32(binary.BigEndian.Uint16(data[i : i+2]))
270 | }
271 |
272 | // Add the last byte (if odd length)
273 | if length%2 != 0 {
274 | sum += uint32(data[length-1])
275 | }
276 |
277 | // Fold 32-bit sum to 16 bits
278 | sum = (sum >> 16) + (sum & 0xffff)
279 | sum += (sum >> 16)
280 |
281 | return uint16(^sum)
282 | }
283 |
--------------------------------------------------------------------------------
/core/common_test.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "testing"
7 | )
8 |
9 | func TestAESCBC(t *testing.T) {
10 | for packetSize := 1; packetSize <= 8192; packetSize++ {
11 | log.Println("test packetSize=", packetSize)
12 | data := make([]byte, packetSize)
13 | for i := 0; i < packetSize; i++ {
14 | data[i] = byte('0' + i%10)
15 | }
16 | p2pEncryptBuf := make([]byte, len(data)+PaddingSize)
17 | inBuf := make([]byte, len(data)+PaddingSize)
18 | copy(inBuf, data)
19 | cryptKey := []byte("0123456789ABCDEF")
20 | sendBuf, err := encryptBytes(cryptKey, p2pEncryptBuf, inBuf, len(data))
21 | if err != nil {
22 | t.Errorf("encrypt packet failed:%s", err)
23 | }
24 | log.Printf("encrypt data len=%d\n", len(sendBuf))
25 |
26 | decryptBuf := make([]byte, len(sendBuf))
27 | outBuf, err := decryptBytes(cryptKey, decryptBuf, sendBuf, len(sendBuf))
28 | if err != nil {
29 | t.Errorf("decrypt packet failed:%s", err)
30 | }
31 | // log.Printf("len=%d,content=%s\n", len(outBuf), outBuf)
32 | log.Printf("decrypt data len=%d\n", len(outBuf))
33 | log.Println("validate")
34 | for i := 0; i < len(outBuf); i++ {
35 | if outBuf[i] != byte('0'+i%10) {
36 | t.Error("validate failed")
37 | }
38 | }
39 | log.Println("validate ok")
40 | }
41 |
42 | }
43 |
44 | func TestNetInfo(t *testing.T) {
45 | log.Println(netInfo())
46 | }
47 |
48 | func assertCompareVersion(t *testing.T, v1 string, v2 string, result int) {
49 | if compareVersion(v1, v2) != result {
50 | t.Errorf("compare version %s %s fail\n", v1, v2)
51 | }
52 | }
53 | func assertParseMajorVer(t *testing.T, v string, result int) {
54 | if parseMajorVer(v) != result {
55 | t.Errorf("ParseMajorVer %s fail\n", v)
56 | }
57 | }
58 | func TestCompareVersion(t *testing.T) {
59 | // test =
60 | assertCompareVersion(t, "0.98.0", "0.98.0", EQUAL)
61 | assertCompareVersion(t, "0.98", "0.98", EQUAL)
62 | assertCompareVersion(t, "1.4.0", "1.4.0", EQUAL)
63 | assertCompareVersion(t, "1.5.0", "1.5.0", EQUAL)
64 | // test >
65 | assertCompareVersion(t, "0.98.0.22345", "0.98.0.12345", GREATER)
66 | assertCompareVersion(t, "1.98.0.12345", "0.98", GREATER)
67 | assertCompareVersion(t, "10.98.0.12345", "9.98.0.12345", GREATER)
68 | assertCompareVersion(t, "1.4.0", "0.98.0.12345", GREATER)
69 | assertCompareVersion(t, "1.4", "0.98.0.12345", GREATER)
70 | assertCompareVersion(t, "1", "0.98.0.12345", GREATER)
71 | // test <
72 | assertCompareVersion(t, "0.98.0.12345", "0.98.0.12346", LESS)
73 | assertCompareVersion(t, "9.98.0.12345", "10.98.0.12345", LESS)
74 | assertCompareVersion(t, "1.4.2", "1.5.0", LESS)
75 | assertCompareVersion(t, "", "1.5.0", LESS)
76 |
77 | }
78 |
79 | func TestParseMajorVer(t *testing.T) {
80 |
81 | assertParseMajorVer(t, "0.98.0", 0)
82 | assertParseMajorVer(t, "0.98", 0)
83 | assertParseMajorVer(t, "1.4.0", 1)
84 | assertParseMajorVer(t, "1.5.0", 1)
85 |
86 | assertParseMajorVer(t, "0.98.0.22345", 0)
87 | assertParseMajorVer(t, "1.98.0.12345", 1)
88 | assertParseMajorVer(t, "10.98.0.12345", 10)
89 | assertParseMajorVer(t, "1.4.0", 1)
90 | assertParseMajorVer(t, "1.4", 1)
91 | assertParseMajorVer(t, "1", 1)
92 | assertParseMajorVer(t, "2", 2)
93 | assertParseMajorVer(t, "3", 3)
94 | assertParseMajorVer(t, "2.1.0", 2)
95 | assertParseMajorVer(t, "3.0.0", 3)
96 |
97 | }
98 |
99 | func TestIsIPv6(t *testing.T) {
100 | tests := []struct {
101 | ipStr string
102 | want bool
103 | }{
104 | {"2001:0db8:85a3:0000:0000:8a2e:0370:7334", true}, // 有效的 IPv6 地址
105 | {"2001:db8::2:1", true}, // 有效的 IPv6 地址
106 | {"192.168.1.1", false}, // 无效的 IPv6 地址,是 IPv4
107 | {"2001:db8::G:1", false}, // 无效的 IPv6 地址,包含非法字符
108 | // 可以添加更多测试用例
109 | }
110 |
111 | for _, tt := range tests {
112 | got := IsIPv6(tt.ipStr)
113 | if got != tt.want {
114 | t.Errorf("isValidIPv6(%s) = %v, want %v", tt.ipStr, got, tt.want)
115 | }
116 | }
117 | }
118 |
119 | func TestNodeID(t *testing.T) {
120 | node1 := "n1-stable"
121 | node2 := "tony-stable"
122 | nodeID1 := NodeNameToID(node1)
123 | nodeID2 := NodeNameToID(node2)
124 | if nodeID1 < nodeID2 {
125 | fmt.Printf("%s < %s\n", node1, node2)
126 | } else {
127 | fmt.Printf("%s >= %s\n", node1, node2)
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/core/config_test.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "encoding/json"
5 | "testing"
6 | )
7 |
8 | func TestSetSDWAN_ChangeNode(t *testing.T) {
9 | conf := Config{}
10 | sdwanInfo := SDWANInfo{}
11 | sdwanStr := `{"id":1312667996276071700,"name":"network1","gateway":"10.2.3.254/24","mode":"fullmesh","centralNode":"n1-stable","enable":1,"Nodes":[{"name":"222-debug","ip":"10.2.3.13"},{"name":"222stable","ip":"10.2.3.222"},{"name":"5800-debug","ip":"10.2.3.56"},{"name":"Mate60pro","ip":"10.2.3.60"},{"name":"Mymatepad2023","ip":"10.2.3.23"},{"name":"n1-stable","ip":"10.2.3.29","resource":"192.168.3.0/24"},{"name":"tony-stable","ip":"10.2.3.4","resource":"10.1.0.0/16"}]}`
12 | if err := json.Unmarshal([]byte(sdwanStr), &sdwanInfo); err != nil {
13 | t.Errorf("unmarshal error")
14 | return
15 | }
16 |
17 | conf.setSDWAN(sdwanInfo)
18 | if len(conf.getDelNodes()) > 0 {
19 | t.Errorf("getDelNodes error")
20 | return
21 | }
22 | if len(conf.getAddNodes()) != 7 {
23 | t.Errorf("getAddNodes error")
24 | return
25 | }
26 | sdwanInfo2 := SDWANInfo{}
27 | sdwanStr = `{"id":1312667996276071700,"name":"network1","gateway":"10.2.3.254/24","mode":"fullmesh","centralNode":"n1-stable","enable":1,"Nodes":[{"name":"222-debug","ip":"10.2.3.13"},{"name":"222stable","ip":"10.2.3.222"},{"name":"5800-debug","ip":"10.2.3.56"},{"name":"Mate60pro","ip":"10.2.3.60"},{"name":"Mymatepad2023","ip":"10.2.3.23"},{"name":"n1-stable","ip":"10.2.3.29","resource":"192.168.3.0/24"}]}`
28 | if err := json.Unmarshal([]byte(sdwanStr), &sdwanInfo2); err != nil {
29 | t.Errorf("unmarshal error")
30 | return
31 | }
32 |
33 | conf.setSDWAN(sdwanInfo2)
34 | diff := conf.getDelNodes()
35 | if len(diff) != 1 && diff[0].IP != "10.2.3.4" {
36 | t.Errorf("getDelNodes error")
37 | return
38 | }
39 | sdwanInfo3 := SDWANInfo{}
40 | sdwanStr = `{"id":1312667996276071700,"name":"network1","gateway":"10.2.3.254/24","mode":"fullmesh","centralNode":"n1-stable","enable":1,"Nodes":[{"name":"222-debug","ip":"10.2.3.13"},{"name":"222stable","ip":"10.2.3.222"},{"name":"5800-debug","ip":"10.2.3.56"},{"name":"Mymatepad2023","ip":"10.2.3.23"},{"name":"n1-stable","ip":"10.2.3.29","resource":"192.168.3.0/24"}]}`
41 | if err := json.Unmarshal([]byte(sdwanStr), &sdwanInfo3); err != nil {
42 | t.Errorf("unmarshal error")
43 | return
44 | }
45 |
46 | conf.setSDWAN(sdwanInfo3)
47 | diff = conf.getDelNodes()
48 | if len(diff) != 1 && diff[0].IP != "10.2.3.60" {
49 | t.Errorf("getDelNodes error")
50 | return
51 | }
52 | // add new node
53 | sdwanInfo4 := SDWANInfo{}
54 | sdwanStr = `{"id":1312667996276071700,"name":"network1","gateway":"10.2.3.254/24","mode":"fullmesh","centralNode":"n1-stable","enable":1,"Nodes":[{"name":"222-debug","ip":"10.2.3.13"},{"name":"222stable","ip":"10.2.3.222"},{"name":"5800-debug","ip":"10.2.3.56"},{"name":"Mate60pro","ip":"10.2.3.60"},{"name":"Mymatepad2023","ip":"10.2.3.23"},{"name":"n1-stable","ip":"10.2.3.29","resource":"192.168.3.0/24"}]}`
55 | if err := json.Unmarshal([]byte(sdwanStr), &sdwanInfo4); err != nil {
56 | t.Errorf("unmarshal error")
57 | return
58 | }
59 |
60 | conf.setSDWAN(sdwanInfo4)
61 | diff = conf.getDelNodes()
62 | if len(diff) > 0 {
63 | t.Errorf("getDelNodes error")
64 | return
65 | }
66 | diff = conf.getAddNodes()
67 | if len(diff) != 1 && diff[0].IP != "10.2.3.60" {
68 | t.Errorf("getAddNodes error")
69 | return
70 | }
71 | }
72 |
73 | func TestSetSDWAN_ChangeNodeIP(t *testing.T) {
74 | conf := Config{}
75 | sdwanInfo := SDWANInfo{}
76 | sdwanStr := `{"id":1312667996276071700,"name":"network1","gateway":"10.2.3.254/24","mode":"fullmesh","centralNode":"n1-stable","enable":1,"Nodes":[{"name":"222-debug","ip":"10.2.3.13"},{"name":"222stable","ip":"10.2.3.222"},{"name":"5800-debug","ip":"10.2.3.56"},{"name":"Mate60pro","ip":"10.2.3.60"},{"name":"Mymatepad2023","ip":"10.2.3.23"},{"name":"n1-stable","ip":"10.2.3.29","resource":"192.168.3.0/24"},{"name":"tony-stable","ip":"10.2.3.4","resource":"10.1.0.0/16"}]}`
77 | if err := json.Unmarshal([]byte(sdwanStr), &sdwanInfo); err != nil {
78 | t.Errorf("unmarshal error")
79 | return
80 | }
81 |
82 | conf.setSDWAN(sdwanInfo)
83 | if len(conf.getDelNodes()) > 0 {
84 | t.Errorf("getDelNodes error")
85 | return
86 | }
87 | sdwanInfo2 := SDWANInfo{}
88 | sdwanStr = `{"id":1312667996276071700,"name":"network1","gateway":"10.2.3.254/24","mode":"fullmesh","centralNode":"n1-stable","enable":1,"Nodes":[{"name":"222-debug","ip":"10.2.3.13"},{"name":"222stable","ip":"10.2.3.222"},{"name":"5800-debug","ip":"10.2.3.56"},{"name":"Mate60pro","ip":"10.2.3.60"},{"name":"Mymatepad2023","ip":"10.2.3.23"},{"name":"n1-stable","ip":"10.2.3.29","resource":"192.168.3.0/24"},{"name":"tony-stable","ip":"10.2.3.44","resource":"10.1.0.0/16"}]}`
89 | if err := json.Unmarshal([]byte(sdwanStr), &sdwanInfo2); err != nil {
90 | t.Errorf("unmarshal error")
91 | return
92 | }
93 |
94 | conf.setSDWAN(sdwanInfo2)
95 | diff := conf.getDelNodes()
96 | if len(diff) != 1 && diff[0].IP != "10.2.3.4" {
97 | t.Errorf("getDelNodes error")
98 | return
99 | }
100 | diff = conf.getAddNodes()
101 | if len(diff) != 1 || diff[0].IP != "10.2.3.44" {
102 | t.Errorf("getAddNodes error")
103 | return
104 | }
105 | }
106 | func TestSetSDWAN_ClearAll(t *testing.T) {
107 | conf := Config{}
108 | sdwanInfo := SDWANInfo{}
109 | sdwanStr := `{"id":1312667996276071700,"name":"network1","gateway":"10.2.3.254/24","mode":"fullmesh","centralNode":"n1-stable","enable":1,"Nodes":[{"name":"222-debug","ip":"10.2.3.13"},{"name":"222stable","ip":"10.2.3.222"},{"name":"5800-debug","ip":"10.2.3.56"},{"name":"Mate60pro","ip":"10.2.3.60"},{"name":"Mymatepad2023","ip":"10.2.3.23"},{"name":"n1-stable","ip":"10.2.3.29","resource":"192.168.3.0/24"},{"name":"tony-stable","ip":"10.2.3.4","resource":"10.1.0.0/16"}]}`
110 | if err := json.Unmarshal([]byte(sdwanStr), &sdwanInfo); err != nil {
111 | t.Errorf("unmarshal error")
112 | return
113 | }
114 |
115 | conf.setSDWAN(sdwanInfo)
116 | if len(conf.getDelNodes()) > 0 {
117 | t.Errorf("getDelNodes error")
118 | return
119 | }
120 | sdwanInfo2 := SDWANInfo{}
121 | sdwanStr = `{"Nodes":null}`
122 | if err := json.Unmarshal([]byte(sdwanStr), &sdwanInfo2); err != nil {
123 | t.Errorf("unmarshal error")
124 | return
125 | }
126 |
127 | conf.setSDWAN(sdwanInfo2)
128 | diff := conf.getDelNodes()
129 | if len(diff) != 7 {
130 | t.Errorf("getDelNodes error")
131 | return
132 | }
133 | diff = conf.getAddNodes()
134 | if len(diff) != 0 {
135 | t.Errorf("getAddNodes error")
136 | return
137 | }
138 | }
139 | func TestSetSDWAN_ChangeNodeResource(t *testing.T) {
140 | conf := Config{}
141 | sdwanInfo := SDWANInfo{}
142 | sdwanStr := `{"id":1312667996276071700,"name":"network1","gateway":"10.2.3.254/24","mode":"fullmesh","centralNode":"n1-stable","enable":1,"Nodes":[{"name":"222-debug","ip":"10.2.3.13"},{"name":"222stable","ip":"10.2.3.222"},{"name":"5800-debug","ip":"10.2.3.56"},{"name":"Mate60pro","ip":"10.2.3.60"},{"name":"Mymatepad2023","ip":"10.2.3.23"},{"name":"n1-stable","ip":"10.2.3.29","resource":"192.168.3.0/24"},{"name":"tony-stable","ip":"10.2.3.4","resource":"10.1.0.0/16"}]}`
143 | if err := json.Unmarshal([]byte(sdwanStr), &sdwanInfo); err != nil {
144 | t.Errorf("unmarshal error")
145 | return
146 | }
147 |
148 | conf.setSDWAN(sdwanInfo)
149 | if len(conf.getDelNodes()) > 0 {
150 | t.Errorf("getDelNodes error")
151 | return
152 | }
153 | sdwanInfo2 := SDWANInfo{}
154 | sdwanStr = `{"id":1312667996276071700,"name":"network1","gateway":"10.2.3.254/24","mode":"fullmesh","centralNode":"n1-stable","enable":1,"Nodes":[{"name":"222-debug","ip":"10.2.3.13"},{"name":"222stable","ip":"10.2.3.222"},{"name":"5800-debug","ip":"10.2.3.56"},{"name":"Mate60pro","ip":"10.2.3.60"},{"name":"Mymatepad2023","ip":"10.2.3.23"},{"name":"n1-stable","ip":"10.2.3.29","resource":"192.168.3.0/24"},{"name":"tony-stable","ip":"10.2.3.4","resource":"10.11.0.0/16"}]}`
155 | if err := json.Unmarshal([]byte(sdwanStr), &sdwanInfo2); err != nil {
156 | t.Errorf("unmarshal error")
157 | return
158 | }
159 |
160 | conf.setSDWAN(sdwanInfo2)
161 | diff := conf.getDelNodes()
162 | if len(diff) != 1 && diff[0].IP != "10.2.3.4" {
163 | t.Errorf("getDelNodes error")
164 | return
165 | }
166 | diff = conf.getAddNodes()
167 | if len(diff) != 1 || diff[0].Resource != "10.11.0.0/16" {
168 | t.Errorf("getAddNodes error")
169 | return
170 | }
171 | }
172 |
173 | func TestInetAtoN(t *testing.T) {
174 | ipa, _ := inetAtoN("121.5.147.4")
175 | t.Log(ipa)
176 | ipa, _ = inetAtoN("121.5.147.4/32")
177 | t.Log(ipa)
178 | }
179 |
--------------------------------------------------------------------------------
/core/daemon.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "path/filepath"
7 | "time"
8 |
9 | "github.com/openp2p-cn/service"
10 | )
11 |
12 | type daemon struct {
13 | running bool
14 | proc *os.Process
15 | }
16 |
17 | func (d *daemon) Start(s service.Service) error {
18 | gLog.Println(LvINFO, "daemon start")
19 | return nil
20 | }
21 |
22 | func (d *daemon) Stop(s service.Service) error {
23 | gLog.Println(LvINFO, "service stop")
24 | d.running = false
25 | if d.proc != nil {
26 | gLog.Println(LvINFO, "stop worker")
27 | d.proc.Kill()
28 | }
29 | if service.Interactive() {
30 | gLog.Println(LvINFO, "stop daemon")
31 | os.Exit(0)
32 | }
33 | return nil
34 | }
35 |
36 | func (d *daemon) run() {
37 | gLog.Println(LvINFO, "daemon run start")
38 | defer gLog.Println(LvINFO, "daemon run end")
39 | d.running = true
40 | binPath, _ := os.Executable()
41 | mydir, err := os.Getwd()
42 | if err != nil {
43 | fmt.Println(err)
44 | }
45 | gLog.Println(LvINFO, mydir)
46 | conf := &service.Config{
47 | Name: ProductName,
48 | DisplayName: ProductName,
49 | Description: ProductName,
50 | Executable: binPath,
51 | }
52 |
53 | s, _ := service.New(d, conf)
54 | go s.Run()
55 | var args []string
56 | // rm -d parameter
57 | for i := 0; i < len(os.Args); i++ {
58 | if os.Args[i] == "-d" {
59 | args = append(os.Args[0:i], os.Args[i+1:]...)
60 | break
61 | }
62 | }
63 |
64 | args = append(args, "-nv")
65 | for {
66 | // start worker
67 | tmpDump := filepath.Join("log", "dump.log.tmp")
68 | dumpFile := filepath.Join("log", "dump.log")
69 | f, err := os.Create(filepath.Join(tmpDump))
70 | if err != nil {
71 | gLog.Printf(LvERROR, "start worker error:%s", err)
72 | return
73 | }
74 | gLog.Println(LvINFO, "start worker process, args:", args)
75 | execSpec := &os.ProcAttr{Env: append(os.Environ(), "GOTRACEBACK=crash"), Files: []*os.File{os.Stdin, os.Stdout, f}}
76 | lastRebootTime := time.Now()
77 | p, err := os.StartProcess(binPath, args, execSpec)
78 | if err != nil {
79 | gLog.Printf(LvERROR, "start worker error:%s", err)
80 | return
81 | }
82 | d.proc = p
83 | _, _ = p.Wait()
84 | f.Close()
85 | time.Sleep(time.Second)
86 | err = os.Rename(tmpDump, dumpFile)
87 | if err != nil {
88 | gLog.Printf(LvERROR, "rename dump error:%s", err)
89 | }
90 | if !d.running {
91 | return
92 | }
93 | if time.Since(lastRebootTime) < time.Second*10 {
94 | gLog.Printf(LvERROR, "worker stop, restart it after 10s")
95 | time.Sleep(time.Second * 10)
96 | }
97 |
98 | }
99 | }
100 |
101 | func (d *daemon) Control(ctrlComm string, exeAbsPath string, args []string) error {
102 | svcConfig := &service.Config{
103 | Name: ProductName,
104 | DisplayName: ProductName,
105 | Description: ProductName,
106 | Executable: exeAbsPath,
107 | Arguments: args,
108 | }
109 |
110 | s, e := service.New(d, svcConfig)
111 | if e != nil {
112 | return e
113 | }
114 | e = service.Control(s, ctrlComm)
115 | if e != nil {
116 | return e
117 | }
118 |
119 | return nil
120 | }
121 |
--------------------------------------------------------------------------------
/core/errorcode.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "errors"
5 | )
6 |
7 | // error message
8 | var (
9 | // ErrorS2S string = "s2s is not supported"
10 | // ErrorHandshake string = "handshake error"
11 | ErrorS2S = errors.New("s2s is not supported")
12 | ErrorHandshake = errors.New("handshake error")
13 | ErrorNewUser = errors.New("new user")
14 | ErrorLogin = errors.New("user or password not correct")
15 | ErrNodeTooShort = errors.New("node name too short, it must >=8 charaters")
16 | ErrReadDB = errors.New("read db error")
17 | ErrNoUpdate = errors.New("there are currently no updates available")
18 | ErrPeerOffline = errors.New("peer offline")
19 | ErrNetwork = errors.New("network error")
20 | ErrMsgFormat = errors.New("message format wrong")
21 | ErrVersionNotCompatible = errors.New("version not compatible")
22 | ErrOverlayConnDisconnect = errors.New("overlay connection is disconnected")
23 | ErrConnectRelayNode = errors.New("connect relay node error")
24 | ErrConnectPublicV4 = errors.New("connect public ipv4 error")
25 | ErrMsgChannelNotFound = errors.New("message channel not found")
26 | ErrRelayTunnelNotFound = errors.New("relay tunnel not found")
27 | ErrSymmetricLimit = errors.New("symmetric limit")
28 | ErrForceRelay = errors.New("force relay")
29 | ErrPeerConnectRelay = errors.New("peer connect relayNode error")
30 | ErrBuildTunnelBusy = errors.New("build tunnel busy")
31 | ErrMemAppTunnelNotFound = errors.New("memapp tunnel not found")
32 | ErrRemoteServiceUnable = errors.New("remote service unable")
33 | )
34 |
--------------------------------------------------------------------------------
/core/install.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "os"
7 | "os/exec"
8 | "path/filepath"
9 | "strings"
10 | "time"
11 | )
12 |
13 | func install() {
14 | gLog.Println(LvINFO, "openp2p start. version: ", OpenP2PVersion)
15 | gLog.Println(LvINFO, "Contact: QQ group 16947733, Email openp2p.cn@gmail.com")
16 | gLog.Println(LvINFO, "install start")
17 | defer gLog.Println(LvINFO, "install end")
18 | // auto uninstall
19 | err := os.MkdirAll(defaultInstallPath, 0775)
20 |
21 | if err != nil {
22 | gLog.Printf(LvERROR, "MkdirAll %s error:%s", defaultInstallPath, err)
23 | return
24 | }
25 | err = os.Chdir(defaultInstallPath)
26 | if err != nil {
27 | gLog.Println(LvERROR, "cd error:", err)
28 | return
29 | }
30 |
31 | uninstall()
32 | // save config file
33 | parseParams("install", "")
34 | targetPath := filepath.Join(defaultInstallPath, defaultBinName)
35 | d := daemon{}
36 | // copy files
37 |
38 | binPath, _ := os.Executable()
39 | src, errFiles := os.Open(binPath) // can not use args[0], on Windows call openp2p is ok(=openp2p.exe)
40 | if errFiles != nil {
41 | gLog.Printf(LvERROR, "os.OpenFile %s error:%s", os.Args[0], errFiles)
42 | return
43 | }
44 |
45 | dst, errFiles := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0775)
46 | if errFiles != nil {
47 | gLog.Printf(LvERROR, "os.OpenFile %s error:%s", targetPath, errFiles)
48 | return
49 | }
50 |
51 | _, errFiles = io.Copy(dst, src)
52 | if errFiles != nil {
53 | gLog.Printf(LvERROR, "io.Copy error:%s", errFiles)
54 | return
55 | }
56 | src.Close()
57 | dst.Close()
58 |
59 | // install system service
60 | gLog.Println(LvINFO, "targetPath:", targetPath)
61 | err = d.Control("install", targetPath, []string{"-d"})
62 | if err == nil {
63 | gLog.Println(LvINFO, "install system service ok.")
64 | }
65 | time.Sleep(time.Second * 2)
66 | err = d.Control("start", targetPath, []string{"-d"})
67 | if err != nil {
68 | gLog.Println(LvERROR, "start openp2p service error:", err)
69 | } else {
70 | gLog.Println(LvINFO, "start openp2p service ok.")
71 | }
72 | gLog.Println(LvINFO, "Visit WebUI on https://console.openp2p.cn")
73 | }
74 |
75 | func installByFilename() {
76 | params := strings.Split(filepath.Base(os.Args[0]), "-")
77 | if len(params) < 4 {
78 | return
79 | }
80 | serverHost := params[1]
81 | token := params[2]
82 | gLog.Println(LvINFO, "install start")
83 | targetPath := os.Args[0]
84 | args := []string{"install"}
85 | args = append(args, "-serverhost")
86 | args = append(args, serverHost)
87 | args = append(args, "-token")
88 | args = append(args, token)
89 | env := os.Environ()
90 | cmd := exec.Command(targetPath, args...)
91 | cmd.Stdout = os.Stdout
92 | cmd.Stderr = os.Stderr
93 | cmd.Stdin = os.Stdin
94 | cmd.Env = env
95 | err := cmd.Run()
96 | if err != nil {
97 | gLog.Println(LvERROR, "install by filename, start process error:", err)
98 | return
99 | }
100 | gLog.Println(LvINFO, "install end")
101 | gLog.Println(LvINFO, "Visit WebUI on https://console.openp2p.cn")
102 | fmt.Println("Press the Any Key to exit")
103 | fmt.Scanln()
104 | os.Exit(0)
105 | }
106 | func uninstall() {
107 | gLog.Println(LvINFO, "uninstall start")
108 | defer gLog.Println(LvINFO, "uninstall end")
109 | d := daemon{}
110 | err := d.Control("stop", "", nil)
111 | if err != nil { // service maybe not install
112 | return
113 | }
114 | err = d.Control("uninstall", "", nil)
115 | if err != nil {
116 | gLog.Println(LvERROR, "uninstall system service error:", err)
117 | } else {
118 | gLog.Println(LvINFO, "uninstall system service ok.")
119 | }
120 | binPath := filepath.Join(defaultInstallPath, defaultBinName)
121 | os.Remove(binPath + "0")
122 | os.Remove(binPath)
123 | // os.RemoveAll(defaultInstallPath) // reserve config.json
124 | }
125 |
--------------------------------------------------------------------------------
/core/iptables.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "log"
5 | "os/exec"
6 | "runtime"
7 | )
8 |
9 | func allowTunForward() {
10 | if runtime.GOOS != "linux" { // only support Linux
11 | return
12 | }
13 | exec.Command("sh", "-c", `iptables -t filter -D FORWARD -i optun -j ACCEPT`).Run()
14 | exec.Command("sh", "-c", `iptables -t filter -D FORWARD -o optun -j ACCEPT`).Run()
15 | err := exec.Command("sh", "-c", `iptables -t filter -I FORWARD -i optun -j ACCEPT`).Run()
16 | if err != nil {
17 | log.Println("allow foward in error:", err)
18 | }
19 | err = exec.Command("sh", "-c", `iptables -t filter -I FORWARD -o optun -j ACCEPT`).Run()
20 | if err != nil {
21 | log.Println("allow foward out error:", err)
22 | }
23 | }
24 |
25 | func clearSNATRule() {
26 | if runtime.GOOS != "linux" {
27 | return
28 | }
29 | execCommand("iptables", true, "-t", "nat", "-D", "POSTROUTING", "-j", "OPSDWAN")
30 | execCommand("iptables", true, "-t", "nat", "-F", "OPSDWAN")
31 | execCommand("iptables", true, "-t", "nat", "-X", "OPSDWAN")
32 | }
33 |
34 | func initSNATRule(localNet string) {
35 | if runtime.GOOS != "linux" {
36 | return
37 | }
38 | clearSNATRule()
39 |
40 | err := execCommand("iptables", true, "-t", "nat", "-N", "OPSDWAN")
41 | if err != nil {
42 | log.Println("iptables new sdwan chain error:", err)
43 | return
44 | }
45 | err = execCommand("iptables", true, "-t", "nat", "-A", "POSTROUTING", "-j", "OPSDWAN")
46 | if err != nil {
47 | log.Println("iptables append postrouting error:", err)
48 | return
49 | }
50 | err = execCommand("iptables", true, "-t", "nat", "-A", "OPSDWAN",
51 | "-o", "optun", "!", "-s", localNet, "-j", "MASQUERADE")
52 | if err != nil {
53 | log.Println("add optun snat error:", err)
54 | return
55 | }
56 | err = execCommand("iptables", true, "-t", "nat", "-A", "OPSDWAN", "!", "-o", "optun",
57 | "-s", localNet, "-j", "MASQUERADE")
58 | if err != nil {
59 | log.Println("add optun snat error:", err)
60 | return
61 | }
62 | }
63 |
64 | func addSNATRule(target string) {
65 | if runtime.GOOS != "linux" {
66 | return
67 | }
68 | err := execCommand("iptables", true, "-t", "nat", "-A", "OPSDWAN", "!", "-o", "optun",
69 | "-s", target, "-j", "MASQUERADE")
70 | if err != nil {
71 | log.Println("iptables add optun snat error:", err)
72 | return
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/core/iptree.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "fmt"
7 | "log"
8 | "net"
9 | "strings"
10 | "sync"
11 |
12 | "github.com/emirpasic/gods/trees/avltree"
13 | "github.com/emirpasic/gods/utils"
14 | )
15 |
16 | type IPTree struct {
17 | tree *avltree.Tree
18 | treeMtx sync.RWMutex
19 | }
20 | type IPTreeValue struct {
21 | maxIP uint32
22 | v interface{}
23 | }
24 |
25 | // TODO: deal interset
26 | func (iptree *IPTree) DelIntIP(minIP uint32, maxIP uint32) {
27 | iptree.tree.Remove(minIP)
28 | }
29 |
30 | // add 120k cost 0.5s
31 | func (iptree *IPTree) AddIntIP(minIP uint32, maxIP uint32, v interface{}) bool {
32 | if minIP > maxIP {
33 | return false
34 | }
35 | iptree.treeMtx.Lock()
36 | defer iptree.treeMtx.Unlock()
37 | newMinIP := minIP
38 | newMaxIP := maxIP
39 | cur := iptree.tree.Root
40 | for {
41 | if cur == nil {
42 | break
43 | }
44 | tv := cur.Value.(*IPTreeValue)
45 | curMinIP := cur.Key.(uint32)
46 |
47 | // newNode all in existNode, treat as inserted.
48 | if newMinIP >= curMinIP && newMaxIP <= tv.maxIP {
49 | return true
50 | }
51 | // has no interset
52 | if newMinIP > tv.maxIP {
53 | cur = cur.Children[1]
54 | continue
55 | }
56 | if newMaxIP < curMinIP {
57 | cur = cur.Children[0]
58 | continue
59 | }
60 | // has interset, rm it and Add the new merged ip segment
61 | iptree.tree.Remove(curMinIP)
62 | if curMinIP < newMinIP {
63 | newMinIP = curMinIP
64 | }
65 | if tv.maxIP > newMaxIP {
66 | newMaxIP = tv.maxIP
67 | }
68 | cur = iptree.tree.Root
69 | }
70 | // put in the tree
71 | iptree.tree.Put(newMinIP, &IPTreeValue{newMaxIP, v})
72 | return true
73 | }
74 |
75 | func (iptree *IPTree) Add(minIPStr string, maxIPStr string, v interface{}) bool {
76 | var minIP, maxIP uint32
77 | binary.Read(bytes.NewBuffer(net.ParseIP(minIPStr).To4()), binary.BigEndian, &minIP)
78 | binary.Read(bytes.NewBuffer(net.ParseIP(maxIPStr).To4()), binary.BigEndian, &maxIP)
79 | return iptree.AddIntIP(minIP, maxIP, v)
80 | }
81 |
82 | func (iptree *IPTree) Del(minIPStr string, maxIPStr string) {
83 | var minIP, maxIP uint32
84 | binary.Read(bytes.NewBuffer(net.ParseIP(minIPStr).To4()), binary.BigEndian, &minIP)
85 | binary.Read(bytes.NewBuffer(net.ParseIP(maxIPStr).To4()), binary.BigEndian, &maxIP)
86 | iptree.DelIntIP(minIP, maxIP)
87 | }
88 |
89 | func (iptree *IPTree) Contains(ipStr string) bool {
90 | var ip uint32
91 | binary.Read(bytes.NewBuffer(net.ParseIP(ipStr).To4()), binary.BigEndian, &ip)
92 | _, ok := iptree.Load(ip)
93 | return ok
94 | }
95 |
96 | func IsLocalhost(ipStr string) bool {
97 | if ipStr == "localhost" || ipStr == "127.0.0.1" || ipStr == "::1" {
98 | return true
99 | }
100 | return false
101 | }
102 |
103 | func (iptree *IPTree) Load(ip uint32) (interface{}, bool) {
104 | iptree.treeMtx.RLock()
105 | defer iptree.treeMtx.RUnlock()
106 | if iptree.tree == nil {
107 | return nil, false
108 | }
109 | n := iptree.tree.Root
110 | for n != nil {
111 | tv := n.Value.(*IPTreeValue)
112 | curMinIP := n.Key.(uint32)
113 | switch {
114 | case ip >= curMinIP && ip <= tv.maxIP: // hit
115 | return tv.v, true
116 | case ip < curMinIP:
117 | n = n.Children[0]
118 | default:
119 | n = n.Children[1]
120 | }
121 | }
122 | return nil, false
123 | }
124 |
125 | func (iptree *IPTree) Size() int {
126 | iptree.treeMtx.RLock()
127 | defer iptree.treeMtx.RUnlock()
128 | return iptree.tree.Size()
129 | }
130 |
131 | func (iptree *IPTree) Print() {
132 | iptree.treeMtx.RLock()
133 | defer iptree.treeMtx.RUnlock()
134 | log.Println("size:", iptree.Size())
135 | log.Println(iptree.tree.String())
136 | }
137 |
138 | func (iptree *IPTree) Clear() {
139 | iptree.treeMtx.Lock()
140 | defer iptree.treeMtx.Unlock()
141 | iptree.tree.Clear()
142 | }
143 |
144 | // input format 127.0.0.1,192.168.1.0/24,10.1.1.30-10.1.1.50
145 | // 127.0.0.1
146 | // 192.168.1.0/24
147 | // 192.168.1.1-192.168.1.10
148 | func NewIPTree(ips string) *IPTree {
149 | iptree := &IPTree{
150 | tree: avltree.NewWith(utils.UInt32Comparator),
151 | }
152 | ipArr := strings.Split(ips, ",")
153 | for _, ip := range ipArr {
154 | if strings.Contains(ip, "/") { // x.x.x.x/24
155 | _, ipNet, err := net.ParseCIDR(ip)
156 | if err != nil {
157 | fmt.Println("Error parsing CIDR:", err)
158 | continue
159 | }
160 | minIP := ipNet.IP.Mask(ipNet.Mask).String()
161 | maxIP := calculateMaxIP(ipNet).String()
162 | iptree.Add(minIP, maxIP, nil)
163 | } else if strings.Contains(ip, "-") { // x.x.x.x-y.y.y.y
164 | minAndMax := strings.Split(ip, "-")
165 | iptree.Add(minAndMax[0], minAndMax[1], nil)
166 | } else { // single ip
167 | iptree.Add(ip, ip, nil)
168 | }
169 | }
170 | return iptree
171 | }
172 | func calculateMaxIP(ipNet *net.IPNet) net.IP {
173 | maxIP := make(net.IP, len(ipNet.IP))
174 | copy(maxIP, ipNet.IP)
175 | for i := range maxIP {
176 | maxIP[i] |= ^ipNet.Mask[i]
177 | }
178 | return maxIP
179 | }
180 |
--------------------------------------------------------------------------------
/core/iptree_test.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "net"
7 | "testing"
8 | "time"
9 | )
10 |
11 | func wrapTestContains(t *testing.T, iptree *IPTree, ip string, result bool) {
12 | if iptree.Contains(ip) == result {
13 | // t.Logf("compare version %s %s ok\n", v1, v2)
14 | } else {
15 | t.Errorf("test %s fail\n", ip)
16 | }
17 | }
18 | func wrapBenchmarkContains(t *testing.B, iptree *IPTree, ip string, result bool) {
19 | if iptree.Contains(ip) == result {
20 | // t.Logf("compare version %s %s ok\n", v1, v2)
21 | } else {
22 | t.Errorf("test %s fail\n", ip)
23 | }
24 | }
25 |
26 | func TestAllInputFormat(t *testing.T) {
27 | iptree := NewIPTree("219.137.185.70,127.0.0.1,127.0.0.0/8,192.168.1.0/24,192.168.3.100-192.168.3.255,192.168.100.0-192.168.200.255")
28 | wrapTestContains(t, iptree, "127.0.0.1", true)
29 | wrapTestContains(t, iptree, "127.0.0.2", true)
30 | wrapTestContains(t, iptree, "127.1.1.1", true)
31 | wrapTestContains(t, iptree, "219.137.185.70", true)
32 | wrapTestContains(t, iptree, "219.137.185.71", false)
33 | wrapTestContains(t, iptree, "192.168.1.2", true)
34 | wrapTestContains(t, iptree, "192.168.2.2", false)
35 | wrapTestContains(t, iptree, "192.168.3.1", false)
36 | wrapTestContains(t, iptree, "192.168.3.100", true)
37 | wrapTestContains(t, iptree, "192.168.3.255", true)
38 | wrapTestContains(t, iptree, "192.168.150.1", true)
39 | wrapTestContains(t, iptree, "192.168.250.1", false)
40 | }
41 |
42 | func TestSingleIP(t *testing.T) {
43 | iptree := NewIPTree("")
44 | iptree.Add("219.137.185.70", "219.137.185.70", nil)
45 | wrapTestContains(t, iptree, "219.137.185.70", true)
46 | wrapTestContains(t, iptree, "219.137.185.71", false)
47 | }
48 |
49 | func TestWrongSegment(t *testing.T) {
50 | iptree := NewIPTree("")
51 | inserted := iptree.Add("87.251.75.0", "82.251.75.255", nil)
52 | if inserted {
53 | t.Errorf("TestWrongSegment failed\n")
54 | }
55 | }
56 |
57 | func TestSegment2(t *testing.T) {
58 | iptree := NewIPTree("")
59 | iptree.Clear()
60 | iptree.Add("10.1.5.50", "10.1.5.100", nil)
61 | iptree.Add("10.1.1.50", "10.1.1.100", nil)
62 | iptree.Add("10.1.2.50", "10.1.2.100", nil)
63 | iptree.Add("10.1.6.50", "10.1.6.100", nil)
64 | iptree.Add("10.1.7.50", "10.1.7.100", nil)
65 | iptree.Add("10.1.3.50", "10.1.3.100", nil)
66 | iptree.Add("10.1.1.1", "10.1.1.10", nil) // no interset
67 | iptree.Add("10.1.1.200", "10.1.1.250", nil) // no interset
68 | iptree.Print()
69 |
70 | iptree.Add("10.1.1.80", "10.1.1.90", nil) // all in
71 | iptree.Add("10.1.1.40", "10.1.1.60", nil) // interset
72 | iptree.Print()
73 | iptree.Add("10.1.1.90", "10.1.1.110", nil) // interset
74 | iptree.Print()
75 | t.Logf("ipTree size:%d\n", iptree.Size())
76 | wrapTestContains(t, iptree, "10.1.1.40", true)
77 | wrapTestContains(t, iptree, "10.1.5.50", true)
78 | wrapTestContains(t, iptree, "10.1.6.50", true)
79 | wrapTestContains(t, iptree, "10.1.7.50", true)
80 | wrapTestContains(t, iptree, "10.1.2.50", true)
81 | wrapTestContains(t, iptree, "10.1.3.50", true)
82 | wrapTestContains(t, iptree, "10.1.1.60", true)
83 | wrapTestContains(t, iptree, "10.1.1.90", true)
84 | wrapTestContains(t, iptree, "10.1.1.110", true)
85 | wrapTestContains(t, iptree, "10.1.1.250", true)
86 | wrapTestContains(t, iptree, "10.1.2.60", true)
87 | wrapTestContains(t, iptree, "10.1.100.30", false)
88 | wrapTestContains(t, iptree, "10.1.200.30", false)
89 |
90 | iptree.Add("10.0.0.0", "10.255.255.255", nil) // will merge all segment
91 | iptree.Print()
92 | if iptree.Size() != 1 {
93 | t.Errorf("merge ip segment error\n")
94 | }
95 |
96 | }
97 |
98 | func BenchmarkBuildipTree20k(t *testing.B) {
99 | iptree := NewIPTree("")
100 | iptree.Clear()
101 | iptree.Add("10.1.5.50", "10.1.5.100", nil)
102 | iptree.Add("10.1.1.50", "10.1.1.100", nil)
103 | iptree.Add("10.1.2.50", "10.1.2.100", nil)
104 | iptree.Add("10.1.6.50", "10.1.6.100", nil)
105 | iptree.Add("10.1.7.50", "10.1.7.100", nil)
106 | iptree.Add("10.1.3.50", "10.1.3.100", nil)
107 | iptree.Add("10.1.1.1", "10.1.1.10", nil) // no interset
108 | iptree.Add("10.1.1.200", "10.1.1.250", nil) // no interset
109 | iptree.Add("10.1.1.80", "10.1.1.90", nil) // all in
110 | iptree.Add("10.1.1.40", "10.1.1.60", nil) // interset
111 | iptree.Add("10.1.1.90", "10.1.1.110", nil) // interset
112 | var minIP uint32
113 | binary.Read(bytes.NewBuffer(net.ParseIP("10.1.1.1").To4()), binary.BigEndian, &minIP)
114 |
115 | // insert 10k block ip single
116 | nodeNum := uint32(10000 * 1)
117 | gap := uint32(10)
118 | for i := minIP; i < minIP+nodeNum*gap; i += gap {
119 | iptree.AddIntIP(i, i, nil)
120 | // t.Logf("ipTree size:%d\n", iptree.Size())
121 | }
122 | binary.Read(bytes.NewBuffer(net.ParseIP("100.1.1.1").To4()), binary.BigEndian, &minIP)
123 | // insert 100k block ip segment
124 | for i := minIP; i < minIP+nodeNum*gap; i += gap {
125 | iptree.AddIntIP(i, i+5, nil)
126 | }
127 | t.Logf("ipTree size:%d\n", iptree.Size())
128 | iptree.Clear()
129 | t.Logf("clear. ipTree size:%d\n", iptree.Size())
130 | }
131 | func BenchmarkQuery(t *testing.B) {
132 | ts := time.Now()
133 | iptree := NewIPTree("")
134 | iptree.Clear()
135 | iptree.Add("10.1.5.50", "10.1.5.100", nil)
136 | iptree.Add("10.1.1.50", "10.1.1.100", nil)
137 | iptree.Add("10.1.2.50", "10.1.2.100", nil)
138 | iptree.Add("10.1.6.50", "10.1.6.100", nil)
139 | iptree.Add("10.1.7.50", "10.1.7.100", nil)
140 | iptree.Add("10.1.3.50", "10.1.3.100", nil)
141 | iptree.Add("10.1.1.1", "10.1.1.10", nil) // no interset
142 | iptree.Add("10.1.1.200", "10.1.1.250", nil) // no interset
143 | iptree.Add("10.1.1.80", "10.1.1.90", nil) // all in
144 | iptree.Add("10.1.1.40", "10.1.1.60", nil) // interset
145 | iptree.Add("10.1.1.90", "10.1.1.110", nil) // interset
146 | var minIP uint32
147 | binary.Read(bytes.NewBuffer(net.ParseIP("10.1.1.1").To4()), binary.BigEndian, &minIP)
148 |
149 | // insert 10k block ip single
150 | nodeNum := uint32(10000 * 1000)
151 | gap := uint32(10)
152 | for i := minIP; i < minIP+nodeNum*gap; i += gap {
153 | iptree.AddIntIP(i, i, nil)
154 | // t.Logf("ipTree size:%d\n", iptree.Size())
155 | }
156 | binary.Read(bytes.NewBuffer(net.ParseIP("100.1.1.1").To4()), binary.BigEndian, &minIP)
157 | // insert 100k block ip segment
158 | for i := minIP; i < minIP+nodeNum*gap; i += gap {
159 | iptree.AddIntIP(i, i+5, nil)
160 | }
161 | t.Logf("ipTree size:%d cost:%dms\n", iptree.Size(), time.Since(ts)/time.Millisecond)
162 | ts = time.Now()
163 | // t.ResetTimer()
164 | queryNum := 100 * 10000
165 | for i := 0; i < queryNum; i++ {
166 | iptree.Load(minIP + uint32(i))
167 | wrapBenchmarkContains(t, iptree, "10.1.5.55", true)
168 | wrapBenchmarkContains(t, iptree, "10.1.1.1", true)
169 | wrapBenchmarkContains(t, iptree, "10.1.5.200", false)
170 | wrapBenchmarkContains(t, iptree, "200.1.1.1", false)
171 | }
172 | t.Logf("query num:%d cost:%dms\n", queryNum*4, time.Since(ts)/time.Millisecond)
173 |
174 | }
175 |
--------------------------------------------------------------------------------
/core/log.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "log"
5 | "os"
6 | "runtime"
7 | "sync"
8 | "time"
9 | )
10 |
11 | type LogLevel int
12 |
13 | var gLog *logger
14 |
15 | const (
16 | LvDev LogLevel = -1
17 | LvDEBUG LogLevel = iota
18 | LvINFO
19 | LvWARN
20 | LvERROR
21 | )
22 |
23 | var (
24 | logFileNames map[LogLevel]string
25 | loglevel map[LogLevel]string
26 | )
27 |
28 | func init() {
29 | logFileNames = make(map[LogLevel]string)
30 | loglevel = make(map[LogLevel]string)
31 | logFileNames[0] = ".log"
32 | loglevel[LvDEBUG] = "DEBUG"
33 | loglevel[LvINFO] = "INFO"
34 | loglevel[LvWARN] = "WARN"
35 | loglevel[LvERROR] = "ERROR"
36 | loglevel[LvDev] = "Dev"
37 |
38 | }
39 |
40 | const (
41 | LogFile = 1
42 | LogConsole = 1 << 1
43 | )
44 |
45 | type logger struct {
46 | loggers map[LogLevel]*log.Logger
47 | files map[LogLevel]*os.File
48 | level LogLevel
49 | logDir string
50 | mtx *sync.Mutex
51 | lineEnding string
52 | pid int
53 | maxLogSize int64
54 | mode int
55 | stdLogger *log.Logger
56 | }
57 |
58 | func NewLogger(path string, filePrefix string, level LogLevel, maxLogSize int64, mode int) *logger {
59 | loggers := make(map[LogLevel]*log.Logger)
60 | logfiles := make(map[LogLevel]*os.File)
61 | var (
62 | logdir string
63 | )
64 | if path == "" {
65 | logdir = "log/"
66 | } else {
67 | logdir = path + "/log/"
68 | }
69 | os.MkdirAll(logdir, 0777)
70 | for lv := range logFileNames {
71 | logFilePath := logdir + filePrefix + logFileNames[lv]
72 | f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
73 | if err != nil {
74 | log.Fatal(err)
75 | }
76 | os.Chmod(logFilePath, 0644)
77 | logfiles[lv] = f
78 | loggers[lv] = log.New(f, "", log.LstdFlags|log.Lmicroseconds)
79 | }
80 | var le string
81 | if runtime.GOOS == "windows" {
82 | le = "\r\n"
83 | } else {
84 | le = "\n"
85 | }
86 | pLog := &logger{loggers, logfiles, level, logdir, &sync.Mutex{}, le, os.Getpid(), maxLogSize, mode, log.New(os.Stdout, "", 0)}
87 | pLog.stdLogger.SetFlags(log.LstdFlags | log.Lmicroseconds)
88 | go pLog.checkFile()
89 | return pLog
90 | }
91 |
92 | func (l *logger) setLevel(level LogLevel) {
93 | l.mtx.Lock()
94 | defer l.mtx.Unlock()
95 | l.level = level
96 | }
97 |
98 | func (l *logger) setMaxSize(size int64) {
99 | l.mtx.Lock()
100 | defer l.mtx.Unlock()
101 | l.maxLogSize = size
102 | }
103 |
104 | func (l *logger) setMode(mode int) {
105 | l.mtx.Lock()
106 | defer l.mtx.Unlock()
107 | l.mode = mode
108 | }
109 |
110 | func (l *logger) checkFile() {
111 | if l.maxLogSize <= 0 {
112 | return
113 | }
114 | ticker := time.NewTicker(time.Minute)
115 | for {
116 | select {
117 | case <-ticker.C:
118 | l.mtx.Lock()
119 | for lv, logFile := range l.files {
120 | f, e := logFile.Stat()
121 | if e != nil {
122 | continue
123 | }
124 | if f.Size() <= l.maxLogSize {
125 | continue
126 | }
127 | logFile.Close()
128 | fname := f.Name()
129 | backupPath := l.logDir + fname + ".0"
130 | os.Remove(backupPath)
131 | os.Rename(l.logDir+fname, backupPath)
132 | newFile, e := os.OpenFile(l.logDir+fname, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
133 | if e == nil {
134 | l.loggers[lv].SetOutput(newFile)
135 | l.files[lv] = newFile
136 | }
137 | }
138 | l.mtx.Unlock()
139 | }
140 | }
141 | }
142 |
143 | func (l *logger) Printf(level LogLevel, format string, params ...interface{}) {
144 | l.mtx.Lock()
145 | defer l.mtx.Unlock()
146 | if level < l.level {
147 | return
148 | }
149 | pidAndLevel := []interface{}{l.pid, loglevel[level]}
150 | params = append(pidAndLevel, params...)
151 | if l.mode&LogFile != 0 {
152 | l.loggers[0].Printf("%d %s "+format+l.lineEnding, params...)
153 | }
154 | if l.mode&LogConsole != 0 {
155 | l.stdLogger.Printf("%d %s "+format+l.lineEnding, params...)
156 | }
157 | }
158 |
159 | func (l *logger) Println(level LogLevel, params ...interface{}) {
160 | l.mtx.Lock()
161 | defer l.mtx.Unlock()
162 | if level < l.level {
163 | return
164 | }
165 | pidAndLevel := []interface{}{l.pid, " ", loglevel[level], " "}
166 | params = append(pidAndLevel, params...)
167 | params = append(params, l.lineEnding)
168 | if l.mode&LogFile != 0 {
169 | l.loggers[0].Print(params...)
170 | }
171 | if l.mode&LogConsole != 0 {
172 | l.stdLogger.Print(params...)
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/core/nat.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "math/rand"
7 | "net"
8 | "strconv"
9 | "strings"
10 | "time"
11 |
12 | reuse "github.com/openp2p-cn/go-reuseport"
13 | )
14 |
15 | func natTCP(serverHost string, serverPort int) (publicIP string, publicPort int, localPort int) {
16 | // dialer := &net.Dialer{
17 | // LocalAddr: &net.TCPAddr{
18 | // IP: net.ParseIP("0.0.0.0"),
19 | // Port: localPort,
20 | // },
21 | // }
22 | conn, err := reuse.DialTimeout("tcp4", fmt.Sprintf("%s:%d", "0.0.0.0", 0), fmt.Sprintf("%s:%d", serverHost, serverPort), NatTestTimeout)
23 | // conn, err := net.Dial("tcp4", fmt.Sprintf("%s:%d", serverHost, serverPort))
24 | // log.Println(LvINFO, conn.LocalAddr())
25 | if err != nil {
26 | fmt.Printf("Dial tcp4 %s:%d error:%s", serverHost, serverPort, err)
27 | return
28 | }
29 | defer conn.Close()
30 | localPort, _ = strconv.Atoi(strings.Split(conn.LocalAddr().String(), ":")[1])
31 | _, wrerr := conn.Write([]byte("1"))
32 | if wrerr != nil {
33 | fmt.Printf("Write error: %s\n", wrerr)
34 | return
35 | }
36 | b := make([]byte, 1000)
37 | conn.SetReadDeadline(time.Now().Add(NatTestTimeout))
38 | n, rderr := conn.Read(b)
39 | if rderr != nil {
40 | fmt.Printf("Read error: %s\n", rderr)
41 | return
42 | }
43 | arr := strings.Split(string(b[:n]), ":")
44 | if len(arr) < 2 {
45 | return
46 | }
47 | publicIP = arr[0]
48 | port, _ := strconv.ParseInt(arr[1], 10, 32)
49 | publicPort = int(port)
50 | return
51 |
52 | }
53 | func natTest(serverHost string, serverPort int, localPort int) (publicIP string, publicPort int, err error) {
54 | gLog.Println(LvDEBUG, "natTest start")
55 | defer gLog.Println(LvDEBUG, "natTest end")
56 | conn, err := net.ListenPacket("udp", fmt.Sprintf(":%d", localPort))
57 | if err != nil {
58 | gLog.Println(LvERROR, "natTest listen udp error:", err)
59 | return "", 0, err
60 | }
61 | defer conn.Close()
62 |
63 | dst, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", serverHost, serverPort))
64 | if err != nil {
65 | return "", 0, err
66 | }
67 |
68 | // The connection can write data to the desired address.
69 | msg, err := newMessage(MsgNATDetect, MsgNAT, nil)
70 | _, err = conn.WriteTo(msg, dst)
71 | if err != nil {
72 | return "", 0, err
73 | }
74 | deadline := time.Now().Add(NatTestTimeout)
75 | err = conn.SetReadDeadline(deadline)
76 | if err != nil {
77 | return "", 0, err
78 | }
79 | buffer := make([]byte, 1024)
80 | nRead, _, err := conn.ReadFrom(buffer)
81 | if err != nil {
82 | gLog.Println(LvERROR, "NAT detect error:", err)
83 | return "", 0, err
84 | }
85 | natRsp := NatDetectRsp{}
86 | json.Unmarshal(buffer[openP2PHeaderSize:nRead], &natRsp)
87 |
88 | return natRsp.IP, natRsp.Port, nil
89 | }
90 |
91 | func getNATType(host string, udp1 int, udp2 int) (publicIP string, NATType int, err error) {
92 | // the random local port may be used by other.
93 | localPort := int(rand.Uint32()%15000 + 50000)
94 |
95 | ip1, port1, err := natTest(host, udp1, localPort)
96 | if err != nil {
97 | return "", 0, err
98 | }
99 | _, port2, err := natTest(host, udp2, localPort) // 2rd nat test not need testing publicip
100 | gLog.Printf(LvDEBUG, "local port:%d nat port:%d", localPort, port2)
101 | if err != nil {
102 | return "", 0, err
103 | }
104 | natType := NATSymmetric
105 | if port1 == port2 {
106 | natType = NATCone
107 | }
108 | return ip1, natType, nil
109 | }
110 |
111 | func publicIPTest(publicIP string, echoPort int) (hasPublicIP int, hasUPNPorNATPMP int) {
112 | if publicIP == "" || echoPort == 0 {
113 | return
114 | }
115 | var echoConn *net.UDPConn
116 | gLog.Println(LvDEBUG, "echo server start")
117 | var err error
118 | echoConn, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: echoPort})
119 | if err != nil { // listen error
120 | gLog.Println(LvERROR, "echo server listen error:", err)
121 | return
122 | }
123 | defer echoConn.Close()
124 | // testing for public ip
125 | for i := 0; i < 2; i++ {
126 | if i == 1 {
127 | // test upnp or nat-pmp
128 | gLog.Println(LvDEBUG, "upnp test start")
129 | nat, err := Discover()
130 | if err != nil || nat == nil {
131 | gLog.Println(LvDEBUG, "could not perform UPNP discover:", err)
132 | break
133 | }
134 | ext, err := nat.GetExternalAddress()
135 | if err != nil {
136 | gLog.Println(LvDEBUG, "could not perform UPNP external address:", err)
137 | break
138 | }
139 | gLog.Println(LvINFO, "PublicIP:", ext)
140 |
141 | externalPort, err := nat.AddPortMapping("udp", echoPort, echoPort, "openp2p", 30) // 30 seconds fot upnp testing
142 | if err != nil {
143 | gLog.Println(LvDEBUG, "could not add udp UPNP port mapping", externalPort)
144 | break
145 | } else {
146 | nat.AddPortMapping("tcp", echoPort, echoPort, "openp2p", 604800) // 7 days for tcp connection
147 | }
148 | }
149 | gLog.Printf(LvDEBUG, "public ip test start %s:%d", publicIP, echoPort)
150 | conn, err := net.ListenUDP("udp", nil)
151 | if err != nil {
152 | break
153 | }
154 | defer conn.Close()
155 | dst, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", gConf.Network.ServerHost, gConf.Network.ServerPort))
156 | if err != nil {
157 | break
158 | }
159 |
160 | // The connection can write data to the desired address.
161 | msg, _ := newMessage(MsgNATDetect, MsgPublicIP, NatDetectReq{EchoPort: echoPort})
162 | _, err = conn.WriteTo(msg, dst)
163 | if err != nil {
164 | continue
165 | }
166 | buf := make([]byte, 1600)
167 |
168 | // wait for echo testing
169 | echoConn.SetReadDeadline(time.Now().Add(PublicIPEchoTimeout))
170 | nRead, _, err := echoConn.ReadFromUDP(buf)
171 | if err != nil {
172 | gLog.Println(LvDEBUG, "PublicIP detect error:", err)
173 | continue
174 | }
175 | natRsp := NatDetectRsp{}
176 | err = json.Unmarshal(buf[openP2PHeaderSize:nRead], &natRsp)
177 | if err != nil {
178 | gLog.Println(LvDEBUG, "PublicIP detect error:", err)
179 | continue
180 | }
181 | if natRsp.Port == echoPort {
182 | if i == 1 {
183 | gLog.Println(LvDEBUG, "UPNP or NAT-PMP:YES")
184 | hasUPNPorNATPMP = 1
185 | } else {
186 | gLog.Println(LvDEBUG, "public ip:YES")
187 | hasPublicIP = 1
188 | }
189 | break
190 | }
191 | }
192 | return
193 | }
194 |
--------------------------------------------------------------------------------
/core/openp2p.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "fmt"
5 | "math/rand"
6 | "os"
7 | "path/filepath"
8 | "strconv"
9 | "time"
10 | )
11 |
12 | var GNetwork *P2PNetwork
13 |
14 | func Run() {
15 | rand.Seed(time.Now().UnixNano())
16 | baseDir := filepath.Dir(os.Args[0])
17 | os.Chdir(baseDir) // for system service
18 | gLog = NewLogger(baseDir, ProductName, LvDEBUG, 1024*1024, LogFile|LogConsole)
19 | if len(os.Args) > 1 {
20 | switch os.Args[1] {
21 | case "version", "-v", "--version":
22 | fmt.Println(OpenP2PVersion)
23 | return
24 | case "install":
25 | install()
26 | return
27 | case "uninstall":
28 | uninstall()
29 | return
30 | }
31 | } else {
32 | installByFilename()
33 | }
34 | parseParams("", "")
35 | gLog.Println(LvINFO, "openp2p start. version: ", OpenP2PVersion)
36 | gLog.Println(LvINFO, "Contact: QQ group 16947733, Email openp2p.cn@gmail.com")
37 |
38 | if gConf.daemonMode {
39 | d := daemon{}
40 | d.run()
41 | return
42 | }
43 |
44 | gLog.Println(LvINFO, &gConf)
45 | setFirewall()
46 | err := setRLimit()
47 | if err != nil {
48 | gLog.Println(LvINFO, "setRLimit error:", err)
49 | }
50 | GNetwork = P2PNetworkInstance()
51 | if ok := GNetwork.Connect(30000); !ok {
52 | gLog.Println(LvERROR, "P2PNetwork login error")
53 | return
54 | }
55 | // gLog.Println(LvINFO, "waiting for connection...")
56 | forever := make(chan bool)
57 | <-forever
58 | }
59 |
60 | // for Android app
61 | // gomobile not support uint64 exported to java
62 |
63 | func RunAsModule(baseDir string, token string, bw int, logLevel int) *P2PNetwork {
64 | rand.Seed(time.Now().UnixNano())
65 | os.Chdir(baseDir) // for system service
66 | gLog = NewLogger(baseDir, ProductName, LvINFO, 1024*1024, LogFile|LogConsole)
67 |
68 | parseParams("", "")
69 |
70 | n, err := strconv.ParseUint(token, 10, 64)
71 | if err == nil && n > 0 {
72 | gConf.setToken(n)
73 | }
74 | if n <= 0 && gConf.Network.Token == 0 { // not input token
75 | return nil
76 | }
77 | // gLog.setLevel(LogLevel(logLevel))
78 | gConf.setShareBandwidth(bw)
79 | gLog.Println(LvINFO, "openp2p start. version: ", OpenP2PVersion)
80 | gLog.Println(LvINFO, "Contact: QQ group 16947733, Email openp2p.cn@gmail.com")
81 | gLog.Println(LvINFO, &gConf)
82 |
83 | GNetwork = P2PNetworkInstance()
84 | if ok := GNetwork.Connect(30000); !ok {
85 | gLog.Println(LvERROR, "P2PNetwork login error")
86 | return nil
87 | }
88 | // gLog.Println(LvINFO, "waiting for connection...")
89 | return GNetwork
90 | }
91 |
92 | func RunCmd(cmd string) {
93 | rand.Seed(time.Now().UnixNano())
94 | baseDir := filepath.Dir(os.Args[0])
95 | os.Chdir(baseDir) // for system service
96 | gLog = NewLogger(baseDir, ProductName, LvINFO, 1024*1024, LogFile|LogConsole)
97 |
98 | parseParams("", cmd)
99 | setFirewall()
100 | err := setRLimit()
101 | if err != nil {
102 | gLog.Println(LvINFO, "setRLimit error:", err)
103 | }
104 | GNetwork = P2PNetworkInstance()
105 | if ok := GNetwork.Connect(30000); !ok {
106 | gLog.Println(LvERROR, "P2PNetwork login error")
107 | return
108 | }
109 | forever := make(chan bool)
110 | <-forever
111 | }
112 |
113 | func GetToken(baseDir string) string {
114 | os.Chdir(baseDir)
115 | gConf.load()
116 | return fmt.Sprintf("%d", gConf.Network.Token)
117 | }
118 |
119 | func Stop() {
120 | os.Exit(0)
121 | }
122 |
--------------------------------------------------------------------------------
/core/optun.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "github.com/openp2p-cn/wireguard-go/tun"
5 | )
6 |
7 | var AndroidSDWANConfig chan []byte
8 |
9 | type optun struct {
10 | tunName string
11 | dev tun.Device
12 | }
13 |
14 | func (t *optun) Stop() error {
15 | t.dev.Close()
16 | return nil
17 | }
18 | func init() {
19 | AndroidSDWANConfig = make(chan []byte, 1)
20 | }
21 |
--------------------------------------------------------------------------------
/core/optun_android.go:
--------------------------------------------------------------------------------
1 | // optun_android.go
2 | //go:build android
3 | // +build android
4 |
5 | package openp2p
6 |
7 | import (
8 | "net"
9 | )
10 |
11 | const (
12 | tunIfaceName = "optun"
13 | PIHeaderSize = 0
14 | )
15 |
16 | var AndroidReadTun chan []byte // TODO: multi channel
17 | var AndroidWriteTun chan []byte
18 |
19 | func (t *optun) Start(localAddr string, detail *SDWANInfo) error {
20 |
21 | return nil
22 | }
23 |
24 | func (t *optun) Read(bufs [][]byte, sizes []int, offset int) (n int, err error) {
25 | bufs[0] = <-AndroidReadTun
26 | sizes[0] = len(bufs[0])
27 | return 1, nil
28 | }
29 |
30 | func (t *optun) Write(bufs [][]byte, offset int) (int, error) {
31 | AndroidWriteTun <- bufs[0]
32 | return len(bufs[0]), nil
33 | }
34 |
35 | func AndroidRead(data []byte, len int) {
36 | head := PacketHeader{}
37 | parseHeader(data, &head)
38 | gLog.Printf(LvDev, "AndroidRead tun dst ip=%s,len=%d", net.IP{byte(head.dst >> 24), byte(head.dst >> 16), byte(head.dst >> 8), byte(head.dst)}.String(), len)
39 | buf := make([]byte, len)
40 | copy(buf, data)
41 | AndroidReadTun <- buf
42 | }
43 |
44 | func AndroidWrite(buf []byte) int {
45 | p := <-AndroidWriteTun
46 | copy(buf, p)
47 | return len(p)
48 | }
49 |
50 | func GetAndroidSDWANConfig(buf []byte) int {
51 | p := <-AndroidSDWANConfig
52 | copy(buf, p)
53 | gLog.Printf(LvINFO, "AndroidSDWANConfig=%s", p)
54 | return len(p)
55 | }
56 |
57 | func GetAndroidNodeName() string {
58 | gLog.Printf(LvINFO, "GetAndroidNodeName=%s", gConf.Network.Node)
59 | return gConf.Network.Node
60 | }
61 |
62 | func setTunAddr(ifname, localAddr, remoteAddr string, wintun interface{}) error {
63 | // TODO:
64 | return nil
65 | }
66 |
67 | func addRoute(dst, gw, ifname string) error {
68 | // TODO:
69 | return nil
70 | }
71 |
72 | func delRoute(dst, gw string) error {
73 | // TODO:
74 | return nil
75 | }
76 |
77 | func delRoutesByGateway(gateway string) error {
78 | // TODO:
79 | return nil
80 | }
81 |
82 | func init() {
83 | AndroidReadTun = make(chan []byte, 1000)
84 | AndroidWriteTun = make(chan []byte, 1000)
85 | }
86 |
--------------------------------------------------------------------------------
/core/optun_darwin.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | "os/exec"
7 | "strings"
8 |
9 | "github.com/openp2p-cn/wireguard-go/tun"
10 | )
11 |
12 | const (
13 | tunIfaceName = "utun"
14 | PIHeaderSize = 4 // utun has no IFF_NO_PI
15 | )
16 |
17 | func (t *optun) Start(localAddr string, detail *SDWANInfo) error {
18 | var err error
19 | t.tunName = tunIfaceName
20 | t.dev, err = tun.CreateTUN(t.tunName, 1420)
21 | if err != nil {
22 | return err
23 | }
24 | t.tunName, _ = t.dev.Name()
25 | return nil
26 | }
27 |
28 | func (t *optun) Read(bufs [][]byte, sizes []int, offset int) (n int, err error) {
29 | return t.dev.Read(bufs, sizes, offset)
30 | }
31 |
32 | func (t *optun) Write(bufs [][]byte, offset int) (int, error) {
33 | return t.dev.Write(bufs, offset)
34 | }
35 |
36 | func setTunAddr(ifname, localAddr, remoteAddr string, wintun interface{}) error {
37 | li, _, err := net.ParseCIDR(localAddr)
38 | if err != nil {
39 | return fmt.Errorf("parse local addr fail:%s", err)
40 | }
41 | ri, _, err := net.ParseCIDR(remoteAddr)
42 | if err != nil {
43 | return fmt.Errorf("parse remote addr fail:%s", err)
44 | }
45 | err = exec.Command("ifconfig", ifname, "inet", li.String(), ri.String(), "up").Run()
46 | return err
47 | }
48 |
49 | func addRoute(dst, gw, ifname string) error {
50 | err := exec.Command("route", "add", dst, gw).Run()
51 | return err
52 | }
53 |
54 | func delRoute(dst, gw string) error {
55 | err := exec.Command("route", "delete", dst, "-gateway", gw).Run()
56 | return err
57 | }
58 | func delRoutesByGateway(gateway string) error {
59 | cmd := exec.Command("netstat", "-rn")
60 | output, err := cmd.Output()
61 | if err != nil {
62 | return err
63 | }
64 |
65 | lines := strings.Split(string(output), "\n")
66 | for _, line := range lines {
67 | if !strings.Contains(line, gateway) {
68 | continue
69 | }
70 | fields := strings.Fields(line)
71 | if len(fields) >= 2 {
72 | cmd := exec.Command("route", "delete", fields[0], gateway)
73 | err := cmd.Run()
74 | if err != nil {
75 | gLog.Printf(LvERROR, "Delete route %s error:%s", fields[0], err)
76 | continue
77 | }
78 | gLog.Printf(LvINFO, "Delete route ok: %s %s\n", fields[0], gateway)
79 | }
80 | }
81 | return nil
82 | }
83 | func addTunAddr(localAddr, remoteAddr string) error {
84 | return nil
85 | }
86 | func delTunAddr(localAddr, remoteAddr string) error {
87 | return nil
88 | }
89 |
--------------------------------------------------------------------------------
/core/optun_linux.go:
--------------------------------------------------------------------------------
1 | //go:build !android
2 | // +build !android
3 |
4 | // optun_linux.go
5 | package openp2p
6 |
7 | import (
8 | "fmt"
9 | "net"
10 | "os/exec"
11 | "strings"
12 |
13 | "github.com/openp2p-cn/wireguard-go/tun"
14 | "github.com/vishvananda/netlink"
15 | )
16 |
17 | const (
18 | tunIfaceName = "optun"
19 | PIHeaderSize = 0
20 | )
21 |
22 | var previousIP = ""
23 |
24 | func (t *optun) Start(localAddr string, detail *SDWANInfo) error {
25 | var err error
26 | t.tunName = tunIfaceName
27 | t.dev, err = tun.CreateTUN(t.tunName, 1420)
28 | if err != nil {
29 | return err
30 | }
31 | return nil
32 | }
33 |
34 | func (t *optun) Read(bufs [][]byte, sizes []int, offset int) (n int, err error) {
35 | return t.dev.Read(bufs, sizes, offset)
36 | }
37 |
38 | func (t *optun) Write(bufs [][]byte, offset int) (int, error) {
39 | return t.dev.Write(bufs, offset)
40 | }
41 |
42 | func setTunAddr(ifname, localAddr, remoteAddr string, wintun interface{}) error {
43 | ifce, err := netlink.LinkByName(ifname)
44 | if err != nil {
45 | return err
46 | }
47 | netlink.LinkSetMTU(ifce, 1375)
48 | netlink.LinkSetTxQLen(ifce, 100)
49 | netlink.LinkSetUp(ifce)
50 |
51 | ln, err := netlink.ParseIPNet(localAddr)
52 | if err != nil {
53 | return err
54 | }
55 | ln.Mask = net.CIDRMask(32, 32)
56 | rn, err := netlink.ParseIPNet(remoteAddr)
57 | if err != nil {
58 | return err
59 | }
60 | rn.Mask = net.CIDRMask(32, 32)
61 |
62 | addr := &netlink.Addr{
63 | IPNet: ln,
64 | Peer: rn,
65 | }
66 | if previousIP != "" {
67 | lnDel, err := netlink.ParseIPNet(previousIP)
68 | if err != nil {
69 | return err
70 | }
71 | lnDel.Mask = net.CIDRMask(32, 32)
72 |
73 | addrDel := &netlink.Addr{
74 | IPNet: lnDel,
75 | Peer: rn,
76 | }
77 | netlink.AddrDel(ifce, addrDel)
78 | }
79 | previousIP = localAddr
80 | return netlink.AddrAdd(ifce, addr)
81 | }
82 |
83 | func addRoute(dst, gw, ifname string) error {
84 | _, networkid, err := net.ParseCIDR(dst)
85 | if err != nil {
86 | return err
87 | }
88 | ipGW := net.ParseIP(gw)
89 | if ipGW == nil {
90 | return fmt.Errorf("parse gateway %s failed", gw)
91 | }
92 | route := &netlink.Route{
93 | Dst: networkid,
94 | Gw: ipGW,
95 | }
96 | return netlink.RouteAdd(route)
97 | }
98 |
99 | func delRoute(dst, gw string) error {
100 | _, networkid, err := net.ParseCIDR(dst)
101 | if err != nil {
102 | return err
103 | }
104 | route := &netlink.Route{
105 | Dst: networkid,
106 | }
107 | return netlink.RouteDel(route)
108 | }
109 |
110 | func delRoutesByGateway(gateway string) error {
111 | cmd := exec.Command("route", "-n")
112 | output, err := cmd.Output()
113 | if err != nil {
114 | return err
115 | }
116 |
117 | lines := strings.Split(string(output), "\n")
118 | for _, line := range lines {
119 | if !strings.Contains(line, gateway) {
120 | continue
121 | }
122 | fields := strings.Fields(line)
123 | if len(fields) >= 8 && fields[1] == "0.0.0.0" && fields[7] == gateway {
124 | delCmd := exec.Command("route", "del", "-net", fields[0], "gw", gateway)
125 | err := delCmd.Run()
126 | if err != nil {
127 | gLog.Printf(LvERROR, "Delete route %s error:%s", fields[0], err)
128 | continue
129 | }
130 | gLog.Printf(LvINFO, "Delete route ok: %s %s %s\n", fields[0], fields[1], gateway)
131 | }
132 | }
133 | return nil
134 | }
135 |
--------------------------------------------------------------------------------
/core/optun_other.go:
--------------------------------------------------------------------------------
1 | //go:build !linux && !windows && !darwin
2 | // +build !linux,!windows,!darwin
3 |
4 | package openp2p
5 |
6 | import "github.com/openp2p-cn/wireguard-go/tun"
7 |
8 | const (
9 | tunIfaceName = "optun"
10 | PIHeaderSize = 0
11 | )
12 |
13 | func (t *optun) Start(localAddr string, detail *SDWANInfo) error {
14 | var err error
15 | t.tunName = tunIfaceName
16 | t.dev, err = tun.CreateTUN(t.tunName, 1420)
17 |
18 | if err != nil {
19 | return err
20 | }
21 | err = setTunAddr(t.tunName, localAddr, detail.Gateway, t.dev)
22 | if err != nil {
23 | return err
24 | }
25 |
26 | return nil
27 | }
28 |
29 | func addRoute(dst, gw, ifname string) error {
30 | return nil
31 | }
32 |
33 | func delRoute(dst, gw string) error {
34 | return nil
35 | }
36 | func addTunAddr(localAddr, remoteAddr string) error {
37 | return nil
38 | }
39 |
40 | func delTunAddr(localAddr, remoteAddr string) error {
41 | return nil
42 | }
43 |
--------------------------------------------------------------------------------
/core/optun_windows.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | "net/netip"
7 | "os"
8 | "os/exec"
9 | "path/filepath"
10 | "runtime"
11 | "strconv"
12 | "strings"
13 |
14 | "github.com/openp2p-cn/wireguard-go/tun"
15 | "golang.org/x/sys/windows"
16 | "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
17 | )
18 |
19 | const (
20 | tunIfaceName = "optun"
21 | PIHeaderSize = 0
22 | )
23 |
24 | func (t *optun) Start(localAddr string, detail *SDWANInfo) error {
25 | // check wintun.dll
26 | tmpFile := filepath.Dir(os.Args[0]) + "/wintun.dll"
27 | fs, err := os.Stat(tmpFile)
28 | if err != nil || fs.Size() == 0 {
29 | url := fmt.Sprintf("https://openp2p.cn/download/v1/latest/wintun/%s/wintun.dll", runtime.GOARCH)
30 | err = downloadFile(url, "", tmpFile)
31 | if err != nil {
32 | os.Remove(tmpFile)
33 | return err
34 | }
35 | }
36 |
37 | t.tunName = tunIfaceName
38 |
39 | uuid := &windows.GUID{
40 | Data1: 0xf411e821,
41 | Data2: 0xb310,
42 | Data3: 0x4567,
43 | Data4: [8]byte{0x80, 0x42, 0x83, 0x7e, 0xf4, 0x56, 0xce, 0x13},
44 | }
45 | t.dev, err = tun.CreateTUNWithRequestedGUID(t.tunName, uuid, 1420)
46 | if err != nil { // retry
47 | t.dev, err = tun.CreateTUNWithRequestedGUID(t.tunName, uuid, 1420)
48 | }
49 |
50 | if err != nil {
51 | return err
52 | }
53 |
54 | return nil
55 | }
56 |
57 | func (t *optun) Read(bufs [][]byte, sizes []int, offset int) (n int, err error) {
58 | return t.dev.Read(bufs, sizes, offset)
59 | }
60 |
61 | func (t *optun) Write(bufs [][]byte, offset int) (int, error) {
62 | return t.dev.Write(bufs, offset)
63 | }
64 |
65 | func setTunAddr(ifname, localAddr, remoteAddr string, wintun interface{}) error {
66 | nativeTunDevice := wintun.(*tun.NativeTun)
67 | link := winipcfg.LUID(nativeTunDevice.LUID())
68 | ip, err := netip.ParsePrefix(localAddr)
69 | if err != nil {
70 | gLog.Printf(LvERROR, "ParsePrefix error:%s, luid:%d,localAddr:%s", err, nativeTunDevice.LUID(), localAddr)
71 | return err
72 | }
73 | err = link.SetIPAddresses([]netip.Prefix{ip})
74 | if err != nil {
75 | gLog.Printf(LvERROR, "SetIPAddresses error:%s, netip.Prefix:%+v", err, []netip.Prefix{ip})
76 | return err
77 | }
78 | return nil
79 | }
80 |
81 | func addRoute(dst, gw, ifname string) error {
82 | _, dstNet, err := net.ParseCIDR(dst)
83 | if err != nil {
84 | return err
85 | }
86 | i, err := net.InterfaceByName(ifname)
87 | if err != nil {
88 | return err
89 | }
90 | params := make([]string, 0)
91 | params = append(params, "add")
92 | params = append(params, dstNet.IP.String())
93 | params = append(params, "mask")
94 | params = append(params, net.IP(dstNet.Mask).String())
95 | params = append(params, gw)
96 | params = append(params, "if")
97 | params = append(params, strconv.Itoa(i.Index))
98 | // gLogger.Println(LevelINFO, "windows add route params:", params)
99 | execCommand("route", true, params...)
100 | return nil
101 | }
102 |
103 | func delRoute(dst, gw string) error {
104 | _, dstNet, err := net.ParseCIDR(dst)
105 | if err != nil {
106 | return err
107 | }
108 | params := make([]string, 0)
109 | params = append(params, "delete")
110 | params = append(params, dstNet.IP.String())
111 | params = append(params, "mask")
112 | params = append(params, net.IP(dstNet.Mask).String())
113 | params = append(params, gw)
114 | // gLogger.Println(LevelINFO, "windows delete route params:", params)
115 | execCommand("route", true, params...)
116 | return nil
117 | }
118 |
119 | func delRoutesByGateway(gateway string) error {
120 | cmd := exec.Command("route", "print", "-4")
121 | output, err := cmd.Output()
122 | if err != nil {
123 | return err
124 | }
125 |
126 | lines := strings.Split(string(output), "\n")
127 | for _, line := range lines {
128 | if !strings.Contains(line, gateway) {
129 | continue
130 | }
131 | fields := strings.Fields(line)
132 | if len(fields) >= 5 {
133 | cmd := exec.Command("route", "delete", fields[0], "mask", fields[1], gateway)
134 | err := cmd.Run()
135 | if err != nil {
136 | gLog.Printf(LvERROR, "Delete route %s error:%s", fields[0], err)
137 | continue
138 | }
139 | gLog.Printf(LvINFO, "Delete route ok: %s %s %s\n", fields[0], fields[1], gateway)
140 | }
141 | }
142 | return nil
143 | }
144 |
--------------------------------------------------------------------------------
/core/overlay.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "errors"
7 | "net"
8 | "time"
9 | )
10 |
11 | var ErrDeadlineExceeded error = &DeadlineExceededError{}
12 |
13 | // DeadlineExceededError is returned for an expired deadline.
14 | type DeadlineExceededError struct{}
15 |
16 | // Implement the net.Error interface.
17 | // The string is "i/o timeout" because that is what was returned
18 | // by earlier Go versions. Changing it may break programs that
19 | // match on error strings.
20 | func (e *DeadlineExceededError) Error() string { return "i/o timeout" }
21 | func (e *DeadlineExceededError) Timeout() bool { return true }
22 | func (e *DeadlineExceededError) Temporary() bool { return true }
23 |
24 | // implement io.Writer
25 | type overlayConn struct {
26 | tunnel *P2PTunnel // TODO: del
27 | app *p2pApp
28 | connTCP net.Conn
29 | id uint64
30 | rtid uint64
31 | running bool
32 | isClient bool
33 | appID uint64 // TODO: del
34 | appKey uint64 // TODO: del
35 | appKeyBytes []byte // TODO: del
36 | // for udp
37 | connUDP *net.UDPConn
38 | remoteAddr net.Addr
39 | udpData chan []byte
40 | lastReadUDPTs time.Time
41 | }
42 |
43 | func (oConn *overlayConn) run() {
44 | gLog.Printf(LvDEBUG, "%d overlayConn run start", oConn.id)
45 | defer gLog.Printf(LvDEBUG, "%d overlayConn run end", oConn.id)
46 | oConn.lastReadUDPTs = time.Now()
47 | buffer := make([]byte, ReadBuffLen+PaddingSize) // 16 bytes for padding
48 | reuseBuff := buffer[:ReadBuffLen]
49 | encryptData := make([]byte, ReadBuffLen+PaddingSize) // 16 bytes for padding
50 | tunnelHead := new(bytes.Buffer)
51 | relayHead := new(bytes.Buffer)
52 | binary.Write(relayHead, binary.LittleEndian, oConn.rtid)
53 | binary.Write(tunnelHead, binary.LittleEndian, oConn.id)
54 | for oConn.running && oConn.tunnel.isRuning() {
55 | readBuff, dataLen, err := oConn.Read(reuseBuff)
56 | if err != nil {
57 | if ne, ok := err.(net.Error); ok && ne.Timeout() {
58 | continue
59 | }
60 | // overlay tcp connection normal close, debug log
61 | gLog.Printf(LvDEBUG, "overlayConn %d read error:%s,close it", oConn.id, err)
62 | break
63 | }
64 | payload := readBuff[:dataLen]
65 | if oConn.appKey != 0 {
66 | payload, _ = encryptBytes(oConn.appKeyBytes, encryptData, readBuff[:dataLen], dataLen)
67 | }
68 | writeBytes := append(tunnelHead.Bytes(), payload...)
69 | // TODO: app.write
70 | if oConn.rtid == 0 {
71 | oConn.tunnel.conn.WriteBytes(MsgP2P, MsgOverlayData, writeBytes)
72 | gLog.Printf(LvDev, "write overlay data to tid:%d,oid:%d bodylen=%d", oConn.tunnel.id, oConn.id, len(writeBytes))
73 | } else {
74 | // write raley data
75 | all := append(relayHead.Bytes(), encodeHeader(MsgP2P, MsgOverlayData, uint32(len(writeBytes)))...)
76 | all = append(all, writeBytes...)
77 | oConn.tunnel.conn.WriteBytes(MsgP2P, MsgRelayData, all)
78 | gLog.Printf(LvDev, "write relay data to tid:%d,rtid:%d,oid:%d bodylen=%d", oConn.tunnel.id, oConn.rtid, oConn.id, len(writeBytes))
79 | }
80 | }
81 | if oConn.connTCP != nil {
82 | oConn.connTCP.Close()
83 | }
84 | if oConn.connUDP != nil {
85 | oConn.connUDP.Close()
86 | }
87 | oConn.tunnel.overlayConns.Delete(oConn.id)
88 | // notify peer disconnect
89 | req := OverlayDisconnectReq{ID: oConn.id}
90 | oConn.tunnel.WriteMessage(oConn.rtid, MsgP2P, MsgOverlayDisconnectReq, &req)
91 | }
92 |
93 | func (oConn *overlayConn) Read(reuseBuff []byte) (buff []byte, dataLen int, err error) {
94 | if !oConn.running {
95 | err = ErrOverlayConnDisconnect
96 | return
97 | }
98 | if oConn.connUDP != nil {
99 | if time.Now().After(oConn.lastReadUDPTs.Add(time.Minute * 5)) {
100 | err = errors.New("udp close")
101 | return
102 | }
103 | if oConn.remoteAddr != nil { // as server
104 | select {
105 | case buff = <-oConn.udpData:
106 | dataLen = len(buff) - PaddingSize
107 | oConn.lastReadUDPTs = time.Now()
108 | case <-time.After(time.Second * 10):
109 | err = ErrDeadlineExceeded
110 | }
111 | } else { // as client
112 | oConn.connUDP.SetReadDeadline(time.Now().Add(UDPReadTimeout))
113 | dataLen, _, err = oConn.connUDP.ReadFrom(reuseBuff)
114 | if err == nil {
115 | oConn.lastReadUDPTs = time.Now()
116 | }
117 | buff = reuseBuff
118 | }
119 | return
120 | }
121 | if oConn.connTCP != nil {
122 | oConn.connTCP.SetReadDeadline(time.Now().Add(UDPReadTimeout))
123 | dataLen, err = oConn.connTCP.Read(reuseBuff)
124 | buff = reuseBuff
125 | }
126 |
127 | return
128 | }
129 |
130 | // calling by p2pTunnel
131 | func (oConn *overlayConn) Write(buff []byte) (n int, err error) {
132 | // add mutex when multi-thread calling
133 | if !oConn.running {
134 | return 0, ErrOverlayConnDisconnect
135 | }
136 | if oConn.connUDP != nil {
137 | if oConn.remoteAddr == nil {
138 | n, err = oConn.connUDP.Write(buff)
139 | } else {
140 | n, err = oConn.connUDP.WriteTo(buff, oConn.remoteAddr)
141 | }
142 | if err != nil {
143 | oConn.running = false
144 | }
145 | return
146 | }
147 | if oConn.connTCP != nil {
148 | n, err = oConn.connTCP.Write(buff)
149 | }
150 |
151 | if err != nil {
152 | oConn.running = false
153 | }
154 | return
155 | }
156 |
157 | func (oConn *overlayConn) Close() (err error) {
158 | oConn.running = false
159 | if oConn.connTCP != nil {
160 | oConn.connTCP.Close()
161 | // oConn.connTCP = nil
162 | }
163 | if oConn.connUDP != nil {
164 | oConn.connUDP.Close()
165 | // oConn.connUDP = nil
166 | }
167 | return nil
168 | }
169 |
--------------------------------------------------------------------------------
/core/p2pappkeys.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | var p2pAppKeys sync.Map
8 |
9 | func GetKey(appID uint64) uint64 {
10 | i, ok := p2pAppKeys.Load(appID)
11 | if !ok {
12 | return 0
13 | }
14 | return i.(uint64)
15 | }
16 |
17 | func SaveKey(appID uint64, appKey uint64) {
18 | p2pAppKeys.Store(appID, appKey)
19 | }
20 |
--------------------------------------------------------------------------------
/core/p2ptunnel_test.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestSelectPriority(t *testing.T) {
9 | writeData := make(chan []byte, WriteDataChanSize)
10 | writeDataSmall := make(chan []byte, WriteDataChanSize/30)
11 | for i := 0; i < 100; i++ {
12 | writeData <- []byte("data")
13 | writeDataSmall <- []byte("small data")
14 | }
15 | for i := 0; i < 100; i++ {
16 | select {
17 | case buff := <-writeDataSmall:
18 | fmt.Printf("got small data:%s\n", string(buff))
19 | case buff := <-writeData:
20 | fmt.Printf("got data:%s\n", string(buff))
21 | }
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/core/ping.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | "os"
7 | "time"
8 |
9 | "golang.org/x/net/icmp"
10 | "golang.org/x/net/ipv4"
11 | )
12 |
13 | // 定义ICMP回显请求和应答的结构
14 | type ICMPMessage struct {
15 | Type uint8
16 | Code uint8
17 | Checksum uint16
18 | Ident uint16
19 | Seq uint16
20 | Data []byte
21 | }
22 |
23 | // Ping sends an ICMP Echo request to the specified host and returns the response time.
24 | func Ping(host string) (time.Duration, error) {
25 | // Resolve the IP address of the host
26 | ipAddr, err := net.ResolveIPAddr("ip4", host)
27 | if err != nil {
28 | return 0, fmt.Errorf("failed to resolve host: %v", err)
29 | }
30 |
31 | // Create an ICMP listener
32 | conn, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
33 | if err != nil {
34 | return 0, fmt.Errorf("failed to create ICMP connection: %v", err)
35 | }
36 | defer conn.Close()
37 |
38 | // Create an ICMP Echo request message
39 | message := icmp.Message{
40 | Type: ipv4.ICMPTypeEcho,
41 | Code: 0,
42 | Body: &icmp.Echo{
43 | ID: os.Getpid() & 0xffff,
44 | Seq: 1,
45 | Data: []byte("HELLO-R-U-THERE"),
46 | },
47 | }
48 |
49 | // Marshal the message into binary form
50 | messageBytes, err := message.Marshal(nil)
51 | if err != nil {
52 | return 0, fmt.Errorf("failed to marshal ICMP message: %v", err)
53 | }
54 |
55 | // Send the ICMP Echo request
56 | start := time.Now()
57 | if _, err := conn.WriteTo(messageBytes, ipAddr); err != nil {
58 | return 0, fmt.Errorf("failed to send ICMP request: %v", err)
59 | }
60 |
61 | // Set a deadline for the response
62 | err = conn.SetReadDeadline(time.Now().Add(3 * time.Second))
63 | if err != nil {
64 | return 0, fmt.Errorf("failed to set read deadline: %v", err)
65 | }
66 |
67 | // Read the ICMP response
68 | response := make([]byte, 1500)
69 | n, _, err := conn.ReadFrom(response)
70 | if err != nil {
71 | return 0, fmt.Errorf("failed to read ICMP response: %v", err)
72 | }
73 |
74 | // Parse the ICMP response message
75 | parsedMessage, err := icmp.ParseMessage(ipv4.ICMPTypeEchoReply.Protocol(), response[:n])
76 | if err != nil {
77 | return 0, fmt.Errorf("failed to parse ICMP response: %v", err)
78 | }
79 |
80 | // Check if the response is an Echo reply
81 | if parsedMessage.Type == ipv4.ICMPTypeEchoReply {
82 | duration := time.Since(start)
83 | return duration, nil
84 | } else {
85 | return 0, fmt.Errorf("unexpected ICMP message: %+v", parsedMessage)
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/core/speedlimiter.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "sync"
5 | "time"
6 | )
7 |
8 | // SpeedLimiter ...
9 | type SpeedLimiter struct {
10 | lastUpdate time.Time
11 | speed int // per second
12 | precision int // seconds
13 | freeCap int
14 | maxFreeCap int
15 | mtx sync.Mutex
16 | }
17 |
18 | func newSpeedLimiter(speed int, precision int) *SpeedLimiter {
19 | return &SpeedLimiter{
20 | speed: speed,
21 | precision: precision,
22 | lastUpdate: time.Now(),
23 | maxFreeCap: speed * precision,
24 | freeCap: speed * precision,
25 | }
26 | }
27 |
28 | // Add ...
29 | func (sl *SpeedLimiter) Add(increment int, wait bool) bool {
30 | if sl.speed <= 0 {
31 | return true
32 | }
33 | sl.mtx.Lock()
34 | defer sl.mtx.Unlock()
35 | sl.freeCap += int(time.Since(sl.lastUpdate) * time.Duration(sl.speed) / time.Second)
36 | if sl.freeCap > sl.maxFreeCap {
37 | sl.freeCap = sl.maxFreeCap
38 | }
39 | if !wait && sl.freeCap < increment {
40 | return false
41 | }
42 | sl.freeCap -= increment
43 | sl.lastUpdate = time.Now()
44 | if sl.freeCap < 0 {
45 | // sleep for the overflow
46 | // fmt.Println("sleep ", time.Millisecond*time.Duration(-sl.freeCap*100)/time.Duration(sl.speed))
47 | time.Sleep(time.Millisecond * time.Duration(-sl.freeCap*1000) / time.Duration(sl.speed)) // sleep ms
48 | }
49 | return true
50 | }
51 |
--------------------------------------------------------------------------------
/core/speedlimiter_test.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "testing"
5 | "time"
6 | )
7 |
8 | func TestBandwidth(t *testing.T) {
9 | speed := 10 * 1024 * 1024 / 8 // 10mbps
10 | speedl := newSpeedLimiter(speed, 1)
11 | oneBuffSize := 4096
12 | writeNum := 5000
13 | expectTime := oneBuffSize * writeNum / speed
14 | startTs := time.Now()
15 | for i := 0; i < writeNum; i++ {
16 | speedl.Add(oneBuffSize, true)
17 | }
18 | t.Logf("cost %ds, expect %ds", time.Since(startTs)/time.Second, expectTime)
19 | if time.Since(startTs) > time.Duration(expectTime+1)*time.Second || time.Since(startTs) < time.Duration(expectTime-1)*time.Second {
20 | t.Error("error")
21 | }
22 | }
23 |
24 | func TestSymmetric(t *testing.T) {
25 | speed := 20000 / 180
26 | speedl := newSpeedLimiter(speed, 180)
27 | oneBuffSize := 300
28 | writeNum := 70
29 | expectTime := (oneBuffSize*writeNum - 20000) / speed
30 | t.Logf("expect %ds", expectTime)
31 | startTs := time.Now()
32 | for i := 0; i < writeNum; i++ {
33 | speedl.Add(oneBuffSize, true)
34 | }
35 | t.Logf("cost %ds, expect %ds", time.Since(startTs)/time.Second, expectTime)
36 | if time.Since(startTs) > time.Duration(expectTime+1)*time.Second || time.Since(startTs) < time.Duration(expectTime-1)*time.Second {
37 | t.Error("error")
38 | }
39 | }
40 |
41 | func TestSymmetric2(t *testing.T) {
42 | speed := 30000 / 180
43 | speedl := newSpeedLimiter(speed, 180)
44 | oneBuffSize := 800
45 | writeNum := 40
46 | expectTime := (oneBuffSize*writeNum - 30000) / speed
47 | startTs := time.Now()
48 | for i := 0; i < writeNum; {
49 | if speedl.Add(oneBuffSize, true) {
50 | i++
51 | } else {
52 | time.Sleep(time.Millisecond)
53 | }
54 | }
55 | t.Logf("cost %ds, expect %ds", time.Since(startTs)/time.Second, expectTime)
56 | if time.Since(startTs) > time.Duration(expectTime+1)*time.Second || time.Since(startTs) < time.Duration(expectTime-1)*time.Second {
57 | t.Error("error")
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/core/udp.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "net"
7 | "time"
8 | )
9 |
10 | func UDPWrite(conn *net.UDPConn, dst net.Addr, mainType uint16, subType uint16, packet interface{}) (len int, err error) {
11 | msg, err := newMessage(mainType, subType, packet)
12 | if err != nil {
13 | return 0, err
14 | }
15 | if dst == nil {
16 | return conn.Write(msg)
17 | }
18 | return conn.WriteTo(msg, dst)
19 | }
20 |
21 | func UDPRead(conn *net.UDPConn, timeout time.Duration) (ra net.Addr, head *openP2PHeader, buff []byte, length int, err error) {
22 | if timeout > 0 {
23 | err = conn.SetReadDeadline(time.Now().Add(timeout))
24 | if err != nil {
25 | gLog.Println(LvERROR, "SetReadDeadline error")
26 | return nil, nil, nil, 0, err
27 | }
28 | }
29 |
30 | buff = make([]byte, 1024)
31 | length, ra, err = conn.ReadFrom(buff)
32 | if err != nil {
33 | // gLog.Println(LevelDEBUG, "ReadFrom error")
34 | return nil, nil, nil, 0, err
35 | }
36 | head = &openP2PHeader{}
37 | err = binary.Read(bytes.NewReader(buff[:openP2PHeaderSize]), binary.LittleEndian, head)
38 | if err != nil || head.DataLen > uint32(len(buff)-openP2PHeaderSize) {
39 | gLog.Println(LvERROR, "parse p2pheader error:", err)
40 | return nil, nil, nil, 0, err
41 | }
42 | return
43 | }
44 |
--------------------------------------------------------------------------------
/core/underlay.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "io"
5 | "time"
6 | )
7 |
8 | type underlay interface {
9 | Read([]byte) (int, error)
10 | Write([]byte) (int, error)
11 | ReadBuffer() (*openP2PHeader, []byte, error)
12 | WriteBytes(uint16, uint16, []byte) error
13 | WriteBuffer([]byte) error
14 | WriteMessage(uint16, uint16, interface{}) error
15 | Close() error
16 | WLock()
17 | WUnlock()
18 | SetReadDeadline(t time.Time) error
19 | SetWriteDeadline(t time.Time) error
20 | Protocol() string
21 | }
22 |
23 | func DefaultReadBuffer(ul underlay) (*openP2PHeader, []byte, error) {
24 | headBuf := make([]byte, openP2PHeaderSize)
25 | _, err := io.ReadFull(ul, headBuf)
26 | if err != nil {
27 | return nil, nil, err
28 | }
29 | head, err := decodeHeader(headBuf)
30 | if err != nil || head.MainType > 16 {
31 | return nil, nil, err
32 | }
33 | dataBuf := make([]byte, head.DataLen)
34 | _, err = io.ReadFull(ul, dataBuf)
35 | return head, dataBuf, err
36 | }
37 |
38 | func DefaultWriteBytes(ul underlay, mainType, subType uint16, data []byte) error {
39 | writeBytes := append(encodeHeader(mainType, subType, uint32(len(data))), data...)
40 | ul.SetWriteDeadline(time.Now().Add(TunnelHeartbeatTime / 2))
41 | ul.WLock()
42 | _, err := ul.Write(writeBytes)
43 | ul.WUnlock()
44 | return err
45 | }
46 |
47 | func DefaultWriteBuffer(ul underlay, data []byte) error {
48 | ul.SetWriteDeadline(time.Now().Add(TunnelHeartbeatTime / 2))
49 | ul.WLock()
50 | _, err := ul.Write(data)
51 | ul.WUnlock()
52 | return err
53 | }
54 |
55 | func DefaultWriteMessage(ul underlay, mainType uint16, subType uint16, packet interface{}) error {
56 | writeBytes, err := newMessage(mainType, subType, packet)
57 | if err != nil {
58 | return err
59 | }
60 | ul.SetWriteDeadline(time.Now().Add(TunnelHeartbeatTime / 2))
61 | ul.WLock()
62 | _, err = ul.Write(writeBytes)
63 | ul.WUnlock()
64 | return err
65 | }
66 |
--------------------------------------------------------------------------------
/core/underlay_kcp.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | "sync"
7 | "time"
8 |
9 | "github.com/xtaci/kcp-go/v5"
10 | )
11 |
12 | type underlayKCP struct {
13 | listener *kcp.Listener
14 | writeMtx *sync.Mutex
15 | *kcp.UDPSession
16 | }
17 |
18 | func (conn *underlayKCP) Protocol() string {
19 | return "kcp"
20 | }
21 |
22 | func (conn *underlayKCP) ReadBuffer() (*openP2PHeader, []byte, error) {
23 | return DefaultReadBuffer(conn)
24 | }
25 |
26 | func (conn *underlayKCP) WriteBytes(mainType uint16, subType uint16, data []byte) error {
27 | return DefaultWriteBytes(conn, mainType, subType, data)
28 | }
29 |
30 | func (conn *underlayKCP) WriteBuffer(data []byte) error {
31 | return DefaultWriteBuffer(conn, data)
32 | }
33 |
34 | func (conn *underlayKCP) WriteMessage(mainType uint16, subType uint16, packet interface{}) error {
35 | return DefaultWriteMessage(conn, mainType, subType, packet)
36 | }
37 |
38 | func (conn *underlayKCP) Close() error {
39 | conn.UDPSession.Close()
40 | return nil
41 | }
42 | func (conn *underlayKCP) WLock() {
43 | conn.writeMtx.Lock()
44 | }
45 | func (conn *underlayKCP) WUnlock() {
46 | conn.writeMtx.Unlock()
47 | }
48 | func (conn *underlayKCP) CloseListener() {
49 | if conn.listener != nil {
50 | conn.listener.Close()
51 | }
52 | }
53 |
54 | func (conn *underlayKCP) Accept() error {
55 | kConn, err := conn.listener.AcceptKCP()
56 | if err != nil {
57 | conn.listener.Close()
58 | return err
59 | }
60 | kConn.SetNoDelay(0, 40, 0, 0)
61 | kConn.SetWindowSize(512, 512)
62 | kConn.SetWriteBuffer(1024 * 128)
63 | kConn.SetReadBuffer(1024 * 128)
64 | conn.UDPSession = kConn
65 | return nil
66 | }
67 |
68 | func listenKCP(addr string, idleTimeout time.Duration) (*underlayKCP, error) {
69 | gLog.Println(LvDEBUG, "kcp listen on ", addr)
70 | listener, err := kcp.ListenWithOptions(addr, nil, 0, 0)
71 | if err != nil {
72 | return nil, fmt.Errorf("quic.ListenAddr error:%s", err)
73 | }
74 | ul := &underlayKCP{listener: listener, writeMtx: &sync.Mutex{}}
75 | err = ul.Accept()
76 | if err != nil {
77 | ul.CloseListener()
78 | return nil, fmt.Errorf("accept KCP error:%s", err)
79 | }
80 | return ul, nil
81 | }
82 |
83 | func dialKCP(conn *net.UDPConn, remoteAddr *net.UDPAddr, idleTimeout time.Duration) (*underlayKCP, error) {
84 | kConn, err := kcp.NewConn(remoteAddr.String(), nil, 0, 0, conn)
85 | if err != nil {
86 | return nil, fmt.Errorf("quic.DialContext error:%s", err)
87 | }
88 | kConn.SetNoDelay(0, 40, 0, 0)
89 | kConn.SetWindowSize(512, 512)
90 | kConn.SetWriteBuffer(1024 * 128)
91 | kConn.SetReadBuffer(1024 * 128)
92 | ul := &underlayKCP{nil, &sync.Mutex{}, kConn}
93 | return ul, nil
94 | }
95 |
--------------------------------------------------------------------------------
/core/underlay_quic.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "context"
5 | "crypto/rand"
6 | "crypto/rsa"
7 | "crypto/tls"
8 | "crypto/x509"
9 | "encoding/pem"
10 | "fmt"
11 | "math/big"
12 | "net"
13 | "sync"
14 | "time"
15 |
16 | "github.com/quic-go/quic-go"
17 | )
18 |
19 | // quic.DialContext do not support version 44,disable it
20 | var quicVersion []quic.VersionNumber
21 |
22 | type underlayQUIC struct {
23 | listener quic.Listener
24 | writeMtx *sync.Mutex
25 | quic.Stream
26 | quic.Connection
27 | }
28 |
29 | func (conn *underlayQUIC) Protocol() string {
30 | return "quic"
31 | }
32 |
33 | func (conn *underlayQUIC) ReadBuffer() (*openP2PHeader, []byte, error) {
34 | return DefaultReadBuffer(conn)
35 | }
36 |
37 | func (conn *underlayQUIC) WriteBytes(mainType uint16, subType uint16, data []byte) error {
38 | return DefaultWriteBytes(conn, mainType, subType, data)
39 | }
40 |
41 | func (conn *underlayQUIC) WriteBuffer(data []byte) error {
42 | return DefaultWriteBuffer(conn, data)
43 | }
44 |
45 | func (conn *underlayQUIC) WriteMessage(mainType uint16, subType uint16, packet interface{}) error {
46 | return DefaultWriteMessage(conn, mainType, subType, packet)
47 | }
48 |
49 | func (conn *underlayQUIC) Close() error {
50 | conn.Stream.CancelRead(1)
51 | conn.Connection.CloseWithError(0, "")
52 | conn.CloseListener()
53 | return nil
54 | }
55 | func (conn *underlayQUIC) WLock() {
56 | conn.writeMtx.Lock()
57 | }
58 | func (conn *underlayQUIC) WUnlock() {
59 | conn.writeMtx.Unlock()
60 | }
61 | func (conn *underlayQUIC) CloseListener() {
62 | if conn.listener != nil {
63 | conn.listener.Close()
64 | }
65 | }
66 |
67 | func (conn *underlayQUIC) Accept() error {
68 | ctx, cancel := context.WithTimeout(context.Background(), UnderlayConnectTimeout)
69 | defer cancel()
70 | sess, err := conn.listener.Accept(ctx)
71 | if err != nil {
72 | return err
73 | }
74 | stream, err := sess.AcceptStream(context.Background())
75 | if err != nil {
76 | return err
77 | }
78 | conn.Stream = stream
79 | conn.Connection = sess
80 | return nil
81 | }
82 |
83 | func listenQuic(addr string, idleTimeout time.Duration) (*underlayQUIC, error) {
84 | gLog.Println(LvDEBUG, "quic listen on ", addr)
85 | listener, err := quic.ListenAddr(addr, generateTLSConfig(),
86 | &quic.Config{Versions: quicVersion, MaxIdleTimeout: idleTimeout, DisablePathMTUDiscovery: true})
87 | if err != nil {
88 | return nil, fmt.Errorf("quic.ListenAddr error:%s", err)
89 | }
90 | ul := &underlayQUIC{listener: listener, writeMtx: &sync.Mutex{}}
91 | err = ul.Accept()
92 | if err != nil {
93 | ul.CloseListener()
94 | return nil, fmt.Errorf("accept quic error:%s", err)
95 | }
96 | return ul, nil
97 | }
98 |
99 | func dialQuic(conn *net.UDPConn, remoteAddr *net.UDPAddr, idleTimeout time.Duration) (*underlayQUIC, error) {
100 | tlsConf := &tls.Config{
101 | InsecureSkipVerify: true,
102 | NextProtos: []string{"openp2pv1"},
103 | }
104 | Connection, err := quic.DialContext(context.Background(), conn, remoteAddr, conn.LocalAddr().String(), tlsConf,
105 | &quic.Config{Versions: quicVersion, MaxIdleTimeout: idleTimeout, DisablePathMTUDiscovery: true})
106 | if err != nil {
107 | return nil, fmt.Errorf("quic.DialContext error:%s", err)
108 | }
109 | stream, err := Connection.OpenStreamSync(context.Background())
110 | if err != nil {
111 | return nil, fmt.Errorf("OpenStreamSync error:%s", err)
112 | }
113 | qConn := &underlayQUIC{nil, &sync.Mutex{}, stream, Connection}
114 | return qConn, nil
115 | }
116 |
117 | // Setup a bare-bones TLS config for the server
118 | func generateTLSConfig() *tls.Config {
119 | key, err := rsa.GenerateKey(rand.Reader, 1024)
120 | if err != nil {
121 | panic(err)
122 | }
123 | template := x509.Certificate{SerialNumber: big.NewInt(1)}
124 | certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
125 | if err != nil {
126 | panic(err)
127 | }
128 | keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
129 | certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
130 |
131 | tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
132 | if err != nil {
133 | panic(err)
134 | }
135 | return &tls.Config{
136 | Certificates: []tls.Certificate{tlsCert},
137 | NextProtos: []string{"openp2pv1"},
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/core/underlay_tcp.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "encoding/binary"
5 | "fmt"
6 | "net"
7 | "sync"
8 | "time"
9 |
10 | reuse "github.com/openp2p-cn/go-reuseport"
11 | )
12 |
13 | type underlayTCP struct {
14 | writeMtx *sync.Mutex
15 | net.Conn
16 | connectTime time.Time
17 | }
18 |
19 | func (conn *underlayTCP) Protocol() string {
20 | return "tcp"
21 | }
22 |
23 | func (conn *underlayTCP) ReadBuffer() (*openP2PHeader, []byte, error) {
24 | return DefaultReadBuffer(conn)
25 | }
26 |
27 | func (conn *underlayTCP) WriteBytes(mainType uint16, subType uint16, data []byte) error {
28 | return DefaultWriteBytes(conn, mainType, subType, data)
29 | }
30 |
31 | func (conn *underlayTCP) WriteBuffer(data []byte) error {
32 | return DefaultWriteBuffer(conn, data)
33 | }
34 |
35 | func (conn *underlayTCP) WriteMessage(mainType uint16, subType uint16, packet interface{}) error {
36 | return DefaultWriteMessage(conn, mainType, subType, packet)
37 | }
38 |
39 | func (conn *underlayTCP) Close() error {
40 | return conn.Conn.Close()
41 | }
42 | func (conn *underlayTCP) WLock() {
43 | conn.writeMtx.Lock()
44 | }
45 | func (conn *underlayTCP) WUnlock() {
46 | conn.writeMtx.Unlock()
47 | }
48 |
49 | func listenTCP(host string, port int, localPort int, mode string, t *P2PTunnel) (*underlayTCP, error) {
50 | if mode == LinkModeTCPPunch {
51 | if compareVersion(t.config.peerVersion, SyncServerTimeVersion) < 0 {
52 | gLog.Printf(LvDEBUG, "peer version %s less than %s", t.config.peerVersion, SyncServerTimeVersion)
53 | } else {
54 | ts := time.Duration(int64(t.punchTs) + GNetwork.dt - time.Now().UnixNano())
55 | gLog.Printf(LvDEBUG, "sleep %d ms", ts/time.Millisecond)
56 | time.Sleep(ts)
57 | }
58 | gLog.Println(LvDEBUG, " send tcp punch: ", fmt.Sprintf("0.0.0.0:%d", localPort), "-->", fmt.Sprintf("%s:%d", host, port))
59 | c, err := reuse.DialTimeout("tcp", fmt.Sprintf("0.0.0.0:%d", localPort), fmt.Sprintf("%s:%d", host, port), CheckActiveTimeout)
60 | if err != nil {
61 | gLog.Println(LvDEBUG, "send tcp punch: ", err)
62 | return nil, err
63 | }
64 | utcp := &underlayTCP{writeMtx: &sync.Mutex{}, Conn: c}
65 | _, buff, err := utcp.ReadBuffer()
66 | if err != nil {
67 | return nil, fmt.Errorf("read start msg error:%s", err)
68 | }
69 | if buff != nil {
70 | gLog.Println(LvDEBUG, string(buff))
71 | }
72 | utcp.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, buff)
73 | return utcp, nil
74 | }
75 | GNetwork.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
76 | tid := t.id
77 | if compareVersion(t.config.peerVersion, PublicIPVersion) < 0 { // old version
78 | ipBytes := net.ParseIP(t.config.peerIP).To4()
79 | tid = uint64(binary.BigEndian.Uint32(ipBytes))
80 | gLog.Println(LvDEBUG, "compatible with old client, use ip as key:", tid)
81 | }
82 | var utcp *underlayTCP
83 | if mode == LinkModeIntranet && gConf.Network.hasIPv4 == 0 && gConf.Network.hasUPNPorNATPMP == 0 {
84 | addr, _ := net.ResolveTCPAddr("tcp4", fmt.Sprintf("0.0.0.0:%d", localPort))
85 | l, err := net.ListenTCP("tcp4", addr)
86 | if err != nil {
87 | gLog.Printf(LvERROR, "listen %d error:", localPort, err)
88 | return nil, err
89 | }
90 | defer l.Close()
91 | err = l.SetDeadline(time.Now().Add(UnderlayTCPConnectTimeout))
92 | if err != nil {
93 | gLog.Printf(LvERROR, "set listen timeout:", err)
94 | return nil, err
95 | }
96 | c, err := l.Accept()
97 | if err != nil {
98 | return nil, err
99 | }
100 | utcp = &underlayTCP{writeMtx: &sync.Mutex{}, Conn: c}
101 | } else {
102 | if v4l != nil {
103 | utcp = v4l.getUnderlayTCP(tid)
104 | }
105 | }
106 |
107 | if utcp == nil {
108 | return nil, ErrConnectPublicV4
109 | }
110 | return utcp, nil
111 | }
112 |
113 | func dialTCP(host string, port int, localPort int, mode string) (*underlayTCP, error) {
114 | var c net.Conn
115 | var err error
116 | if mode == LinkModeTCPPunch {
117 | gLog.Println(LvDev, " send tcp punch: ", fmt.Sprintf("0.0.0.0:%d", localPort), "-->", fmt.Sprintf("%s:%d", host, port))
118 | if c, err = reuse.DialTimeout("tcp", fmt.Sprintf("0.0.0.0:%d", localPort), fmt.Sprintf("%s:%d", host, port), CheckActiveTimeout); err != nil {
119 | gLog.Println(LvDev, "send tcp punch: ", err)
120 | }
121 |
122 | } else {
123 | c, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), CheckActiveTimeout)
124 | }
125 |
126 | if err != nil {
127 | gLog.Printf(LvDev, "Dial %s:%d error:%s", host, port, err)
128 | return nil, err
129 | }
130 | tc := c.(*net.TCPConn)
131 | tc.SetKeepAlive(true)
132 | tc.SetKeepAlivePeriod(UnderlayTCPKeepalive)
133 | gLog.Printf(LvDEBUG, "Dial %s:%d OK", host, port)
134 | return &underlayTCP{writeMtx: &sync.Mutex{}, Conn: c}, nil
135 | }
136 |
--------------------------------------------------------------------------------
/core/underlay_tcp6.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | "sync"
7 | "time"
8 | )
9 |
10 | type underlayTCP6 struct {
11 | writeMtx *sync.Mutex
12 | net.Conn
13 | }
14 |
15 | func (conn *underlayTCP6) Protocol() string {
16 | return "tcp6"
17 | }
18 |
19 | func (conn *underlayTCP6) ReadBuffer() (*openP2PHeader, []byte, error) {
20 | return DefaultReadBuffer(conn)
21 | }
22 |
23 | func (conn *underlayTCP6) WriteBytes(mainType uint16, subType uint16, data []byte) error {
24 | return DefaultWriteBytes(conn, mainType, subType, data)
25 | }
26 |
27 | func (conn *underlayTCP6) WriteBuffer(data []byte) error {
28 | return DefaultWriteBuffer(conn, data)
29 | }
30 |
31 | func (conn *underlayTCP6) WriteMessage(mainType uint16, subType uint16, packet interface{}) error {
32 | return DefaultWriteMessage(conn, mainType, subType, packet)
33 | }
34 |
35 | func (conn *underlayTCP6) Close() error {
36 | return conn.Conn.Close()
37 | }
38 | func (conn *underlayTCP6) WLock() {
39 | conn.writeMtx.Lock()
40 | }
41 | func (conn *underlayTCP6) WUnlock() {
42 | conn.writeMtx.Unlock()
43 | }
44 | func listenTCP6(port int, timeout time.Duration) (*underlayTCP6, error) {
45 | addr, _ := net.ResolveTCPAddr("tcp6", fmt.Sprintf("[::]:%d", port))
46 | l, err := net.ListenTCP("tcp6", addr)
47 | if err != nil {
48 | return nil, err
49 | }
50 | defer l.Close()
51 | l.SetDeadline(time.Now().Add(timeout))
52 | c, err := l.Accept()
53 | defer l.Close()
54 | if err != nil {
55 | return nil, err
56 | }
57 | return &underlayTCP6{writeMtx: &sync.Mutex{}, Conn: c}, nil
58 | }
59 |
60 | func dialTCP6(host string, port int) (*underlayTCP6, error) {
61 | c, err := net.DialTimeout("tcp6", fmt.Sprintf("[%s]:%d", host, port), UnderlayConnectTimeout)
62 | if err != nil {
63 | gLog.Printf(LvERROR, "Dial %s:%d error:%s", host, port, err)
64 | return nil, err
65 | }
66 | return &underlayTCP6{writeMtx: &sync.Mutex{}, Conn: c}, nil
67 | }
68 |
--------------------------------------------------------------------------------
/core/update.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "archive/tar"
5 | "archive/zip"
6 | "compress/gzip"
7 | "crypto/tls"
8 | "crypto/x509"
9 | "encoding/json"
10 | "fmt"
11 | "io"
12 | "io/ioutil"
13 | "net/http"
14 | "net/url"
15 | "os"
16 | "path/filepath"
17 | "runtime"
18 | "time"
19 | )
20 |
21 | func update(host string, port int) error {
22 | gLog.Println(LvINFO, "update start")
23 | defer gLog.Println(LvINFO, "update end")
24 | caCertPool, err := x509.SystemCertPool()
25 | if err != nil {
26 | gLog.Println(LvERROR, "Failed to load system root CAs:", err)
27 | } else {
28 | caCertPool = x509.NewCertPool()
29 | }
30 | caCertPool.AppendCertsFromPEM([]byte(rootCA))
31 | caCertPool.AppendCertsFromPEM([]byte(ISRGRootX1))
32 |
33 | c := http.Client{
34 | Transport: &http.Transport{
35 | TLSClientConfig: &tls.Config{RootCAs: caCertPool,
36 | InsecureSkipVerify: false},
37 | },
38 | Timeout: time.Second * 30,
39 | }
40 | goos := runtime.GOOS
41 | goarch := runtime.GOARCH
42 | rsp, err := c.Get(fmt.Sprintf("https://%s:%d/api/v1/update?fromver=%s&os=%s&arch=%s&user=%s&node=%s", host, port, OpenP2PVersion, goos, goarch, url.QueryEscape(gConf.Network.User), url.QueryEscape(gConf.Network.Node)))
43 | if err != nil {
44 | gLog.Println(LvERROR, "update:query update list failed:", err)
45 | return err
46 | }
47 | defer rsp.Body.Close()
48 | if rsp.StatusCode != http.StatusOK {
49 | gLog.Println(LvERROR, "get update info error:", rsp.Status)
50 | return err
51 | }
52 | rspBuf, err := ioutil.ReadAll(rsp.Body)
53 | if err != nil {
54 | gLog.Println(LvERROR, "update:read update list failed:", err)
55 | return err
56 | }
57 | updateInfo := UpdateInfo{}
58 | if err = json.Unmarshal(rspBuf, &updateInfo); err != nil {
59 | gLog.Println(LvERROR, rspBuf, " update info decode error:", err)
60 | return err
61 | }
62 | if updateInfo.Error != 0 {
63 | gLog.Println(LvERROR, "update error:", updateInfo.Error, updateInfo.ErrorDetail)
64 | return err
65 | }
66 | err = updateFile(updateInfo.Url, "", "openp2p")
67 | if err != nil {
68 | gLog.Println(LvERROR, "update: download failed:", err)
69 | return err
70 | }
71 | return nil
72 | }
73 |
74 | func downloadFile(url string, checksum string, dstFile string) error {
75 | output, err := os.OpenFile(dstFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0776)
76 | if err != nil {
77 | gLog.Printf(LvERROR, "OpenFile %s error:%s", dstFile, err)
78 | return err
79 | }
80 | caCertPool, err := x509.SystemCertPool()
81 | if err != nil {
82 | gLog.Println(LvERROR, "Failed to load system root CAs:", err)
83 | } else {
84 | caCertPool = x509.NewCertPool()
85 | }
86 | caCertPool.AppendCertsFromPEM([]byte(rootCA))
87 | caCertPool.AppendCertsFromPEM([]byte(ISRGRootX1))
88 | tr := &http.Transport{
89 | TLSClientConfig: &tls.Config{
90 | RootCAs: caCertPool,
91 | InsecureSkipVerify: false},
92 | }
93 | client := &http.Client{Transport: tr}
94 | response, err := client.Get(url)
95 | if err != nil {
96 | gLog.Printf(LvERROR, "download url %s error:%s", url, err)
97 | output.Close()
98 | return err
99 | }
100 | defer response.Body.Close()
101 | n, err := io.Copy(output, response.Body)
102 | if err != nil {
103 | gLog.Printf(LvERROR, "io.Copy error:%s", err)
104 | output.Close()
105 | return err
106 | }
107 | output.Sync()
108 | output.Close()
109 | gLog.Println(LvINFO, "download ", url, " ok")
110 | gLog.Printf(LvINFO, "size: %d bytes", n)
111 | return nil
112 | }
113 |
114 | func updateFile(url string, checksum string, dst string) error {
115 | gLog.Println(LvINFO, "download ", url)
116 | tmpFile := filepath.Dir(os.Args[0]) + "/openp2p.tmp"
117 | err := downloadFile(url, checksum, tmpFile)
118 | if err != nil {
119 | return err
120 | }
121 | backupFile := os.Args[0] + "0"
122 | err = os.Rename(os.Args[0], backupFile) // the old daemon process was using the 0 file, so it will prevent override it
123 | if err != nil {
124 | gLog.Printf(LvINFO, " rename %s error:%s, retry 1", os.Args[0], err)
125 | backupFile = os.Args[0] + "1"
126 | err = os.Rename(os.Args[0], backupFile)
127 | if err != nil {
128 | gLog.Printf(LvINFO, " rename %s error:%s", os.Args[0], err)
129 | }
130 | }
131 | // extract
132 | gLog.Println(LvINFO, "extract files")
133 | err = extract(filepath.Dir(os.Args[0]), tmpFile)
134 | if err != nil {
135 | gLog.Printf(LvERROR, "extract error:%s. revert rename", err)
136 | os.Rename(backupFile, os.Args[0])
137 | return err
138 | }
139 | os.Remove(tmpFile)
140 | return nil
141 | }
142 |
143 | func extract(dst, src string) (err error) {
144 | if runtime.GOOS == "windows" {
145 | return unzip(dst, src)
146 | } else {
147 | return extractTgz(dst, src)
148 | }
149 | }
150 |
151 | func unzip(dst, src string) (err error) {
152 | archive, err := zip.OpenReader(src)
153 | if err != nil {
154 | return err
155 | }
156 | defer archive.Close()
157 |
158 | for _, f := range archive.File {
159 | filePath := filepath.Join(dst, f.Name)
160 | fmt.Println("unzipping file ", filePath)
161 | if f.FileInfo().IsDir() {
162 | fmt.Println("creating directory...")
163 | os.MkdirAll(filePath, os.ModePerm)
164 | continue
165 | }
166 | if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
167 | return err
168 | }
169 | dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
170 | if err != nil {
171 | return err
172 | }
173 | fileInArchive, err := f.Open()
174 | if err != nil {
175 | return err
176 | }
177 | if _, err := io.Copy(dstFile, fileInArchive); err != nil {
178 | return err
179 | }
180 | dstFile.Close()
181 | fileInArchive.Close()
182 | }
183 | return nil
184 | }
185 |
186 | func extractTgz(dst, src string) error {
187 | gzipStream, err := os.Open(src)
188 | if err != nil {
189 | return err
190 | }
191 | uncompressedStream, err := gzip.NewReader(gzipStream)
192 | if err != nil {
193 | return err
194 | }
195 | tarReader := tar.NewReader(uncompressedStream)
196 | for {
197 | header, err := tarReader.Next()
198 | if err == io.EOF {
199 | break
200 | }
201 | if err != nil {
202 | return err
203 | }
204 | switch header.Typeflag {
205 | case tar.TypeDir:
206 | if err := os.Mkdir(header.Name, 0755); err != nil {
207 | return err
208 | }
209 | case tar.TypeReg:
210 | filePath := filepath.Join(dst, header.Name)
211 | outFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(header.Mode))
212 | if err != nil {
213 | return err
214 | }
215 | defer outFile.Close()
216 | if _, err := io.Copy(outFile, tarReader); err != nil {
217 | return err
218 | }
219 | default:
220 | return err
221 | }
222 | }
223 | return nil
224 | }
225 |
226 | func cleanTempFiles() {
227 | tmpFile := os.Args[0] + "0"
228 | if _, err := os.Stat(tmpFile); err == nil {
229 | if err := os.Remove(tmpFile); err != nil {
230 | gLog.Printf(LvDEBUG, " remove %s error:%s", tmpFile, err)
231 | }
232 | }
233 | tmpFile = os.Args[0] + "1"
234 | if _, err := os.Stat(tmpFile); err == nil {
235 | if err := os.Remove(tmpFile); err != nil {
236 | gLog.Printf(LvDEBUG, " remove %s error:%s", tmpFile, err)
237 | }
238 | }
239 | }
240 |
--------------------------------------------------------------------------------
/core/util_darwin.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "strings"
5 | "syscall"
6 | )
7 |
8 | const (
9 | defaultInstallPath = "/usr/local/openp2p"
10 | defaultBinName = "openp2p"
11 | )
12 |
13 | func getOsName() (osName string) {
14 | output := execOutput("sw_vers", "-productVersion")
15 | osName = "Mac OS X " + strings.TrimSpace(output)
16 | return
17 | }
18 |
19 | func setRLimit() error {
20 | var limit syscall.Rlimit
21 | if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
22 | return err
23 | }
24 | limit.Cur = 65536
25 | if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
26 | return err
27 | }
28 | return nil
29 | }
30 |
31 | func setFirewall() {
32 | }
33 |
--------------------------------------------------------------------------------
/core/util_freebsd.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "bufio"
5 | "bytes"
6 | "io/ioutil"
7 | "os"
8 | "runtime"
9 | "strings"
10 | "syscall"
11 | )
12 |
13 | const (
14 | defaultInstallPath = "/usr/local/openp2p"
15 | defaultBinName = "openp2p"
16 | )
17 |
18 | func getOsName() (osName string) {
19 | var sysnamePath string
20 | sysnamePath = "/etc/redhat-release"
21 | _, err := os.Stat(sysnamePath)
22 | if err != nil && os.IsNotExist(err) {
23 | str := "PRETTY_NAME="
24 | f, err := os.Open("/etc/os-release")
25 | if err == nil {
26 | buf := bufio.NewReader(f)
27 | for {
28 | line, err := buf.ReadString('\n')
29 | if err == nil {
30 | line = strings.TrimSpace(line)
31 | pos := strings.Count(line, str)
32 | if pos > 0 {
33 | len1 := len([]rune(str)) + 1
34 | rs := []rune(line)
35 | osName = string(rs[len1 : (len(rs))-1])
36 | break
37 | }
38 | } else {
39 | break
40 | }
41 | }
42 | }
43 | } else {
44 | buff, err := ioutil.ReadFile(sysnamePath)
45 | if err == nil {
46 | osName = string(bytes.TrimSpace(buff))
47 | }
48 | }
49 | if osName == "" {
50 | osName = "FreeBSD"
51 | }
52 | return
53 | }
54 |
55 | func setRLimit() error {
56 | var limit syscall.Rlimit
57 | if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
58 | return err
59 | }
60 | limit.Max = 65536
61 | limit.Cur = limit.Max
62 | if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
63 | return err
64 | }
65 | return nil
66 | }
67 |
68 | func setFirewall() {
69 | }
70 |
--------------------------------------------------------------------------------
/core/util_linux.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "bufio"
5 | "bytes"
6 | "io/ioutil"
7 | "os"
8 | "runtime"
9 | "strings"
10 | "syscall"
11 | )
12 |
13 | const (
14 | defaultInstallPath = "/usr/local/openp2p"
15 | defaultBinName = "openp2p"
16 | )
17 |
18 | func getOsName() (osName string) {
19 | if runtime.GOOS == "android" {
20 | return "Android"
21 | }
22 | var sysnamePath string
23 | sysnamePath = "/etc/redhat-release"
24 | _, err := os.Stat(sysnamePath)
25 | if err != nil && os.IsNotExist(err) {
26 | str := "PRETTY_NAME="
27 | f, err := os.Open("/etc/os-release")
28 | if err != nil && os.IsNotExist(err) {
29 | str = "DISTRIB_ID="
30 | f, err = os.Open("/etc/openwrt_release")
31 | }
32 | if err == nil {
33 | buf := bufio.NewReader(f)
34 | for {
35 | line, err := buf.ReadString('\n')
36 | if err == nil {
37 | line = strings.TrimSpace(line)
38 | pos := strings.Count(line, str)
39 | if pos > 0 {
40 | len1 := len([]rune(str)) + 1
41 | rs := []rune(line)
42 | osName = string(rs[len1 : (len(rs))-1])
43 | break
44 | }
45 | } else {
46 | break
47 | }
48 | }
49 | }
50 | } else {
51 | buff, err := ioutil.ReadFile(sysnamePath)
52 | if err == nil {
53 | osName = string(bytes.TrimSpace(buff))
54 | }
55 | }
56 | if osName == "" {
57 | osName = "Linux"
58 | }
59 | return
60 | }
61 |
62 | func setRLimit() error {
63 | var limit syscall.Rlimit
64 | if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
65 | return err
66 | }
67 | limit.Max = 65536
68 | limit.Cur = limit.Max
69 | if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
70 | return err
71 | }
72 | return nil
73 | }
74 |
75 | func setFirewall() {
76 | }
77 |
--------------------------------------------------------------------------------
/core/util_windows.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "os/exec"
7 | "path/filepath"
8 | "strconv"
9 | "strings"
10 |
11 | "golang.org/x/sys/windows/registry"
12 | )
13 |
14 | const (
15 | defaultInstallPath = "C:\\Program Files\\OpenP2P"
16 | defaultBinName = "openp2p.exe"
17 | )
18 |
19 | func getOsName() (osName string) {
20 | k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE|registry.WOW64_64KEY)
21 | if err != nil {
22 | return
23 | }
24 | defer k.Close()
25 | pn, _, err := k.GetStringValue("ProductName")
26 | if err == nil {
27 | currentBuild, _, err := k.GetStringValue("CurrentBuild")
28 | if err != nil {
29 | return
30 | }
31 | buildNumber, err := strconv.Atoi(currentBuild)
32 | if err != nil {
33 | return
34 | }
35 | if buildNumber >= 22000 {
36 | pn = strings.Replace(pn, "Windows 10", "Windows 11", 1)
37 | }
38 | osName = pn
39 | }
40 | return
41 | }
42 |
43 | func setRLimit() error {
44 | return nil
45 | }
46 |
47 | func setFirewall() {
48 | fullPath, err := filepath.Abs(os.Args[0])
49 | if err != nil {
50 | gLog.Println(LvERROR, "add firewall error:", err)
51 | return
52 | }
53 | isXP := false
54 | osName := getOsName()
55 | if strings.Contains(osName, "XP") || strings.Contains(osName, "2003") {
56 | isXP = true
57 | }
58 | if isXP {
59 | exec.Command("cmd.exe", `/c`, fmt.Sprintf(`netsh firewall del allowedprogram "%s"`, fullPath)).Run()
60 | exec.Command("cmd.exe", `/c`, fmt.Sprintf(`netsh firewall add allowedprogram "%s" "%s" ENABLE`, ProductName, fullPath)).Run()
61 | } else { // win7 or later
62 | exec.Command("cmd.exe", `/c`, fmt.Sprintf(`netsh advfirewall firewall del rule name="%s"`, ProductName)).Run()
63 | exec.Command("cmd.exe", `/c`, fmt.Sprintf(`netsh advfirewall firewall add rule name="%s" dir=in action=allow program="%s" enable=yes`, ProductName, fullPath)).Run()
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/core/v4listener.go:
--------------------------------------------------------------------------------
1 | package openp2p
2 |
3 | import (
4 | "encoding/binary"
5 | "fmt"
6 | "net"
7 | "sync"
8 | "time"
9 | )
10 |
11 | type v4Listener struct {
12 | conns sync.Map
13 | port int
14 | acceptCh chan bool
15 | }
16 |
17 | func (vl *v4Listener) start() error {
18 | v4l.acceptCh = make(chan bool, 500)
19 | for {
20 | vl.listen()
21 | time.Sleep(UnderlayTCPConnectTimeout)
22 | }
23 | }
24 |
25 | func (vl *v4Listener) listen() error {
26 | gLog.Printf(LvINFO, "v4Listener listen %d start", vl.port)
27 | defer gLog.Printf(LvINFO, "v4Listener listen %d end", vl.port)
28 | addr, _ := net.ResolveTCPAddr("tcp4", fmt.Sprintf("0.0.0.0:%d", vl.port))
29 | l, err := net.ListenTCP("tcp4", addr)
30 | if err != nil {
31 | gLog.Printf(LvERROR, "v4Listener listen %d error:", vl.port, err)
32 | return err
33 | }
34 | defer l.Close()
35 | for {
36 | c, err := l.Accept()
37 | if err != nil {
38 | break
39 | }
40 | go vl.handleConnection(c)
41 | }
42 | return nil
43 | }
44 | func (vl *v4Listener) handleConnection(c net.Conn) {
45 | gLog.Println(LvDEBUG, "v4Listener accept connection: ", c.RemoteAddr().String())
46 | utcp := &underlayTCP{writeMtx: &sync.Mutex{}, Conn: c, connectTime: time.Now()}
47 | utcp.SetReadDeadline(time.Now().Add(UnderlayTCPConnectTimeout))
48 | _, buff, err := utcp.ReadBuffer()
49 | if err != nil {
50 | gLog.Println(LvERROR, "utcp.ReadBuffer error:", err)
51 | }
52 | utcp.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, buff)
53 | var tid uint64
54 | if string(buff) == "OpenP2P,hello" { // old client
55 | // save remoteIP as key
56 | remoteAddr := c.RemoteAddr().(*net.TCPAddr).IP
57 | ipBytes := remoteAddr.To4()
58 | tid = uint64(binary.BigEndian.Uint32(ipBytes)) // bytes not enough for uint64
59 | gLog.Println(LvDEBUG, "hello ", string(buff))
60 | } else {
61 | if len(buff) < 8 {
62 | return
63 | }
64 | tid = binary.LittleEndian.Uint64(buff[:8])
65 | gLog.Println(LvDEBUG, "hello ", tid)
66 | }
67 | // clear timeout connection
68 | vl.conns.Range(func(idx, i interface{}) bool {
69 | ut := i.(*underlayTCP)
70 | if ut.connectTime.Before(time.Now().Add(-UnderlayTCPConnectTimeout)) {
71 | vl.conns.Delete(idx)
72 | }
73 | return true
74 | })
75 | vl.conns.Store(tid, utcp)
76 | if len(vl.acceptCh) == 0 {
77 | vl.acceptCh <- true
78 | }
79 | }
80 |
81 | func (vl *v4Listener) getUnderlayTCP(tid uint64) *underlayTCP {
82 | for i := 0; i < 100; i++ {
83 | select {
84 | case <-time.After(time.Millisecond * 50):
85 | case <-vl.acceptCh:
86 | }
87 | if u, ok := vl.conns.LoadAndDelete(tid); ok {
88 | return u.(*underlayTCP)
89 | }
90 | }
91 | return nil
92 | }
93 |
--------------------------------------------------------------------------------
/doc/images/afterconnect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/afterconnect.png
--------------------------------------------------------------------------------
/doc/images/afterconnect_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/afterconnect_en.png
--------------------------------------------------------------------------------
/doc/images/afterconnect_linux.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/afterconnect_linux.png
--------------------------------------------------------------------------------
/doc/images/appdetail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/appdetail.png
--------------------------------------------------------------------------------
/doc/images/architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/architecture.png
--------------------------------------------------------------------------------
/doc/images/devices.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/devices.png
--------------------------------------------------------------------------------
/doc/images/devices_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/devices_en.png
--------------------------------------------------------------------------------
/doc/images/homeconnect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/homeconnect.png
--------------------------------------------------------------------------------
/doc/images/homeconnect_windows.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/homeconnect_windows.png
--------------------------------------------------------------------------------
/doc/images/install.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/install.png
--------------------------------------------------------------------------------
/doc/images/install_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/install_en.png
--------------------------------------------------------------------------------
/doc/images/mem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/mem.png
--------------------------------------------------------------------------------
/doc/images/mstscconnect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/mstscconnect.png
--------------------------------------------------------------------------------
/doc/images/mstscconnect_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/mstscconnect_en.png
--------------------------------------------------------------------------------
/doc/images/newapp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/newapp.png
--------------------------------------------------------------------------------
/doc/images/newapp_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/newapp_en.png
--------------------------------------------------------------------------------
/doc/images/newappedit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/newappedit.png
--------------------------------------------------------------------------------
/doc/images/newappedit_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/newappedit_en.png
--------------------------------------------------------------------------------
/doc/images/officeexecute_linux.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/officeexecute_linux.png
--------------------------------------------------------------------------------
/doc/images/officelisten.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/officelisten.png
--------------------------------------------------------------------------------
/doc/images/officelisten_2_linux.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/officelisten_2_linux.png
--------------------------------------------------------------------------------
/doc/images/officelisten_linux.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/officelisten_linux.png
--------------------------------------------------------------------------------
/doc/images/p2p-debug.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/p2p-debug.png
--------------------------------------------------------------------------------
/doc/images/p2pappok.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/p2pappok.png
--------------------------------------------------------------------------------
/doc/images/p2pappok_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/p2pappok_en.png
--------------------------------------------------------------------------------
/doc/images/prototype.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/prototype.png
--------------------------------------------------------------------------------
/doc/images/register.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/register.png
--------------------------------------------------------------------------------
/doc/images/register_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/register_en.png
--------------------------------------------------------------------------------
/doc/images/release-debug.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/release-debug.png
--------------------------------------------------------------------------------
/doc/images/sshconnect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/sshconnect.png
--------------------------------------------------------------------------------
/doc/images/stillrun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/stillrun.png
--------------------------------------------------------------------------------
/doc/images/stillrun_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/stillrun_en.png
--------------------------------------------------------------------------------
/doc/images/vs2022-remote-debug-attach.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/vs2022-remote-debug-attach.png
--------------------------------------------------------------------------------
/doc/images/win10warn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/win10warn.png
--------------------------------------------------------------------------------
/doc/images/win10warn_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/win10warn_en.png
--------------------------------------------------------------------------------
/doc/images/winscpconnect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openp2p-cn/openp2p/29faf4a95041dbad71fe3634b64b9c489bcc8d97/doc/images/winscpconnect.png
--------------------------------------------------------------------------------
/doc/remote-debug-golang.md:
--------------------------------------------------------------------------------
1 | # 1. 环境
2 | golang1.18.1
3 | 调试端: win10+vscode
4 | 被调试端: ubuntu20.04LTS+dlv
5 | ## dlv安装
6 | 参考官方文档https://github.com/go-delve/delve/tree/master/Documentation/installation
7 | ```
8 | go install github.com/go-delve/delve/cmd/dlv@latest
9 | ```
10 |
11 | ## 被调试端
12 | ```
13 | cd /your-src-path #要确保两端源码一致
14 | dlv debug --headless --listen=:2345 --api-version=2
15 | # 如果失败可查看更多日志
16 | # dlv debug --headless --listen=:2345 --api-version=2 --log --log-output=rpc,dap,debugger
17 | ```
18 |
19 | ## 调试端(vscode)
20 | ```
21 | 打开vscode,修改launch.json,增加下面远程调试配置
22 | {
23 | "version": "0.2.0",
24 | "configurations": [
25 | {
26 | "name": "RemoteDebug",
27 | "type": "go",
28 | "request": "attach",
29 | "mode": "remote",
30 | "port": 2345,
31 | "host": "192.168.3.29",
32 | }
33 | ]
34 | }
35 | 按F5启动调试
36 | ```
37 |
38 | ## 没有公网IP或不在同一个局域网,无法直连如何调试
39 | 到https://openp2p.cn/注册一个用户获得token,两端安装一个客户端程序,可将被调试端的2345端口,通过p2p连接映射到调试端本地。
40 | p2p连接可通过web配置 https://github.com/openp2p-cn/openp2p/blob/master/README-ZH.md#%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8
41 |
42 | 也可以手动下载https://github.com/openp2p-cn/openp2p/releases 通过命令行手动配置
43 |
44 | ```
45 | # 注意替换下面YOUR-开头的参数改成自己的
46 | ./openp2p -node YOUR-DEBUG-SERVER -token YOUR-TOKEN
47 |
48 | openp2p.exe -node YOUR-DEBUG-CLIENT -token YOUR-TOKEN -peernode YOUR-DEBUG-SERVER -dstport 2345 -srcport 2345
49 |
50 | 2022/04/22 11:07:26 25680 INFO LISTEN ON PORT tcp:2345 START
51 | #显示这条日志说明成功了
52 | ```
53 |
54 | p2p端口映射完成后,把vscode的配置改成本地127.0.0.1,一样可以顺利调试。
55 | ```
56 | {
57 | "version": "0.2.0",
58 | "configurations": [
59 | {
60 | "name": "RemoteDebug",
61 | "type": "go",
62 | "request": "attach",
63 | "mode": "remote",
64 | "port": 2345,
65 | "host": "127.0.0.1",
66 | }
67 | ]
68 | }
69 | ```
70 |
71 |
72 | # 参考
73 | https://github.com/golang/vscode-go/blob/master/docs/debugging.md#remote-debugging
--------------------------------------------------------------------------------
/doc/remote-debug-vscpp.md:
--------------------------------------------------------------------------------
1 | # 1. 环境
2 | visual studio 2022
3 | 调试端: win10
4 | 被调试端: win10
5 | ## 程序编译
6 | 一般远程调试我会选择release版本,然后将优化去掉即可,这样更接近真实版本。
7 | 
8 |
9 | ```
10 | go install github.com/go-delve/delve/cmd/dlv@latest
11 | ```
12 |
13 | ## 运行远程调试器
14 | C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\Remote Debugger 拷贝到目标机器
15 |
16 | 如果调试x64程序则cd到Remote Debugger\x64目录
17 |
18 | 管理员方式打开cmd,执行
19 | ```
20 | netsh advfirewall set allprofiles state off
21 | msvsmon.exe /noauth /anyuser /silent
22 | ```
23 |
24 | ## visual studio
25 |
26 | Attach远程进程,按下Ctrl+Atl+P
27 |
28 | 
29 |
30 |
31 | ## 没有公网IP或不在同一个局域网,无法直连如何调试
32 | 到 https://openp2p.cn/ 注册一个用户获得token,两端安装一个客户端程序,可将被调试端的2345端口,通过p2p连接映射到调试端本地。
33 | p2p连接可通过web配置 https://github.com/openp2p-cn/openp2p/blob/master/README-ZH.md#%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8
34 |
35 | 也可以手动下载https://github.com/openp2p-cn/openp2p/releases 通过命令行手动配置
36 |
37 | ```
38 | # 注意替换下面YOUR-开头的参数改成自己的
39 | ./openp2p -node YOUR-DEBUG-SERVER -token YOUR-TOKEN
40 |
41 | openp2p.exe -node YOUR-DEBUG-CLIENT -token YOUR-TOKEN -peernode YOUR-DEBUG-SERVER -dstport 4026 -srcport 4026
42 |
43 | 2022/04/22 11:07:26 25680 INFO LISTEN ON PORT tcp:4026 START
44 | #显示这条日志说明成功了
45 | ```
46 |
47 | 
48 |
49 | 可以顺利远程调试
50 | ```
--------------------------------------------------------------------------------
/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM alpine:3.18.2
2 |
3 | # Replace the default Alpine repositories with Aliyun mirrors
4 | RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
5 | apk add --no-cache ca-certificates iptables && \
6 | rm -rf /tmp/* /var/tmp/* /var/cache/apk/* /var/cache/distfiles/*
7 |
8 | COPY get-client.sh /
9 | ARG DOCKER_VER="latest"
10 | RUN echo $TARGETPLATFORM && chmod +x /get-client.sh && ./get-client.sh
11 |
12 | ENTRYPOINT ["/openp2p"]
13 |
--------------------------------------------------------------------------------
/docker/get-client.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 |
4 | echo "Building version:${DOCKER_VER}"
5 | echo "Running on platform: $TARGETPLATFORM"
6 | # TARGETPLATFORM=$(echo $TARGETPLATFORM | tr ',' '/')
7 | echo "Running on platform: $TARGETPLATFORM"
8 | sysType="linux-amd64"
9 | archType=$(uname -m)
10 | if [[ $archType == aarch64 ]] ;
11 | then
12 | sysType="linux-arm64"
13 | elif [[ $archType == arm* ]] ;
14 | then
15 | sysType="linux-arm"
16 | elif [[ $archType == i*86 ]] ;
17 | then
18 | sysType="linux-386"
19 | elif [[ $archType == mips ]] ;
20 | then
21 | sysType="linux-mipsle"
22 | ls /lib |grep mipsel
23 | if [[ $? -ne 0 ]]; then
24 | # mipsel not found, it's mipseb
25 | sysType="linux-mipsbe"
26 | fi
27 | fi
28 | url="https://openp2p.cn/download/v1/${DOCKER_VER}/openp2p-latest.$sysType.tar.gz"
29 | echo "download $url start"
30 |
31 | if [ -f /usr/bin/curl ]; then
32 | curl -k -o openp2p.tar.gz $url
33 | else
34 | wget --no-check-certificate -O openp2p.tar.gz $url
35 | fi
36 | if [ $? -ne 0 ]; then
37 | echo "download error $?"
38 | exit 9
39 | fi
40 | echo "download ok"
41 | tar -xzvf openp2p.tar.gz
42 | chmod +x openp2p
43 | pwd
44 | ls -l
45 | exit 0
46 |
--------------------------------------------------------------------------------
/example/dll/dll.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | using namespace std;
5 | typedef void (*pRun)(const char *);
6 |
7 | int main(int argc, char *argv[])
8 | {
9 | HMODULE dll = LoadLibraryA("openp2p.dll");
10 | pRun run = (pRun)GetProcAddress(dll, "RunCmd");
11 | run("-node 5800-debug2 -token YOUR-TOKEN");
12 | FreeLibrary(dll);
13 | return 0;
14 | }
15 |
--------------------------------------------------------------------------------
/example/echo/echo-client.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | op "openp2p/core"
6 | "time"
7 | )
8 |
9 | func main() {
10 | op.Run()
11 | for i := 0; i < 10; i++ {
12 | go echoClient("5800-debug")
13 | }
14 | echoClient("5800-debug")
15 | }
16 |
17 | func echoClient(peerNode string) {
18 | sendDatalen := op.ReadBuffLen
19 | sendBuff := make([]byte, sendDatalen)
20 | for i := 0; i < len(sendBuff); i++ {
21 | sendBuff[i] = byte('A' + i/100)
22 | }
23 | // peerNode = "YOUR-PEER-NODE-NAME"
24 | if err := op.GNetwork.ConnectNode(peerNode); err != nil {
25 | fmt.Println("connect error:", err)
26 | return
27 | }
28 | for i := 0; ; i++ {
29 | sendBuff[1] = 'A' + byte(i%26)
30 | if err := op.GNetwork.WriteNode(op.NodeNameToID(peerNode), sendBuff[:sendDatalen]); err != nil {
31 | fmt.Println("write error:", err)
32 | break
33 | }
34 | nd := op.GNetwork.ReadNode(time.Second * 10)
35 | if nd == nil {
36 | fmt.Printf("waiting for node data\n")
37 | time.Sleep(time.Second * 10)
38 | continue
39 | }
40 | fmt.Printf("read %d len=%d data=%s\n", nd.NodeID, len(nd.Data), nd.Data[:16]) // only print 16 bytes
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/example/echo/echo-server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | op "openp2p/core"
6 | "time"
7 | )
8 |
9 | func main() {
10 | op.Run()
11 | echoServer()
12 | forever := make(chan bool)
13 | <-forever
14 | }
15 |
16 | func echoServer() {
17 | // peerID := fmt.Sprintf("%d", core.NodeNameToID(peerNode))
18 | for {
19 | nd := op.GNetwork.ReadNode(time.Second * 10)
20 | if nd == nil {
21 | fmt.Printf("waiting for node data\n")
22 | // time.Sleep(time.Second * 10)
23 | continue
24 | }
25 | // fmt.Printf("read %s len=%d data=%s\n", nd.Node, len(nd.Data), nd.Data[:16])
26 | nd.Data[0] = 'R' // echo server mark as replied
27 | if err := op.GNetwork.WriteNode(nd.NodeID, nd.Data); err != nil {
28 | fmt.Println("write error:", err)
29 | break
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module openp2p
2 |
3 | go 1.20
4 |
5 | require (
6 | github.com/emirpasic/gods v1.18.1
7 | github.com/gorilla/websocket v1.4.2
8 | github.com/openp2p-cn/go-reuseport v0.3.2
9 | github.com/openp2p-cn/service v1.0.0
10 | github.com/openp2p-cn/totp v0.0.0-20230421034602-0f3320ffb25e
11 | github.com/quic-go/quic-go v0.34.0
12 | github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54
13 | github.com/xtaci/kcp-go/v5 v5.5.17
14 | golang.org/x/sys v0.26.0
15 | golang.zx2c4.com/wireguard/windows v0.5.3
16 | )
17 |
18 | require (
19 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
20 | github.com/golang/mock v1.7.0-rc.1 // indirect
21 | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
22 | github.com/kardianos/service v1.2.2 // indirect
23 | github.com/klauspost/cpuid/v2 v2.2.5 // indirect
24 | github.com/klauspost/reedsolomon v1.11.8 // indirect
25 | github.com/onsi/ginkgo/v2 v2.2.0 // indirect
26 | github.com/pkg/errors v0.9.1 // indirect
27 | github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
28 | github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
29 | github.com/templexxx/cpu v0.1.0 // indirect
30 | github.com/templexxx/xorsimd v0.4.2 // indirect
31 | github.com/tjfoc/gmsm v1.4.1 // indirect
32 | github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
33 | golang.org/x/crypto v0.28.0 // indirect
34 | golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect
35 | golang.org/x/mod v0.21.0 // indirect
36 | golang.org/x/net v0.30.0 // indirect
37 | golang.org/x/tools v0.26.0 // indirect
38 | golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
39 | google.golang.org/protobuf v1.33.0 // indirect
40 | gvisor.dev/gvisor v0.0.0-20241128011400-745828301c93 // indirect
41 | )
42 |
--------------------------------------------------------------------------------
/lib/openp2p.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | // On Windows env
4 | // cd lib
5 | // go build -o openp2p.dll -buildmode=c-shared openp2p.go
6 | // caller example see example/dll
7 | import (
8 | op "openp2p/core"
9 | )
10 | import "C"
11 |
12 | func main() {
13 | }
14 |
15 | //export RunCmd
16 | func RunCmd(cmd *C.char) {
17 | op.RunCmd(C.GoString(cmd))
18 | }
19 |
--------------------------------------------------------------------------------