Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2275620060 | ||
|
|
29faf4a950 | ||
|
|
080e6af779 | ||
|
|
77bfa45172 | ||
|
|
3616768682 | ||
|
|
f015b828fc | ||
|
|
df1e16e708 | ||
|
|
c68094cc12 | ||
|
|
a0df0b1e95 | ||
|
|
9c3d557f5d | ||
|
|
2dea3a718d |
@@ -21,3 +21,4 @@ wintun.dll
|
||||
app/.idea/
|
||||
*_debug_bin*
|
||||
cmd/openp2p
|
||||
vendor/
|
||||
+2
-2
@@ -31,7 +31,7 @@ P2P直连可以让你的设备跑满带宽。不论你的设备在任何网络
|
||||
## 快速入门
|
||||
仅需简单4步就能用起来。
|
||||
下面是一个远程办公例子:在家里连入办公室Windows电脑。
|
||||
(另外一个快速入门视频 https://www.bilibili.com/video/BV1Et4y1P7bF/)
|
||||
(另外一个快速入门视频 <https://www.bilibili.com/video/BV1Et4y1P7bF/>)
|
||||
### 1.注册
|
||||
前往<https://console.openp2p.cn> 注册新用户,暂无需任何认证
|
||||
|
||||
@@ -96,7 +96,7 @@ Windows默认会阻止没有花钱买它家证书签名过的程序,选择“
|
||||
服务端有个调度模型,根据带宽、ping值、稳定性、服务时长,尽可能地使共享节点均匀地提供服务。连接共享节点使用TOTP密码,hmac-sha256算法校验,它是一次性密码,和我们平时使用的手机验证码或银行密码器一样的原理。
|
||||
|
||||
## 编译
|
||||
go version go1.18.1+
|
||||
go version 1.20 only (支持win7)
|
||||
cd到代码根目录,执行
|
||||
```
|
||||
make
|
||||
|
||||
@@ -103,7 +103,7 @@ That's right, the relay node is naturally an man-in-middle, so AES encryption is
|
||||
The server side has a scheduling model, which calculate bandwith, ping value,stability and service duration to provide a well-proportioned service to every share node. It uses TOTP(Time-based One-time Password) with hmac-sha256 algorithem, its theory as same as the cellphone validation code or bank cipher coder.
|
||||
|
||||
## Build
|
||||
go version go1.18.1+
|
||||
go version 1.20 only (support win7)
|
||||
cd root directory of the socure code and execute
|
||||
```
|
||||
make
|
||||
@@ -162,3 +162,5 @@ Email: [email protected] [email protected]
|
||||
## Disclaimer
|
||||
This project is open source for everyone to learn and use for free. It is forbidden to be used for illegal purposes. Any loss caused by improper use of this project or accident, this project and related personnel will not bear any responsibility.
|
||||
|
||||
## Thanks
|
||||
[](https://dartnode.com "Powered by DartNode - Free VPS for Open Source")
|
||||
+3
-2
@@ -2,9 +2,10 @@
|
||||
depends on openjdk 11, gradle 8.1.3, ndk 21
|
||||
```
|
||||
|
||||
go install golang.org/x/mobile/cmd/gomobile@latest
|
||||
# latest version not support go1.20
|
||||
go install golang.org/x/mobile/cmd/gomobile@7c4916698cc93475ebfea76748ee0faba2deb2a5
|
||||
gomobile init
|
||||
go get -v golang.org/x/mobile/bind
|
||||
go get -v golang.org/x/mobile/bind@7c4916698cc93475ebfea76748ee0faba2deb2a5
|
||||
cd core
|
||||
gomobile bind -target android -v
|
||||
if [[ $? -ne 0 ]]; then
|
||||
|
||||
@@ -203,7 +203,8 @@ class OpenP2PService : VpnService() {
|
||||
val network = Network(id, name, gateway, nodeList)
|
||||
println(network)
|
||||
Log.i(OpenP2PService.LOG_TAG, "onBind");
|
||||
builder.addDnsServer("8.8.8.8")
|
||||
builder.addDnsServer("223.5.5.5")
|
||||
builder.addDnsServer("2400:3200::1") // alicloud dns v6 & v4
|
||||
builder.addRoute("10.2.3.0", 24)
|
||||
// builder.addRoute("0.0.0.0", 0);
|
||||
builder.setSession(LOG_TAG!!)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package openp2p
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"testing"
|
||||
)
|
||||
@@ -114,3 +115,15 @@ func TestIsIPv6(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeID(t *testing.T) {
|
||||
node1 := "n1-stable"
|
||||
node2 := "tony-stable"
|
||||
nodeID1 := NodeNameToID(node1)
|
||||
nodeID2 := NodeNameToID(node2)
|
||||
if nodeID1 < nodeID2 {
|
||||
fmt.Printf("%s < %s\n", node1, node2)
|
||||
} else {
|
||||
fmt.Printf("%s >= %s\n", node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
+39
-22
@@ -3,6 +3,7 @@ package openp2p
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -28,6 +29,7 @@ type AppConfig struct {
|
||||
ForceRelay int // default:0 disable;1 enable
|
||||
Enabled int // default:1
|
||||
// runtime info
|
||||
relayMode string // private|public
|
||||
peerVersion string
|
||||
peerToken uint64
|
||||
peerNatType int
|
||||
@@ -64,17 +66,25 @@ func (c *AppConfig) ID() uint64 {
|
||||
return uint64(c.SrcPort)*10 + 1
|
||||
}
|
||||
|
||||
func (c *AppConfig) LogPeerNode() string {
|
||||
if c.relayMode == "public" { // memapp
|
||||
return fmt.Sprintf("%d", NodeNameToID(c.PeerNode))
|
||||
}
|
||||
return c.PeerNode
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Network NetworkConfig `json:"network"`
|
||||
Apps []*AppConfig `json:"apps"`
|
||||
|
||||
LogLevel int
|
||||
MaxLogSize int
|
||||
daemonMode bool
|
||||
mtx sync.Mutex
|
||||
sdwanMtx sync.Mutex
|
||||
sdwan SDWANInfo
|
||||
delNodes []SDWANNode
|
||||
addNodes []SDWANNode
|
||||
delNodes []*SDWANNode
|
||||
addNodes []*SDWANNode
|
||||
}
|
||||
|
||||
func (c *Config) getSDWAN() SDWANInfo {
|
||||
@@ -83,23 +93,30 @@ func (c *Config) getSDWAN() SDWANInfo {
|
||||
return c.sdwan
|
||||
}
|
||||
|
||||
func (c *Config) getDelNodes() []SDWANNode {
|
||||
func (c *Config) getDelNodes() []*SDWANNode {
|
||||
c.sdwanMtx.Lock()
|
||||
defer c.sdwanMtx.Unlock()
|
||||
return c.delNodes
|
||||
}
|
||||
|
||||
func (c *Config) getAddNodes() []SDWANNode {
|
||||
func (c *Config) getAddNodes() []*SDWANNode {
|
||||
c.sdwanMtx.Lock()
|
||||
defer c.sdwanMtx.Unlock()
|
||||
return c.addNodes
|
||||
}
|
||||
|
||||
func (c *Config) resetSDWAN() {
|
||||
c.sdwanMtx.Lock()
|
||||
defer c.sdwanMtx.Unlock()
|
||||
c.delNodes = []*SDWANNode{}
|
||||
c.addNodes = []*SDWANNode{}
|
||||
c.sdwan = SDWANInfo{}
|
||||
}
|
||||
func (c *Config) setSDWAN(s SDWANInfo) {
|
||||
c.sdwanMtx.Lock()
|
||||
defer c.sdwanMtx.Unlock()
|
||||
// get old-new
|
||||
c.delNodes = []SDWANNode{}
|
||||
c.delNodes = []*SDWANNode{}
|
||||
for _, oldNode := range c.sdwan.Nodes {
|
||||
isDeleted := true
|
||||
for _, newNode := range s.Nodes {
|
||||
@@ -113,7 +130,7 @@ func (c *Config) setSDWAN(s SDWANInfo) {
|
||||
}
|
||||
}
|
||||
// get new-old
|
||||
c.addNodes = []SDWANNode{}
|
||||
c.addNodes = []*SDWANNode{}
|
||||
for _, newNode := range s.Nodes {
|
||||
isNew := true
|
||||
for _, oldNode := range c.sdwan.Nodes {
|
||||
@@ -147,7 +164,7 @@ func (c *Config) retryApp(peerNode string) {
|
||||
GNetwork.apps.Range(func(id, i interface{}) bool {
|
||||
app := i.(*p2pApp)
|
||||
if app.config.PeerNode == peerNode {
|
||||
gLog.Println(LvDEBUG, "retry app ", peerNode)
|
||||
gLog.Println(LvDEBUG, "retry app ", app.config.LogPeerNode())
|
||||
app.config.retryNum = 0
|
||||
app.config.nextRetryTime = time.Now()
|
||||
app.retryRelayNum = 0
|
||||
@@ -157,7 +174,7 @@ func (c *Config) retryApp(peerNode string) {
|
||||
app.hbMtx.Unlock()
|
||||
}
|
||||
if app.config.RelayNode == peerNode {
|
||||
gLog.Println(LvDEBUG, "retry app ", peerNode)
|
||||
gLog.Println(LvDEBUG, "retry app ", app.config.LogPeerNode())
|
||||
app.retryRelayNum = 0
|
||||
app.nextRetryRelayTime = time.Now()
|
||||
app.hbMtx.Lock()
|
||||
@@ -171,7 +188,7 @@ func (c *Config) retryApp(peerNode string) {
|
||||
func (c *Config) retryAllApp() {
|
||||
GNetwork.apps.Range(func(id, i interface{}) bool {
|
||||
app := i.(*p2pApp)
|
||||
gLog.Println(LvDEBUG, "retry app ", app.config.PeerNode)
|
||||
gLog.Println(LvDEBUG, "retry app ", app.config.LogPeerNode())
|
||||
app.config.retryNum = 0
|
||||
app.config.nextRetryTime = time.Now()
|
||||
app.retryRelayNum = 0
|
||||
@@ -189,7 +206,7 @@ func (c *Config) retryAllMemApp() {
|
||||
if app.config.SrcPort != 0 {
|
||||
return true
|
||||
}
|
||||
gLog.Println(LvDEBUG, "retry app ", app.config.PeerNode)
|
||||
gLog.Println(LvDEBUG, "retry app ", app.config.LogPeerNode())
|
||||
app.config.retryNum = 0
|
||||
app.config.nextRetryTime = time.Now()
|
||||
app.retryRelayNum = 0
|
||||
@@ -221,17 +238,8 @@ func (c *Config) delete(app AppConfig) {
|
||||
defer c.mtx.Unlock()
|
||||
defer c.save()
|
||||
for i := 0; i < len(c.Apps); i++ {
|
||||
got := false
|
||||
if app.SrcPort != 0 { // normal p2papp
|
||||
if c.Apps[i].Protocol == app.Protocol && c.Apps[i].SrcPort == app.SrcPort {
|
||||
got = true
|
||||
}
|
||||
} else { // memapp
|
||||
if c.Apps[i].PeerNode == app.PeerNode {
|
||||
got = true
|
||||
}
|
||||
}
|
||||
if got {
|
||||
if (app.SrcPort != 0 && c.Apps[i].Protocol == app.Protocol && c.Apps[i].SrcPort == app.SrcPort) || // normal app
|
||||
(app.SrcPort == 0 && c.Apps[i].SrcPort == 0 && c.Apps[i].PeerNode == app.PeerNode) { // memapp
|
||||
if i == len(c.Apps)-1 {
|
||||
c.Apps = c.Apps[:i]
|
||||
} else {
|
||||
@@ -240,12 +248,14 @@ func (c *Config) delete(app AppConfig) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (c *Config) save() {
|
||||
// c.mtx.Lock()
|
||||
// defer c.mtx.Unlock() // internal call
|
||||
if c.Network.Token == 0 {
|
||||
return
|
||||
}
|
||||
data, _ := json.MarshalIndent(c, "", " ")
|
||||
err := os.WriteFile("config.json", data, 0644)
|
||||
if err != nil {
|
||||
@@ -256,6 +266,9 @@ func (c *Config) save() {
|
||||
func (c *Config) saveCache() {
|
||||
// c.mtx.Lock()
|
||||
// defer c.mtx.Unlock() // internal call
|
||||
if c.Network.Token == 0 {
|
||||
return
|
||||
}
|
||||
data, _ := json.MarshalIndent(c, "", " ")
|
||||
err := os.WriteFile("config.json0", data, 0644)
|
||||
if err != nil {
|
||||
@@ -265,6 +278,7 @@ func (c *Config) saveCache() {
|
||||
|
||||
func init() {
|
||||
gConf.LogLevel = int(LvINFO)
|
||||
gConf.MaxLogSize = 1024 * 1024
|
||||
gConf.Network.ShareBandwidth = 10
|
||||
gConf.Network.ServerHost = "api.openp2p.cn"
|
||||
gConf.Network.ServerPort = WsPort
|
||||
@@ -448,6 +462,9 @@ func parseParams(subCommand string, cmd string) {
|
||||
if f.Name == "loglevel" {
|
||||
gConf.LogLevel = *logLevel
|
||||
}
|
||||
if f.Name == "maxlogsize" {
|
||||
gConf.MaxLogSize = *maxLogSize
|
||||
}
|
||||
if f.Name == "tcpport" {
|
||||
gConf.Network.TCPPort = *tcpPort
|
||||
}
|
||||
|
||||
+7
-2
@@ -60,6 +60,7 @@ func (d *daemon) run() {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
args = append(args, "-nv")
|
||||
for {
|
||||
// start worker
|
||||
@@ -72,6 +73,7 @@ func (d *daemon) run() {
|
||||
}
|
||||
gLog.Println(LvINFO, "start worker process, args:", args)
|
||||
execSpec := &os.ProcAttr{Env: append(os.Environ(), "GOTRACEBACK=crash"), Files: []*os.File{os.Stdin, os.Stdout, f}}
|
||||
lastRebootTime := time.Now()
|
||||
p, err := os.StartProcess(binPath, args, execSpec)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "start worker error:%s", err)
|
||||
@@ -88,8 +90,11 @@ func (d *daemon) run() {
|
||||
if !d.running {
|
||||
return
|
||||
}
|
||||
gLog.Printf(LvERROR, "worker stop, restart it after 10s")
|
||||
time.Sleep(time.Second * 10)
|
||||
if time.Since(lastRebootTime) < time.Second*10 {
|
||||
gLog.Printf(LvERROR, "worker stop, restart it after 10s")
|
||||
time.Sleep(time.Second * 10)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,4 +29,5 @@ var (
|
||||
ErrPeerConnectRelay = errors.New("peer connect relayNode error")
|
||||
ErrBuildTunnelBusy = errors.New("build tunnel busy")
|
||||
ErrMemAppTunnelNotFound = errors.New("memapp tunnel not found")
|
||||
ErrRemoteServiceUnable = errors.New("remote service unable")
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@@ -44,6 +45,7 @@ func handlePush(subType uint16, msg []byte) error {
|
||||
config := AppConfig{}
|
||||
config.PeerNode = req.RelayName
|
||||
config.peerToken = req.RelayToken
|
||||
config.relayMode = req.RelayMode
|
||||
go func(r AddRelayTunnelReq) {
|
||||
t, errDt := GNetwork.addDirectTunnel(config, 0)
|
||||
if errDt == nil {
|
||||
@@ -142,6 +144,8 @@ func handlePush(subType uint16, msg []byte) error {
|
||||
err = handleLog(msg)
|
||||
case MsgPushReportGoroutine:
|
||||
err = handleReportGoroutine()
|
||||
case MsgPushCheckRemoteService:
|
||||
err = handleCheckRemoteService(msg)
|
||||
case MsgPushEditApp:
|
||||
err = handleEditApp(msg)
|
||||
case MsgPushEditNode:
|
||||
@@ -458,3 +462,21 @@ func handleReportGoroutine() (err error) {
|
||||
stackLen := runtime.Stack(buf, true)
|
||||
return GNetwork.write(MsgReport, MsgPushReportLog, string(buf[:stackLen]))
|
||||
}
|
||||
|
||||
func handleCheckRemoteService(msg []byte) (err error) {
|
||||
gLog.Println(LvDEBUG, "handleCheckRemoteService")
|
||||
req := CheckRemoteService{}
|
||||
if err = json.Unmarshal(msg[openP2PHeaderSize:], &req); err != nil {
|
||||
gLog.Printf(LvERROR, "wrong %v:%s %s", reflect.TypeOf(req), err, string(msg[openP2PHeaderSize:]))
|
||||
return err
|
||||
}
|
||||
rsp := PushRsp{Error: 0}
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", req.Host, req.Port), time.Second*3)
|
||||
if err != nil {
|
||||
rsp.Error = 1
|
||||
rsp.Detail = ErrRemoteServiceUnable.Error()
|
||||
} else {
|
||||
conn.Close()
|
||||
}
|
||||
return GNetwork.write(MsgReport, MsgReportResponse, rsp)
|
||||
}
|
||||
|
||||
+15
-15
@@ -13,12 +13,12 @@ import (
|
||||
func handshakeC2C(t *P2PTunnel) (err error) {
|
||||
gLog.Printf(LvDEBUG, "handshakeC2C %s:%d:%d to %s:%d", gConf.Network.Node, t.coneLocalPort, t.coneNatPort, t.config.peerIP, t.config.peerConeNatPort)
|
||||
defer gLog.Printf(LvDEBUG, "handshakeC2C end")
|
||||
conn, err := net.ListenUDP("udp", t.la)
|
||||
conn, err := net.ListenUDP("udp", t.localHoleAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
_, err = UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id})
|
||||
_, err = UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id})
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshake error:", err)
|
||||
return err
|
||||
@@ -28,7 +28,7 @@ func handshakeC2C(t *P2PTunnel) (err error) {
|
||||
gLog.Println(LvDEBUG, "handshakeC2C read MsgPunchHandshake error:", err)
|
||||
return err
|
||||
}
|
||||
t.ra, _ = net.ResolveUDPAddr("udp", ra.String())
|
||||
t.remoteHoleAddr, _ = net.ResolveUDPAddr("udp", ra.String())
|
||||
var tunnelID uint64
|
||||
if len(buff) > openP2PHeaderSize {
|
||||
req := P2PHandshakeReq{}
|
||||
@@ -40,7 +40,7 @@ func handshakeC2C(t *P2PTunnel) (err error) {
|
||||
}
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake && tunnelID == t.id {
|
||||
gLog.Printf(LvDEBUG, "read %d handshake ", t.id)
|
||||
UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
_, head, _, _, err = UDPRead(conn, HandshakeTimeout)
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err)
|
||||
@@ -49,7 +49,7 @@ func handshakeC2C(t *P2PTunnel) (err error) {
|
||||
}
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck && tunnelID == t.id {
|
||||
gLog.Printf(LvDEBUG, "read %d handshake ack ", t.id)
|
||||
_, err = UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
_, err = UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err)
|
||||
return err
|
||||
@@ -70,7 +70,7 @@ func handshakeC2S(t *P2PTunnel) error {
|
||||
startTime := time.Now()
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
randPorts := r.Perm(65532)
|
||||
conn, err := net.ListenUDP("udp", t.la)
|
||||
conn, err := net.ListenUDP("udp", t.localHoleAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -111,7 +111,7 @@ func handshakeC2S(t *P2PTunnel) error {
|
||||
gLog.Println(LvERROR, "parse p2pheader error:", err)
|
||||
return err
|
||||
}
|
||||
t.ra, _ = net.ResolveUDPAddr("udp", dst.String())
|
||||
t.remoteHoleAddr, _ = net.ResolveUDPAddr("udp", dst.String())
|
||||
var tunnelID uint64
|
||||
if len(buff) > openP2PHeaderSize {
|
||||
req := P2PHandshakeReq{}
|
||||
@@ -123,7 +123,7 @@ func handshakeC2S(t *P2PTunnel) error {
|
||||
}
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake && tunnelID == t.id {
|
||||
gLog.Printf(LvDEBUG, "handshakeC2S read %d handshake ", t.id)
|
||||
UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
for {
|
||||
_, head, buff, _, err = UDPRead(conn, HandshakeTimeout)
|
||||
if err != nil {
|
||||
@@ -146,8 +146,8 @@ func handshakeC2S(t *P2PTunnel) error {
|
||||
}
|
||||
}
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
|
||||
gLog.Printf(LvDEBUG, "handshakeC2S read %d handshake ack %s", t.id, t.ra.String())
|
||||
_, err = UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
gLog.Printf(LvDEBUG, "handshakeC2S read %d handshake ack %s", t.id, t.remoteHoleAddr.String())
|
||||
_, err = UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
return err
|
||||
} else {
|
||||
gLog.Println(LvDEBUG, "handshakeS2C read msg but not MsgPunchHandshakeAck")
|
||||
@@ -178,7 +178,7 @@ func handshakeS2C(t *P2PTunnel) error {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id})
|
||||
UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id})
|
||||
_, head, buff, _, err := UDPRead(conn, HandshakeTimeout)
|
||||
if err != nil {
|
||||
// gLog.Println(LevelDEBUG, "one of the handshake error:", err)
|
||||
@@ -199,7 +199,7 @@ func handshakeS2C(t *P2PTunnel) error {
|
||||
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake && tunnelID == t.id {
|
||||
gLog.Printf(LvDEBUG, "handshakeS2C read %d handshake ", t.id)
|
||||
UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
// may read several MsgPunchHandshake
|
||||
for {
|
||||
_, head, buff, _, err = UDPRead(conn, HandshakeTimeout)
|
||||
@@ -224,7 +224,7 @@ func handshakeS2C(t *P2PTunnel) error {
|
||||
}
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
|
||||
gLog.Printf(LvDEBUG, "handshakeS2C read %d handshake ack %s", t.id, conn.LocalAddr().String())
|
||||
UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
UDPWrite(conn, t.remoteHoleAddr, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
gotIt = true
|
||||
la, _ := net.ResolveUDPAddr("udp", conn.LocalAddr().String())
|
||||
gotCh <- la
|
||||
@@ -238,14 +238,14 @@ func handshakeS2C(t *P2PTunnel) error {
|
||||
gLog.Printf(LvDEBUG, "send symmetric handshake end")
|
||||
if compareVersion(t.config.peerVersion, SymmetricSimultaneouslySendVersion) < 0 { // compatible with old client
|
||||
gLog.Println(LvDEBUG, "handshakeS2C ready, notify peer connect")
|
||||
t.pn.push(t.config.PeerNode, MsgPushHandshakeStart, TunnelMsg{ID: t.id})
|
||||
GNetwork.push(t.config.PeerNode, MsgPushHandshakeStart, TunnelMsg{ID: t.id})
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(HandshakeTimeout):
|
||||
return fmt.Errorf("wait handshake timeout")
|
||||
case la := <-gotCh:
|
||||
t.la = la
|
||||
t.localHoleAddr = la
|
||||
gLog.Println(LvDEBUG, "symmetric handshake ok", la)
|
||||
gLog.Printf(LvINFO, "handshakeS2C ok. cost %dms", time.Since(startTime)/time.Millisecond)
|
||||
}
|
||||
|
||||
+22
-18
@@ -66,7 +66,7 @@ func natTest(serverHost string, serverPort int, localPort int) (publicIP string,
|
||||
}
|
||||
|
||||
// The connection can write data to the desired address.
|
||||
msg, err := newMessage(MsgNATDetect, 0, nil)
|
||||
msg, err := newMessage(MsgNATDetect, MsgNAT, nil)
|
||||
_, err = conn.WriteTo(msg, dst)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
@@ -121,18 +121,6 @@ func publicIPTest(publicIP string, echoPort int) (hasPublicIP int, hasUPNPorNATP
|
||||
return
|
||||
}
|
||||
defer echoConn.Close()
|
||||
go func() {
|
||||
// close outside for breaking the ReadFromUDP
|
||||
// wait 30s for echo testing
|
||||
buf := make([]byte, 1600)
|
||||
echoConn.SetReadDeadline(time.Now().Add(time.Second * 30))
|
||||
n, addr, err := echoConn.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
echoConn.WriteToUDP(buf[0:n], addr)
|
||||
gLog.Println(LvDEBUG, "echo server end")
|
||||
}()
|
||||
// testing for public ip
|
||||
for i := 0; i < 2; i++ {
|
||||
if i == 1 {
|
||||
@@ -164,17 +152,33 @@ func publicIPTest(publicIP string, echoPort int) (hasPublicIP int, hasUPNPorNATP
|
||||
break
|
||||
}
|
||||
defer conn.Close()
|
||||
dst, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", publicIP, echoPort))
|
||||
dst, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", gConf.Network.ServerHost, gConf.Network.ServerPort))
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
conn.WriteTo([]byte("echo"), dst)
|
||||
|
||||
// The connection can write data to the desired address.
|
||||
msg, _ := newMessage(MsgNATDetect, MsgPublicIP, NatDetectReq{EchoPort: echoPort})
|
||||
_, err = conn.WriteTo(msg, dst)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
buf := make([]byte, 1600)
|
||||
|
||||
// wait for echo testing
|
||||
conn.SetReadDeadline(time.Now().Add(PublicIPEchoTimeout))
|
||||
_, _, err = conn.ReadFromUDP(buf)
|
||||
if err == nil {
|
||||
echoConn.SetReadDeadline(time.Now().Add(PublicIPEchoTimeout))
|
||||
nRead, _, err := echoConn.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "PublicIP detect error:", err)
|
||||
continue
|
||||
}
|
||||
natRsp := NatDetectRsp{}
|
||||
err = json.Unmarshal(buf[openP2PHeaderSize:nRead], &natRsp)
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "PublicIP detect error:", err)
|
||||
continue
|
||||
}
|
||||
if natRsp.Port == echoPort {
|
||||
if i == 1 {
|
||||
gLog.Println(LvDEBUG, "UPNP or NAT-PMP:YES")
|
||||
hasUPNPorNATPMP = 1
|
||||
|
||||
@@ -52,7 +52,7 @@ func addRoute(dst, gw, ifname string) error {
|
||||
}
|
||||
|
||||
func delRoute(dst, gw string) error {
|
||||
err := exec.Command("route", "delete", dst, gw).Run()
|
||||
err := exec.Command("route", "delete", dst, "-gateway", gw).Run()
|
||||
return err
|
||||
}
|
||||
func delRoutesByGateway(gateway string) error {
|
||||
@@ -68,13 +68,14 @@ func delRoutesByGateway(gateway string) error {
|
||||
continue
|
||||
}
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) >= 7 && fields[0] == "default" && fields[len(fields)-1] == gateway {
|
||||
delCmd := exec.Command("route", "delete", "default", gateway)
|
||||
err := delCmd.Run()
|
||||
if len(fields) >= 2 {
|
||||
cmd := exec.Command("route", "delete", fields[0], gateway)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
gLog.Printf(LvERROR, "Delete route %s error:%s", fields[0], err)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("Delete route ok: %s %s\n", "default", gateway)
|
||||
gLog.Printf(LvINFO, "Delete route ok: %s %s\n", fields[0], gateway)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
+3
-2
@@ -124,9 +124,10 @@ func delRoutesByGateway(gateway string) error {
|
||||
delCmd := exec.Command("route", "del", "-net", fields[0], "gw", gateway)
|
||||
err := delCmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
gLog.Printf(LvERROR, "Delete route %s error:%s", fields[0], err)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("Delete route ok: %s %s %s\n", fields[0], fields[1], gateway)
|
||||
gLog.Printf(LvINFO, "Delete route ok: %s %s %s\n", fields[0], fields[1], gateway)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -133,9 +133,10 @@ func delRoutesByGateway(gateway string) error {
|
||||
cmd := exec.Command("route", "delete", fields[0], "mask", fields[1], gateway)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
fmt.Println("Delete route error:", err)
|
||||
gLog.Printf(LvERROR, "Delete route %s error:%s", fields[0], err)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("Delete route ok: %s %s %s\n", fields[0], fields[1], gateway)
|
||||
gLog.Printf(LvINFO, "Delete route ok: %s %s %s\n", fields[0], fields[1], gateway)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
+14
-14
@@ -138,7 +138,7 @@ func (app *p2pApp) checkDirectTunnel() error {
|
||||
app.config.retryNum = 1
|
||||
}
|
||||
if app.config.retryNum > 0 { // first time not show reconnect log
|
||||
gLog.Printf(LvINFO, "detect app %s appid:%d disconnect, reconnecting the %d times...", app.config.PeerNode, app.id, app.config.retryNum)
|
||||
gLog.Printf(LvINFO, "detect app %s appid:%d disconnect, reconnecting the %d times...", app.config.LogPeerNode(), app.id, app.config.retryNum)
|
||||
}
|
||||
app.config.retryNum++
|
||||
app.config.retryTime = time.Now()
|
||||
@@ -149,7 +149,7 @@ func (app *p2pApp) checkDirectTunnel() error {
|
||||
app.config.errMsg = err.Error()
|
||||
if err == ErrPeerOffline && app.config.retryNum > 2 { // stop retry, waiting for online
|
||||
app.config.retryNum = retryLimit
|
||||
gLog.Printf(LvINFO, " %s offline, it will auto reconnect when peer node online", app.config.PeerNode)
|
||||
gLog.Printf(LvINFO, " %s offline, it will auto reconnect when peer node online", app.config.LogPeerNode())
|
||||
}
|
||||
if err == ErrBuildTunnelBusy {
|
||||
app.config.retryNum--
|
||||
@@ -174,7 +174,7 @@ func (app *p2pApp) buildDirectTunnel() error {
|
||||
pn := GNetwork
|
||||
initErr := pn.requestPeerInfo(&app.config)
|
||||
if initErr != nil {
|
||||
gLog.Printf(LvERROR, "%s init error:%s", app.config.PeerNode, initErr)
|
||||
gLog.Printf(LvERROR, "%s requestPeerInfo error:%s", app.config.LogPeerNode(), initErr)
|
||||
return initErr
|
||||
}
|
||||
t, err = pn.addDirectTunnel(app.config, 0)
|
||||
@@ -212,7 +212,7 @@ func (app *p2pApp) buildDirectTunnel() error {
|
||||
AppID: app.id,
|
||||
AppKey: app.key,
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "sync appkey direct to %s", app.config.PeerNode)
|
||||
gLog.Printf(LvDEBUG, "sync appkey direct to %s", app.config.LogPeerNode())
|
||||
pn.push(app.config.PeerNode, MsgPushAPPKey, &syncKeyReq)
|
||||
app.setDirectTunnel(t)
|
||||
|
||||
@@ -220,7 +220,7 @@ func (app *p2pApp) buildDirectTunnel() error {
|
||||
if app.config.SrcPort == 0 {
|
||||
req := ServerSideSaveMemApp{From: gConf.Network.Node, Node: gConf.Network.Node, TunnelID: t.id, RelayTunnelID: 0, AppID: app.id}
|
||||
pn.push(app.config.PeerNode, MsgPushServerSideSaveMemApp, &req)
|
||||
gLog.Printf(LvDEBUG, "push %s ServerSideSaveMemApp: %s", app.config.PeerNode, prettyJson(req))
|
||||
gLog.Printf(LvDEBUG, "push %s ServerSideSaveMemApp: %s", app.config.LogPeerNode(), prettyJson(req))
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "%s use tunnel %d", app.config.AppName, t.id)
|
||||
return nil
|
||||
@@ -244,7 +244,7 @@ func (app *p2pApp) checkRelayTunnel() error {
|
||||
app.retryRelayNum = 1
|
||||
}
|
||||
if app.retryRelayNum > 0 { // first time not show reconnect log
|
||||
gLog.Printf(LvINFO, "detect app %s appid:%d relay disconnect, reconnecting the %d times...", app.config.PeerNode, app.id, app.retryRelayNum)
|
||||
gLog.Printf(LvINFO, "detect app %s appid:%d relay disconnect, reconnecting the %d times...", app.config.LogPeerNode(), app.id, app.retryRelayNum)
|
||||
}
|
||||
app.setRelayTunnel(nil) // reset relayTunnel
|
||||
app.retryRelayNum++
|
||||
@@ -256,7 +256,7 @@ func (app *p2pApp) checkRelayTunnel() error {
|
||||
app.errMsg = err.Error()
|
||||
if err == ErrPeerOffline && app.retryRelayNum > 2 { // stop retry, waiting for online
|
||||
app.retryRelayNum = retryLimit
|
||||
gLog.Printf(LvINFO, " %s offline, it will auto reconnect when peer node online", app.config.PeerNode)
|
||||
gLog.Printf(LvINFO, " %s offline, it will auto reconnect when peer node online", app.config.LogPeerNode())
|
||||
}
|
||||
}
|
||||
if app.Tunnel() != nil {
|
||||
@@ -282,7 +282,7 @@ func (app *p2pApp) buildRelayTunnel() error {
|
||||
config := app.config
|
||||
initErr := pn.requestPeerInfo(&config)
|
||||
if initErr != nil {
|
||||
gLog.Printf(LvERROR, "%s init error:%s", config.PeerNode, initErr)
|
||||
gLog.Printf(LvERROR, "%s init error:%s", config.LogPeerNode(), initErr)
|
||||
return initErr
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ func (app *p2pApp) buildRelayTunnel() error {
|
||||
AppID: app.id,
|
||||
AppKey: app.key,
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "sync appkey relay to %s", config.PeerNode)
|
||||
gLog.Printf(LvDEBUG, "sync appkey relay to %s", config.LogPeerNode())
|
||||
pn.push(config.PeerNode, MsgPushAPPKey, &syncKeyReq)
|
||||
app.setRelayTunnelID(rtid)
|
||||
app.setRelayTunnel(t)
|
||||
@@ -330,7 +330,7 @@ func (app *p2pApp) buildRelayTunnel() error {
|
||||
if config.SrcPort == 0 {
|
||||
req := ServerSideSaveMemApp{From: gConf.Network.Node, Node: relayNode, TunnelID: rtid, RelayTunnelID: t.id, AppID: app.id, RelayMode: relayMode}
|
||||
pn.push(config.PeerNode, MsgPushServerSideSaveMemApp, &req)
|
||||
gLog.Printf(LvDEBUG, "push %s relay ServerSideSaveMemApp: %s", config.PeerNode, prettyJson(req))
|
||||
gLog.Printf(LvDEBUG, "push %s relay ServerSideSaveMemApp: %s", config.LogPeerNode(), prettyJson(req))
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "%s use tunnel %d", app.config.AppName, t.id)
|
||||
return nil
|
||||
@@ -594,8 +594,8 @@ func (app *p2pApp) close() {
|
||||
func (app *p2pApp) relayHeartbeatLoop() {
|
||||
app.wg.Add(1)
|
||||
defer app.wg.Done()
|
||||
gLog.Printf(LvDEBUG, "%s appid:%d relayHeartbeat to rtid:%d start", app.config.PeerNode, app.id, app.rtid)
|
||||
defer gLog.Printf(LvDEBUG, "%s appid:%d relayHeartbeat to rtid%d end", app.config.PeerNode, app.id, app.rtid)
|
||||
gLog.Printf(LvDEBUG, "%s appid:%d relayHeartbeat to rtid:%d start", app.config.LogPeerNode(), app.id, app.rtid)
|
||||
defer gLog.Printf(LvDEBUG, "%s appid:%d relayHeartbeat to rtid%d end", app.config.LogPeerNode(), app.id, app.rtid)
|
||||
|
||||
for app.running {
|
||||
if app.RelayTunnel() == nil || !app.RelayTunnel().isRuning() {
|
||||
@@ -606,11 +606,11 @@ func (app *p2pApp) relayHeartbeatLoop() {
|
||||
AppID: app.id}
|
||||
err := app.RelayTunnel().WriteMessage(app.rtid, MsgP2P, MsgRelayHeartbeat, &req)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "%s appid:%d rtid:%d write relay tunnel heartbeat error %s", app.config.PeerNode, app.id, app.rtid, err)
|
||||
gLog.Printf(LvERROR, "%s appid:%d rtid:%d write relay tunnel heartbeat error %s", app.config.LogPeerNode(), app.id, app.rtid, err)
|
||||
return
|
||||
}
|
||||
// TODO: debug relay heartbeat
|
||||
gLog.Printf(LvDEBUG, "%s appid:%d rtid:%d write relay tunnel heartbeat ok", app.config.PeerNode, app.id, app.rtid)
|
||||
gLog.Printf(LvDEBUG, "%s appid:%d rtid:%d write relay tunnel heartbeat ok", app.config.LogPeerNode(), app.id, app.rtid)
|
||||
time.Sleep(TunnelHeartbeatTime)
|
||||
}
|
||||
}
|
||||
|
||||
+23
-24
@@ -115,6 +115,7 @@ func (pn *P2PNetwork) run() {
|
||||
pn.write(MsgHeartbeat, 0, "")
|
||||
case <-pn.restartCh:
|
||||
gLog.Printf(LvDEBUG, "got restart channel")
|
||||
pn.sdwan.reset()
|
||||
pn.online = false
|
||||
pn.wgReconnect.Wait() // wait read/autorunapp goroutine end
|
||||
delay := ClientAPITimeout + time.Duration(rand.Int()%pn.loginMaxDelaySeconds)*time.Second
|
||||
@@ -124,8 +125,9 @@ func (pn *P2PNetwork) run() {
|
||||
gLog.Println(LvERROR, "P2PNetwork init error:", err)
|
||||
}
|
||||
gConf.retryAllApp()
|
||||
|
||||
case t := <-pn.tunnelCloseCh:
|
||||
gLog.Printf(LvDEBUG, "got tunnelCloseCh %s", t.config.PeerNode)
|
||||
gLog.Printf(LvDEBUG, "got tunnelCloseCh %s", t.config.LogPeerNode())
|
||||
pn.apps.Range(func(id, i interface{}) bool {
|
||||
app := i.(*p2pApp)
|
||||
if app.DirectTunnel() == t {
|
||||
@@ -195,12 +197,12 @@ func (pn *P2PNetwork) autorunApp() {
|
||||
}
|
||||
|
||||
func (pn *P2PNetwork) addRelayTunnel(config AppConfig) (*P2PTunnel, uint64, string, error) {
|
||||
gLog.Printf(LvINFO, "addRelayTunnel to %s start", config.PeerNode)
|
||||
defer gLog.Printf(LvINFO, "addRelayTunnel to %s end", config.PeerNode)
|
||||
gLog.Printf(LvINFO, "addRelayTunnel to %s start", config.LogPeerNode())
|
||||
defer gLog.Printf(LvINFO, "addRelayTunnel to %s end", config.LogPeerNode())
|
||||
relayConfig := AppConfig{
|
||||
PeerNode: config.RelayNode,
|
||||
peerToken: config.peerToken}
|
||||
relayMode := "private"
|
||||
peerToken: config.peerToken,
|
||||
relayMode: "private"}
|
||||
if relayConfig.PeerNode == "" {
|
||||
// find existing relay tunnel
|
||||
pn.apps.Range(func(id, i interface{}) bool {
|
||||
@@ -212,7 +214,7 @@ func (pn *P2PNetwork) addRelayTunnel(config AppConfig) (*P2PTunnel, uint64, stri
|
||||
return true
|
||||
}
|
||||
relayConfig.PeerNode = app.RelayTunnel().config.PeerNode
|
||||
gLog.Printf(LvDEBUG, "found existing relay tunnel %s", relayConfig.PeerNode)
|
||||
gLog.Printf(LvDEBUG, "found existing relay tunnel %s", relayConfig.LogPeerNode())
|
||||
return false
|
||||
})
|
||||
if relayConfig.PeerNode == "" { // request relay node
|
||||
@@ -231,11 +233,11 @@ func (pn *P2PNetwork) addRelayTunnel(config AppConfig) (*P2PTunnel, uint64, stri
|
||||
gLog.Printf(LvERROR, "MsgRelayNodeReq error")
|
||||
return nil, 0, "", errors.New("MsgRelayNodeReq error")
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "got relay node:%s", rsp.RelayName)
|
||||
gLog.Printf(LvDEBUG, "got relay node:%s", relayConfig.LogPeerNode())
|
||||
|
||||
relayConfig.PeerNode = rsp.RelayName
|
||||
relayConfig.peerToken = rsp.RelayToken
|
||||
relayMode = rsp.Mode
|
||||
relayConfig.relayMode = rsp.Mode
|
||||
}
|
||||
|
||||
}
|
||||
@@ -250,10 +252,10 @@ func (pn *P2PNetwork) addRelayTunnel(config AppConfig) (*P2PTunnel, uint64, stri
|
||||
From: gConf.Network.Node,
|
||||
RelayName: relayConfig.PeerNode,
|
||||
RelayToken: relayConfig.peerToken,
|
||||
RelayMode: relayMode,
|
||||
RelayMode: relayConfig.relayMode,
|
||||
RelayTunnelID: t.id,
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "push %s the relay node(%s)", config.PeerNode, relayConfig.PeerNode)
|
||||
gLog.Printf(LvDEBUG, "push %s the relay node(%s)", config.LogPeerNode(), relayConfig.LogPeerNode())
|
||||
pn.push(config.PeerNode, MsgPushAddRelayTunnelReq, &req)
|
||||
|
||||
// wait relay ready
|
||||
@@ -267,13 +269,13 @@ func (pn *P2PNetwork) addRelayTunnel(config AppConfig) (*P2PTunnel, uint64, stri
|
||||
gLog.Println(LvDEBUG, ErrPeerConnectRelay)
|
||||
return nil, 0, "", ErrPeerConnectRelay
|
||||
}
|
||||
return t, rspID.ID, relayMode, err
|
||||
return t, rspID.ID, relayConfig.relayMode, err
|
||||
}
|
||||
|
||||
// use *AppConfig to save status
|
||||
func (pn *P2PNetwork) AddApp(config AppConfig) error {
|
||||
gLog.Printf(LvINFO, "addApp %s to %s:%s:%d start", config.AppName, config.PeerNode, config.DstHost, config.DstPort)
|
||||
defer gLog.Printf(LvINFO, "addApp %s to %s:%s:%d end", config.AppName, config.PeerNode, config.DstHost, config.DstPort)
|
||||
gLog.Printf(LvINFO, "addApp %s to %s:%s:%d start", config.AppName, config.LogPeerNode(), config.DstHost, config.DstPort)
|
||||
defer gLog.Printf(LvINFO, "addApp %s to %s:%s:%d end", config.AppName, config.LogPeerNode(), config.DstHost, config.DstPort)
|
||||
if !pn.online {
|
||||
return errors.New("P2PNetwork offline")
|
||||
}
|
||||
@@ -304,8 +306,8 @@ func (pn *P2PNetwork) AddApp(config AppConfig) error {
|
||||
}
|
||||
|
||||
func (pn *P2PNetwork) DeleteApp(config AppConfig) {
|
||||
gLog.Printf(LvINFO, "DeleteApp %s to %s:%s:%d start", config.AppName, config.PeerNode, config.DstHost, config.DstPort)
|
||||
defer gLog.Printf(LvINFO, "DeleteApp %s to %s:%s:%d end", config.AppName, config.PeerNode, config.DstHost, config.DstPort)
|
||||
gLog.Printf(LvINFO, "DeleteApp %s to %s:%s:%d start", config.AppName, config.LogPeerNode(), config.DstHost, config.DstPort)
|
||||
defer gLog.Printf(LvINFO, "DeleteApp %s to %s:%s:%d end", config.AppName, config.LogPeerNode(), config.DstHost, config.DstPort)
|
||||
// close the apps of this config
|
||||
i, ok := pn.apps.Load(config.ID())
|
||||
if ok {
|
||||
@@ -339,8 +341,8 @@ func (pn *P2PNetwork) findTunnel(peerNode string) (t *P2PTunnel) {
|
||||
}
|
||||
|
||||
func (pn *P2PNetwork) addDirectTunnel(config AppConfig, tid uint64) (t *P2PTunnel, err error) {
|
||||
gLog.Printf(LvDEBUG, "addDirectTunnel %s%d to %s:%s:%d tid:%d start", config.Protocol, config.SrcPort, config.PeerNode, config.DstHost, config.DstPort, tid)
|
||||
defer gLog.Printf(LvDEBUG, "addDirectTunnel %s%d to %s:%s:%d tid:%d end", config.Protocol, config.SrcPort, config.PeerNode, config.DstHost, config.DstPort, tid)
|
||||
gLog.Printf(LvDEBUG, "addDirectTunnel %s%d to %s:%s:%d tid:%d start", config.Protocol, config.SrcPort, config.LogPeerNode(), config.DstHost, config.DstPort, tid)
|
||||
defer gLog.Printf(LvDEBUG, "addDirectTunnel %s%d to %s:%s:%d tid:%d end", config.Protocol, config.SrcPort, config.LogPeerNode(), config.DstHost, config.DstPort, tid)
|
||||
isClient := false
|
||||
// client side tid=0, assign random uint64
|
||||
if tid == 0 {
|
||||
@@ -360,12 +362,12 @@ func (pn *P2PNetwork) addDirectTunnel(config AppConfig, tid uint64) (t *P2PTunne
|
||||
// peer info
|
||||
initErr := pn.requestPeerInfo(&config)
|
||||
if initErr != nil {
|
||||
gLog.Printf(LvERROR, "%s init error:%s", config.PeerNode, initErr)
|
||||
gLog.Printf(LvERROR, "%s init error:%s", config.LogPeerNode(), initErr)
|
||||
|
||||
return nil, initErr
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "config.peerNode=%s,config.peerVersion=%s,config.peerIP=%s,config.peerLanIP=%s,gConf.Network.publicIP=%s,config.peerIPv6=%s,config.hasIPv4=%d,config.hasUPNPorNATPMP=%d,gConf.Network.hasIPv4=%d,gConf.Network.hasUPNPorNATPMP=%d,config.peerNatType=%d,gConf.Network.natType=%d,",
|
||||
config.PeerNode, config.peerVersion, config.peerIP, config.peerLanIP, gConf.Network.publicIP, config.peerIPv6, config.hasIPv4, config.hasUPNPorNATPMP, gConf.Network.hasIPv4, gConf.Network.hasUPNPorNATPMP, config.peerNatType, gConf.Network.natType)
|
||||
config.LogPeerNode(), config.peerVersion, config.peerIP, config.peerLanIP, gConf.Network.publicIP, config.peerIPv6, config.hasIPv4, config.hasUPNPorNATPMP, gConf.Network.hasIPv4, gConf.Network.hasUPNPorNATPMP, config.peerNatType, gConf.Network.natType)
|
||||
// try Intranet
|
||||
if config.peerIP == gConf.Network.publicIP && compareVersion(config.peerVersion, SupportIntranetVersion) >= 0 { // old version client has no peerLanIP
|
||||
gLog.Println(LvINFO, "try Intranet")
|
||||
@@ -466,7 +468,7 @@ func (pn *P2PNetwork) newTunnel(config AppConfig, tid uint64, isClient bool) (t
|
||||
}
|
||||
}
|
||||
|
||||
t = &P2PTunnel{pn: pn,
|
||||
t = &P2PTunnel{
|
||||
config: config,
|
||||
id: tid,
|
||||
writeData: make(chan []byte, WriteDataChanSize),
|
||||
@@ -533,7 +535,6 @@ func (pn *P2PNetwork) init() error {
|
||||
caCertPool, errCert := x509.SystemCertPool()
|
||||
if errCert != nil {
|
||||
gLog.Println(LvERROR, "Failed to load system root CAs:", errCert)
|
||||
} else {
|
||||
caCertPool = x509.NewCertPool()
|
||||
}
|
||||
caCertPool.AppendCertsFromPEM([]byte(rootCA))
|
||||
@@ -624,8 +625,6 @@ func (pn *P2PNetwork) handleMessage(msg []byte) {
|
||||
gLog.Printf(LvERROR, "login error:%d, detail:%s", rsp.Error, rsp.Detail)
|
||||
pn.running = false
|
||||
} else {
|
||||
gConf.Network.Token = rsp.Token
|
||||
gConf.Network.User = rsp.User
|
||||
gConf.setToken(rsp.Token)
|
||||
gConf.setUser(rsp.User)
|
||||
if len(rsp.Node) >= MinNodeNameLen {
|
||||
@@ -726,7 +725,7 @@ func (pn *P2PNetwork) relay(to uint64, body []byte) error {
|
||||
}
|
||||
|
||||
func (pn *P2PNetwork) push(to string, subType uint16, packet interface{}) error {
|
||||
gLog.Printf(LvDEBUG, "push msgType %d to %s", subType, to)
|
||||
// gLog.Printf(LvDEBUG, "push msgType %d to %s", subType, to)
|
||||
if !pn.online {
|
||||
return errors.New("client offline")
|
||||
}
|
||||
|
||||
+54
-55
@@ -19,13 +19,12 @@ const WriteDataChanSize int = 3000
|
||||
var buildTunnelMtx sync.Mutex
|
||||
|
||||
type P2PTunnel struct {
|
||||
pn *P2PNetwork
|
||||
conn underlay
|
||||
hbTime time.Time
|
||||
hbMtx sync.Mutex
|
||||
config AppConfig
|
||||
la *net.UDPAddr // local hole address
|
||||
ra *net.UDPAddr // remote hole address
|
||||
localHoleAddr *net.UDPAddr // local hole address
|
||||
remoteHoleAddr *net.UDPAddr // remote hole address
|
||||
overlayConns sync.Map // both TCP and UDP
|
||||
id uint64 // client side alloc rand.uint64 = server side
|
||||
running bool
|
||||
@@ -58,12 +57,12 @@ func (t *P2PTunnel) initPort() {
|
||||
t.coneLocalPort = localPort2
|
||||
t.coneNatPort = natPort
|
||||
}
|
||||
t.la = &net.UDPAddr{IP: net.ParseIP(gConf.Network.localIP), Port: t.coneLocalPort}
|
||||
t.localHoleAddr = &net.UDPAddr{IP: net.ParseIP(gConf.Network.localIP), Port: t.coneLocalPort}
|
||||
gLog.Printf(LvDEBUG, "prepare punching port %d:%d", t.coneLocalPort, t.coneNatPort)
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) connect() error {
|
||||
gLog.Printf(LvDEBUG, "start p2pTunnel to %s ", t.config.PeerNode)
|
||||
gLog.Printf(LvDEBUG, "start p2pTunnel to %s ", t.config.LogPeerNode())
|
||||
t.tunnelServer = false
|
||||
appKey := uint64(0)
|
||||
req := PushConnectReq{
|
||||
@@ -85,8 +84,8 @@ func (t *P2PTunnel) connect() error {
|
||||
if req.Token == 0 { // no relay token
|
||||
req.Token = gConf.Network.Token
|
||||
}
|
||||
t.pn.push(t.config.PeerNode, MsgPushConnectReq, req)
|
||||
head, body := t.pn.read(t.config.PeerNode, MsgPush, MsgPushConnectRsp, UnderlayConnectTimeout*3)
|
||||
GNetwork.push(t.config.PeerNode, MsgPushConnectReq, req)
|
||||
head, body := GNetwork.read(t.config.PeerNode, MsgPush, MsgPushConnectRsp, UnderlayConnectTimeout*3)
|
||||
if head == nil {
|
||||
return errors.New("connect error")
|
||||
}
|
||||
@@ -161,7 +160,7 @@ func (t *P2PTunnel) checkActive() bool {
|
||||
|
||||
// call when user delete tunnel
|
||||
func (t *P2PTunnel) close() {
|
||||
t.pn.NotifyTunnelClose(t)
|
||||
GNetwork.NotifyTunnelClose(t)
|
||||
if !t.running {
|
||||
return
|
||||
}
|
||||
@@ -169,8 +168,8 @@ func (t *P2PTunnel) close() {
|
||||
if t.conn != nil {
|
||||
t.conn.Close()
|
||||
}
|
||||
t.pn.allTunnels.Delete(t.id)
|
||||
gLog.Printf(LvINFO, "%d p2ptunnel close %s ", t.id, t.config.PeerNode)
|
||||
GNetwork.allTunnels.Delete(t.id)
|
||||
gLog.Printf(LvINFO, "%d p2ptunnel close %s ", t.id, t.config.LogPeerNode())
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) start() error {
|
||||
@@ -190,7 +189,7 @@ func (t *P2PTunnel) start() error {
|
||||
func (t *P2PTunnel) handshake() error {
|
||||
if t.config.peerConeNatPort > 0 { // only peer is cone should prepare t.ra
|
||||
var err error
|
||||
t.ra, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", t.config.peerIP, t.config.peerConeNatPort))
|
||||
t.remoteHoleAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", t.config.peerIP, t.config.peerConeNatPort))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -198,14 +197,14 @@ func (t *P2PTunnel) handshake() error {
|
||||
if compareVersion(t.config.peerVersion, SyncServerTimeVersion) < 0 {
|
||||
gLog.Printf(LvDEBUG, "peer version %s less than %s", t.config.peerVersion, SyncServerTimeVersion)
|
||||
} else {
|
||||
ts := time.Duration(int64(t.punchTs) + t.pn.dt + t.pn.ddtma*int64(time.Since(t.pn.hbTime)+PunchTsDelay)/int64(NetworkHeartbeatTime) - time.Now().UnixNano())
|
||||
ts := time.Duration(int64(t.punchTs) + GNetwork.dt + GNetwork.ddtma*int64(time.Since(GNetwork.hbTime)+PunchTsDelay)/int64(NetworkHeartbeatTime) - time.Now().UnixNano())
|
||||
if ts > PunchTsDelay || ts < 0 {
|
||||
ts = PunchTsDelay
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "sleep %d ms", ts/time.Millisecond)
|
||||
time.Sleep(ts)
|
||||
}
|
||||
gLog.Println(LvDEBUG, "handshake to ", t.config.PeerNode)
|
||||
gLog.Println(LvDEBUG, "handshake to ", t.config.LogPeerNode())
|
||||
var err error
|
||||
if gConf.Network.natType == NATCone && t.config.peerNatType == NATCone {
|
||||
err = handshakeC2C(t)
|
||||
@@ -223,7 +222,7 @@ func (t *P2PTunnel) handshake() error {
|
||||
gLog.Println(LvERROR, "punch handshake error:", err)
|
||||
return err
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "handshake to %s ok", t.config.PeerNode)
|
||||
gLog.Printf(LvDEBUG, "handshake to %s ok", t.config.LogPeerNode())
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -258,8 +257,8 @@ func (t *P2PTunnel) connectUnderlay() (err error) {
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) connectUnderlayUDP() (c underlay, err error) {
|
||||
gLog.Printf(LvDEBUG, "connectUnderlayUDP %s start ", t.config.PeerNode)
|
||||
defer gLog.Printf(LvDEBUG, "connectUnderlayUDP %s end ", t.config.PeerNode)
|
||||
gLog.Printf(LvDEBUG, "connectUnderlayUDP %s start ", t.config.LogPeerNode())
|
||||
defer gLog.Printf(LvDEBUG, "connectUnderlayUDP %s end ", t.config.LogPeerNode())
|
||||
var ul underlay
|
||||
underlayProtocol := t.config.UnderlayProtocol
|
||||
if underlayProtocol == "" {
|
||||
@@ -267,11 +266,11 @@ func (t *P2PTunnel) connectUnderlayUDP() (c underlay, err error) {
|
||||
}
|
||||
if t.config.isUnderlayServer == 1 {
|
||||
time.Sleep(time.Millisecond * 10) // punching udp port will need some times in some env
|
||||
go t.pn.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
|
||||
go GNetwork.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
|
||||
if t.config.UnderlayProtocol == "kcp" {
|
||||
ul, err = listenKCP(t.la.String(), TunnelIdleTimeout)
|
||||
ul, err = listenKCP(t.localHoleAddr.String(), TunnelIdleTimeout)
|
||||
} else {
|
||||
ul, err = listenQuic(t.la.String(), TunnelIdleTimeout)
|
||||
ul, err = listenQuic(t.localHoleAddr.String(), TunnelIdleTimeout)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -293,24 +292,24 @@ func (t *P2PTunnel) connectUnderlayUDP() (c underlay, err error) {
|
||||
}
|
||||
|
||||
//else
|
||||
conn, errL := net.ListenUDP("udp", t.la)
|
||||
conn, errL := net.ListenUDP("udp", t.localHoleAddr)
|
||||
if errL != nil {
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
conn, errL = net.ListenUDP("udp", t.la)
|
||||
conn, errL = net.ListenUDP("udp", t.localHoleAddr)
|
||||
if errL != nil {
|
||||
return nil, fmt.Errorf("%s listen error:%s", underlayProtocol, errL)
|
||||
}
|
||||
}
|
||||
t.pn.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, ReadMsgTimeout)
|
||||
gLog.Printf(LvDEBUG, "%s dial to %s", underlayProtocol, t.ra.String())
|
||||
GNetwork.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, ReadMsgTimeout)
|
||||
gLog.Printf(LvDEBUG, "%s dial to %s", underlayProtocol, t.remoteHoleAddr.String())
|
||||
if t.config.UnderlayProtocol == "kcp" {
|
||||
ul, errL = dialKCP(conn, t.ra, TunnelIdleTimeout)
|
||||
ul, errL = dialKCP(conn, t.remoteHoleAddr, TunnelIdleTimeout)
|
||||
} else {
|
||||
ul, errL = dialQuic(conn, t.ra, TunnelIdleTimeout)
|
||||
ul, errL = dialQuic(conn, t.remoteHoleAddr, TunnelIdleTimeout)
|
||||
}
|
||||
|
||||
if errL != nil {
|
||||
return nil, fmt.Errorf("%s dial to %s error:%s", underlayProtocol, t.ra.String(), errL)
|
||||
return nil, fmt.Errorf("%s dial to %s error:%s", underlayProtocol, t.remoteHoleAddr.String(), errL)
|
||||
}
|
||||
handshakeBegin := time.Now()
|
||||
ul.WriteBytes(MsgP2P, MsgTunnelHandshake, []byte("OpenP2P,hello"))
|
||||
@@ -330,8 +329,8 @@ func (t *P2PTunnel) connectUnderlayUDP() (c underlay, err error) {
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) connectUnderlayTCP() (c underlay, err error) {
|
||||
gLog.Printf(LvDEBUG, "connectUnderlayTCP %s start ", t.config.PeerNode)
|
||||
defer gLog.Printf(LvDEBUG, "connectUnderlayTCP %s end ", t.config.PeerNode)
|
||||
gLog.Printf(LvDEBUG, "connectUnderlayTCP %s start ", t.config.LogPeerNode())
|
||||
defer gLog.Printf(LvDEBUG, "connectUnderlayTCP %s end ", t.config.LogPeerNode())
|
||||
var ul *underlayTCP
|
||||
peerIP := t.config.peerIP
|
||||
if t.config.linkMode == LinkModeIntranet {
|
||||
@@ -353,12 +352,12 @@ func (t *P2PTunnel) connectUnderlayTCP() (c underlay, err error) {
|
||||
|
||||
// client side
|
||||
if t.config.linkMode == LinkModeTCP4 {
|
||||
t.pn.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, ReadMsgTimeout)
|
||||
GNetwork.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, ReadMsgTimeout)
|
||||
} else { //tcp punch should sleep for punch the same time
|
||||
if compareVersion(t.config.peerVersion, SyncServerTimeVersion) < 0 {
|
||||
gLog.Printf(LvDEBUG, "peer version %s less than %s", t.config.peerVersion, SyncServerTimeVersion)
|
||||
} else {
|
||||
ts := time.Duration(int64(t.punchTs) + t.pn.dt + t.pn.ddtma*int64(time.Since(t.pn.hbTime)+PunchTsDelay)/int64(NetworkHeartbeatTime) - time.Now().UnixNano())
|
||||
ts := time.Duration(int64(t.punchTs) + GNetwork.dt + GNetwork.ddtma*int64(time.Since(GNetwork.hbTime)+PunchTsDelay)/int64(NetworkHeartbeatTime) - time.Now().UnixNano())
|
||||
if ts > PunchTsDelay || ts < 0 {
|
||||
ts = PunchTsDelay
|
||||
}
|
||||
@@ -392,9 +391,9 @@ func (t *P2PTunnel) connectUnderlayTCP() (c underlay, err error) {
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) connectUnderlayTCPSymmetric() (c underlay, err error) {
|
||||
gLog.Printf(LvDEBUG, "connectUnderlayTCPSymmetric %s start ", t.config.PeerNode)
|
||||
defer gLog.Printf(LvDEBUG, "connectUnderlayTCPSymmetric %s end ", t.config.PeerNode)
|
||||
ts := time.Duration(int64(t.punchTs) + t.pn.dt + t.pn.ddtma*int64(time.Since(t.pn.hbTime)+PunchTsDelay)/int64(NetworkHeartbeatTime) - time.Now().UnixNano())
|
||||
gLog.Printf(LvDEBUG, "connectUnderlayTCPSymmetric %s start ", t.config.LogPeerNode())
|
||||
defer gLog.Printf(LvDEBUG, "connectUnderlayTCPSymmetric %s end ", t.config.LogPeerNode())
|
||||
ts := time.Duration(int64(t.punchTs) + GNetwork.dt + GNetwork.ddtma*int64(time.Since(GNetwork.hbTime)+PunchTsDelay)/int64(NetworkHeartbeatTime) - time.Now().UnixNano())
|
||||
if ts > PunchTsDelay || ts < 0 {
|
||||
ts = PunchTsDelay
|
||||
}
|
||||
@@ -426,7 +425,7 @@ func (t *P2PTunnel) connectUnderlayTCPSymmetric() (c underlay, err error) {
|
||||
}
|
||||
_, buff, err := ul.ReadBuffer()
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "utcp.ReadBuffer error:", err)
|
||||
gLog.Println(LvDEBUG, "c2s ul.ReadBuffer error:", err)
|
||||
return
|
||||
}
|
||||
req := P2PHandshakeReq{}
|
||||
@@ -455,7 +454,7 @@ func (t *P2PTunnel) connectUnderlayTCPSymmetric() (c underlay, err error) {
|
||||
|
||||
_, buff, err := ul.ReadBuffer()
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "utcp.ReadBuffer error:", err)
|
||||
gLog.Println(LvDEBUG, "s2c ul.ReadBuffer error:", err)
|
||||
return
|
||||
}
|
||||
req := P2PHandshakeReq{}
|
||||
@@ -486,11 +485,11 @@ func (t *P2PTunnel) connectUnderlayTCPSymmetric() (c underlay, err error) {
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) connectUnderlayTCP6() (c underlay, err error) {
|
||||
gLog.Printf(LvDEBUG, "connectUnderlayTCP6 %s start ", t.config.PeerNode)
|
||||
defer gLog.Printf(LvDEBUG, "connectUnderlayTCP6 %s end ", t.config.PeerNode)
|
||||
gLog.Printf(LvDEBUG, "connectUnderlayTCP6 %s start ", t.config.LogPeerNode())
|
||||
defer gLog.Printf(LvDEBUG, "connectUnderlayTCP6 %s end ", t.config.LogPeerNode())
|
||||
var ul *underlayTCP6
|
||||
if t.config.isUnderlayServer == 1 {
|
||||
t.pn.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
|
||||
GNetwork.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
|
||||
ul, err = listenTCP6(t.coneNatPort, UnderlayConnectTimeout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listen TCP6 error:%s", err)
|
||||
@@ -509,17 +508,17 @@ func (t *P2PTunnel) connectUnderlayTCP6() (c underlay, err error) {
|
||||
}
|
||||
|
||||
//else
|
||||
t.pn.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, ReadMsgTimeout)
|
||||
GNetwork.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, ReadMsgTimeout)
|
||||
gLog.Println(LvDEBUG, "TCP6 dial to ", t.config.peerIPv6)
|
||||
ul, err = dialTCP6(t.config.peerIPv6, t.config.peerConeNatPort)
|
||||
if err != nil {
|
||||
if err != nil || ul == nil {
|
||||
return nil, fmt.Errorf("TCP6 dial to %s:%d error:%s", t.config.peerIPv6, t.config.peerConeNatPort, err)
|
||||
}
|
||||
handshakeBegin := time.Now()
|
||||
ul.WriteBytes(MsgP2P, MsgTunnelHandshake, []byte("OpenP2P,hello"))
|
||||
_, buff, err := ul.ReadBuffer()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read MsgTunnelHandshake error:%s", err)
|
||||
_, buff, errR := ul.ReadBuffer()
|
||||
if errR != nil {
|
||||
return nil, fmt.Errorf("read MsgTunnelHandshake error:%s", errR)
|
||||
}
|
||||
if buff != nil {
|
||||
gLog.Println(LvDEBUG, string(buff))
|
||||
@@ -596,8 +595,8 @@ func (t *P2PTunnel) readLoop() {
|
||||
}
|
||||
tunnelID := binary.LittleEndian.Uint64(body[:8])
|
||||
gLog.Printf(LvDev, "relay data to %d, len=%d", tunnelID, head.DataLen-RelayHeaderSize)
|
||||
if err := t.pn.relay(tunnelID, body[RelayHeaderSize:]); err != nil {
|
||||
gLog.Printf(LvERROR, "%s:%d relay to %d len=%d error:%s", t.config.PeerNode, t.id, tunnelID, len(body), ErrRelayTunnelNotFound)
|
||||
if err := GNetwork.relay(tunnelID, body[RelayHeaderSize:]); err != nil {
|
||||
gLog.Printf(LvERROR, "%s:%d relay to %d len=%d error:%s", t.config.LogPeerNode(), t.id, tunnelID, len(body), ErrRelayTunnelNotFound)
|
||||
}
|
||||
case MsgRelayHeartbeat:
|
||||
req := RelayHeartbeat{}
|
||||
@@ -608,7 +607,7 @@ func (t *P2PTunnel) readLoop() {
|
||||
// TODO: debug relay heartbeat
|
||||
gLog.Printf(LvDEBUG, "read MsgRelayHeartbeat from rtid:%d,appid:%d", req.RelayTunnelID, req.AppID)
|
||||
// update app hbtime
|
||||
t.pn.updateAppHeartbeat(req.AppID)
|
||||
GNetwork.updateAppHeartbeat(req.AppID)
|
||||
req.From = gConf.Network.Node
|
||||
t.WriteMessage(req.RelayTunnelID, MsgP2P, MsgRelayHeartbeatAck, &req)
|
||||
case MsgRelayHeartbeatAck:
|
||||
@@ -620,7 +619,7 @@ func (t *P2PTunnel) readLoop() {
|
||||
}
|
||||
// TODO: debug relay heartbeat
|
||||
gLog.Printf(LvDEBUG, "read MsgRelayHeartbeatAck to appid:%d", req.AppID)
|
||||
t.pn.updateAppHeartbeat(req.AppID)
|
||||
GNetwork.updateAppHeartbeat(req.AppID)
|
||||
case MsgOverlayConnectReq:
|
||||
req := OverlayConnectReq{}
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
@@ -691,8 +690,8 @@ func (t *P2PTunnel) writeLoop() {
|
||||
t.hbMtx.Unlock()
|
||||
tc := time.NewTicker(TunnelHeartbeatTime)
|
||||
defer tc.Stop()
|
||||
gLog.Printf(LvDEBUG, "%s:%d tunnel writeLoop start", t.config.PeerNode, t.id)
|
||||
defer gLog.Printf(LvDEBUG, "%s:%d tunnel writeLoop end", t.config.PeerNode, t.id)
|
||||
gLog.Printf(LvDEBUG, "%s:%d tunnel writeLoop start", t.config.LogPeerNode(), t.id)
|
||||
defer gLog.Printf(LvDEBUG, "%s:%d tunnel writeLoop end", t.config.LogPeerNode(), t.id)
|
||||
for t.isRuning() {
|
||||
select {
|
||||
case buff := <-t.writeDataSmall:
|
||||
@@ -733,7 +732,7 @@ func (t *P2PTunnel) listen() error {
|
||||
FromIP: gConf.Network.publicIP,
|
||||
ConeNatPort: t.coneNatPort,
|
||||
ID: t.id,
|
||||
PunchTs: uint64(time.Now().UnixNano() + int64(PunchTsDelay) - t.pn.dt),
|
||||
PunchTs: uint64(time.Now().UnixNano() + int64(PunchTsDelay) - GNetwork.dt),
|
||||
Version: OpenP2PVersion,
|
||||
}
|
||||
t.punchTs = rsp.PunchTs
|
||||
@@ -742,7 +741,7 @@ func (t *P2PTunnel) listen() error {
|
||||
rsp.IPv6 = gConf.IPv6()
|
||||
}
|
||||
|
||||
t.pn.push(t.config.PeerNode, MsgPushConnectRsp, rsp)
|
||||
GNetwork.push(t.config.PeerNode, MsgPushConnectRsp, rsp)
|
||||
gLog.Printf(LvDEBUG, "p2ptunnel wait for connecting")
|
||||
t.tunnelServer = true
|
||||
return t.start()
|
||||
@@ -760,9 +759,9 @@ func (t *P2PTunnel) closeOverlayConns(appID uint64) {
|
||||
|
||||
func (t *P2PTunnel) handleNodeData(head *openP2PHeader, body []byte, isRelay bool) {
|
||||
gLog.Printf(LvDev, "%d tunnel read node data bodylen=%d, relay=%t", t.id, head.DataLen, isRelay)
|
||||
ch := t.pn.nodeData
|
||||
ch := GNetwork.nodeData
|
||||
// if body[9] == 1 { // TODO: deal relay
|
||||
// ch = t.pn.nodeDataSmall
|
||||
// ch = GNetwork.nodeDataSmall
|
||||
// gLog.Printf(LvDEBUG, "read icmp %d", time.Now().Unix())
|
||||
// }
|
||||
if isRelay {
|
||||
@@ -781,13 +780,13 @@ func (t *P2PTunnel) asyncWriteNodeData(mainType, subType uint16, data []byte) {
|
||||
case t.writeDataSmall <- writeBytes:
|
||||
// gLog.Printf(LvWARN, "%s:%d t.writeDataSmall write %d", t.config.PeerNode, t.id, len(t.writeDataSmall))
|
||||
default:
|
||||
gLog.Printf(LvWARN, "%s:%d t.writeDataSmall is full, drop it", t.config.PeerNode, t.id)
|
||||
gLog.Printf(LvWARN, "%s:%d t.writeDataSmall is full, drop it", t.config.LogPeerNode(), t.id)
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case t.writeData <- writeBytes:
|
||||
default:
|
||||
gLog.Printf(LvWARN, "%s:%d t.writeData is full, drop it", t.config.PeerNode, t.id)
|
||||
gLog.Printf(LvWARN, "%s:%d t.writeData is full, drop it", t.config.LogPeerNode(), t.id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package openp2p
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
// 定义ICMP回显请求和应答的结构
|
||||
type ICMPMessage struct {
|
||||
Type uint8
|
||||
Code uint8
|
||||
Checksum uint16
|
||||
Ident uint16
|
||||
Seq uint16
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// Ping sends an ICMP Echo request to the specified host and returns the response time.
|
||||
func Ping(host string) (time.Duration, error) {
|
||||
// Resolve the IP address of the host
|
||||
ipAddr, err := net.ResolveIPAddr("ip4", host)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to resolve host: %v", err)
|
||||
}
|
||||
|
||||
// Create an ICMP listener
|
||||
conn, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to create ICMP connection: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// Create an ICMP Echo request message
|
||||
message := icmp.Message{
|
||||
Type: ipv4.ICMPTypeEcho,
|
||||
Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: os.Getpid() & 0xffff,
|
||||
Seq: 1,
|
||||
Data: []byte("HELLO-R-U-THERE"),
|
||||
},
|
||||
}
|
||||
|
||||
// Marshal the message into binary form
|
||||
messageBytes, err := message.Marshal(nil)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to marshal ICMP message: %v", err)
|
||||
}
|
||||
|
||||
// Send the ICMP Echo request
|
||||
start := time.Now()
|
||||
if _, err := conn.WriteTo(messageBytes, ipAddr); err != nil {
|
||||
return 0, fmt.Errorf("failed to send ICMP request: %v", err)
|
||||
}
|
||||
|
||||
// Set a deadline for the response
|
||||
err = conn.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to set read deadline: %v", err)
|
||||
}
|
||||
|
||||
// Read the ICMP response
|
||||
response := make([]byte, 1500)
|
||||
n, _, err := conn.ReadFrom(response)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to read ICMP response: %v", err)
|
||||
}
|
||||
|
||||
// Parse the ICMP response message
|
||||
parsedMessage, err := icmp.ParseMessage(ipv4.ICMPTypeEchoReply.Protocol(), response[:n])
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to parse ICMP response: %v", err)
|
||||
}
|
||||
|
||||
// Check if the response is an Echo reply
|
||||
if parsedMessage.Type == ipv4.ICMPTypeEchoReply {
|
||||
duration := time.Since(start)
|
||||
return duration, nil
|
||||
} else {
|
||||
return 0, fmt.Errorf("unexpected ICMP message: %+v", parsedMessage)
|
||||
}
|
||||
}
|
||||
+16
-3
@@ -10,7 +10,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const OpenP2PVersion = "3.18.4"
|
||||
const OpenP2PVersion = "3.21.12"
|
||||
const ProductName string = "openp2p"
|
||||
const LeastSupportVersion = "3.0.0"
|
||||
const SyncServerTimeVersion = "3.9.0"
|
||||
@@ -108,6 +108,7 @@ const (
|
||||
MsgPushReportGoroutine = 16
|
||||
MsgPushReportMemApps = 17
|
||||
MsgPushServerSideSaveMemApp = 18
|
||||
MsgPushCheckRemoteService = 19
|
||||
)
|
||||
|
||||
// MsgP2P sub type message
|
||||
@@ -143,6 +144,7 @@ const (
|
||||
MsgReportApps
|
||||
MsgReportLog
|
||||
MsgReportMemApps
|
||||
MsgReportResponse
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -165,7 +167,7 @@ const (
|
||||
MaxRetry = 10
|
||||
Cone2ConeTCPPunchMaxRetry = 1
|
||||
Cone2ConeUDPPunchMaxRetry = 1
|
||||
PublicIPEchoTimeout = time.Second * 1
|
||||
PublicIPEchoTimeout = time.Second * 3
|
||||
NatTestTimeout = time.Second * 5
|
||||
UDPReadTimeout = time.Second * 5
|
||||
ClientAPITimeout = time.Second * 10
|
||||
@@ -215,6 +217,12 @@ const (
|
||||
MsgSDWANInfoRsp
|
||||
)
|
||||
|
||||
// MsgNATDetect
|
||||
const (
|
||||
MsgNAT = iota
|
||||
MsgPublicIP
|
||||
)
|
||||
|
||||
func newMessage(mainType uint16, subType uint16, packet interface{}) ([]byte, error) {
|
||||
data, err := json.Marshal(packet)
|
||||
if err != nil {
|
||||
@@ -493,7 +501,7 @@ type SDWANInfo struct {
|
||||
ForceRelay int32 `json:"forceRelay,omitempty"`
|
||||
PunchPriority int32 `json:"punchPriority,omitempty"`
|
||||
Enable int32 `json:"enable,omitempty"`
|
||||
Nodes []SDWANNode
|
||||
Nodes []*SDWANNode
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -510,6 +518,11 @@ type ServerSideSaveMemApp struct {
|
||||
AppID uint64 `json:"appID,omitempty"`
|
||||
}
|
||||
|
||||
type CheckRemoteService struct {
|
||||
Host string `json:"host,omitempty"`
|
||||
Port uint32 `json:"port,omitempty"`
|
||||
}
|
||||
|
||||
const rootCA = `-----BEGIN CERTIFICATE-----
|
||||
MIIDhTCCAm0CFHm0cd8dnGCbUW/OcS56jf0gvRk7MA0GCSqGSIb3DQEBCwUAMH4x
|
||||
CzAJBgNVBAYTAkNOMQswCQYDVQQIDAJHRDETMBEGA1UECgwKb3BlbnAycC5jbjET
|
||||
|
||||
+40
-9
@@ -52,18 +52,36 @@ type p2pSDWAN struct {
|
||||
internalRoute *IPTree
|
||||
}
|
||||
|
||||
func (s *p2pSDWAN) reset() {
|
||||
gLog.Println(LvINFO, "reset sdwan when network disconnected")
|
||||
// clear sysroute
|
||||
delRoutesByGateway(s.gateway.String())
|
||||
// clear internel route
|
||||
s.internalRoute = NewIPTree("")
|
||||
// clear p2papp
|
||||
for _, node := range gConf.getAddNodes() {
|
||||
gConf.delete(AppConfig{SrcPort: 0, PeerNode: node.Name})
|
||||
}
|
||||
|
||||
gConf.resetSDWAN()
|
||||
}
|
||||
func (s *p2pSDWAN) init(name string) error {
|
||||
if gConf.getSDWAN().Gateway == "" {
|
||||
gLog.Println(LvDEBUG, "not in sdwan clear all ")
|
||||
gLog.Println(LvDEBUG, "sdwan init: not in sdwan clear all ")
|
||||
}
|
||||
if s.internalRoute == nil {
|
||||
s.internalRoute = NewIPTree("")
|
||||
}
|
||||
|
||||
s.nodeName = name
|
||||
s.gateway, s.subnet, _ = net.ParseCIDR(gConf.getSDWAN().Gateway)
|
||||
if gw, sn, err := net.ParseCIDR(gConf.getSDWAN().Gateway); err == nil { // preserve old gateway
|
||||
s.gateway = gw
|
||||
s.subnet = sn
|
||||
}
|
||||
|
||||
for _, node := range gConf.getDelNodes() {
|
||||
gLog.Println(LvDEBUG, "deal deleted node: ", node.Name)
|
||||
gLog.Println(LvDEBUG, "sdwan init: deal deleted node: ", node.Name)
|
||||
gLog.Printf(LvDEBUG, "sdwan init: delRoute: %s, %s ", node.IP, s.gateway.String())
|
||||
delRoute(node.IP, s.gateway.String())
|
||||
s.internalRoute.Del(node.IP, node.IP)
|
||||
ipNum, _ := inetAtoN(node.IP)
|
||||
@@ -88,24 +106,26 @@ func (s *p2pSDWAN) init(name string) error {
|
||||
}
|
||||
s.internalRoute.Del(minIP.String(), maxIP.String())
|
||||
delRoute(ipnet.String(), s.gateway.String())
|
||||
gLog.Printf(LvDEBUG, "sdwan init: resource delRoute: %s, %s ", ipnet.String(), s.gateway.String())
|
||||
}
|
||||
}
|
||||
for _, node := range gConf.getAddNodes() {
|
||||
gLog.Println(LvDEBUG, "deal add node: ", node.Name)
|
||||
gLog.Println(LvDEBUG, "sdwan init: deal add node: ", node.Name)
|
||||
ipNet := &net.IPNet{
|
||||
IP: net.ParseIP(node.IP),
|
||||
Mask: s.subnet.Mask,
|
||||
}
|
||||
if node.Name == s.nodeName {
|
||||
s.virtualIP = ipNet
|
||||
gLog.Println(LvINFO, "start tun ", ipNet.String())
|
||||
gLog.Println(LvINFO, "sdwan init: start tun ", ipNet.String())
|
||||
err := s.StartTun()
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "start tun error:", err)
|
||||
gLog.Println(LvERROR, "sdwan init: start tun error:", err)
|
||||
return err
|
||||
}
|
||||
gLog.Println(LvINFO, "start tun ok")
|
||||
gLog.Println(LvINFO, "sdwan init: start tun ok")
|
||||
allowTunForward()
|
||||
gLog.Printf(LvDEBUG, "sdwan init: addRoute %s %s %s", s.subnet.String(), s.gateway.String(), s.tun.tunName)
|
||||
addRoute(s.subnet.String(), s.gateway.String(), s.tun.tunName)
|
||||
// addRoute("255.255.255.255/32", s.gateway.String(), s.tun.tunName) // for broadcast
|
||||
// addRoute("224.0.0.0/4", s.gateway.String(), s.tun.tunName) // for multicast
|
||||
@@ -124,18 +144,28 @@ func (s *p2pSDWAN) init(name string) error {
|
||||
continue
|
||||
}
|
||||
if len(node.Resource) > 0 {
|
||||
gLog.Printf(LvINFO, "deal add node: %s resource: %s", node.Name, node.Resource)
|
||||
gLog.Printf(LvINFO, "sdwan init: deal add node: %s resource: %s", node.Name, node.Resource)
|
||||
arr := strings.Split(node.Resource, ",")
|
||||
for _, r := range arr {
|
||||
// add internal route
|
||||
_, ipnet, err := net.ParseCIDR(r)
|
||||
if err != nil {
|
||||
fmt.Println("Error parsing CIDR:", err)
|
||||
fmt.Println("sdwan init: Error parsing CIDR:", err)
|
||||
continue
|
||||
}
|
||||
if ipnet.Contains(net.ParseIP(gConf.Network.localIP)) { // local ip and resource in the same lan
|
||||
gLog.Printf(LvDEBUG, "sdwan init: local ip %s in this resource %s, ignore", gConf.Network.localIP, ipnet.IP.String())
|
||||
continue
|
||||
}
|
||||
// local net could access this single ip
|
||||
if ipnet.Mask[0] == 255 && ipnet.Mask[1] == 255 && ipnet.Mask[2] == 255 && ipnet.Mask[3] == 255 {
|
||||
gLog.Printf(LvDEBUG, "sdwan init: ping %s start", ipnet.IP.String())
|
||||
if _, err := Ping(ipnet.IP.String()); err == nil {
|
||||
gLog.Printf(LvDEBUG, "sdwan init: ping %s ok, ignore this resource", ipnet.IP.String())
|
||||
continue
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "sdwan init: ping %s failed", ipnet.IP.String())
|
||||
}
|
||||
minIP := ipnet.IP
|
||||
maxIP := make(net.IP, len(minIP))
|
||||
copy(maxIP, minIP)
|
||||
@@ -144,6 +174,7 @@ func (s *p2pSDWAN) init(name string) error {
|
||||
}
|
||||
s.internalRoute.Add(minIP.String(), maxIP.String(), &sdwanNode{name: node.Name, id: NodeNameToID(node.Name)})
|
||||
// add sys route
|
||||
gLog.Printf(LvDEBUG, "sdwan init: addRoute %s %s %s", ipnet.String(), s.gateway.String(), s.tun.tunName)
|
||||
addRoute(ipnet.String(), s.gateway.String(), s.tun.tunName)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ func DefaultReadBuffer(ul underlay) (*openP2PHeader, []byte, error) {
|
||||
return nil, nil, err
|
||||
}
|
||||
head, err := decodeHeader(headBuf)
|
||||
if err != nil {
|
||||
if err != nil || head.MainType > 16 {
|
||||
return nil, nil, err
|
||||
}
|
||||
dataBuf := make([]byte, head.DataLen)
|
||||
|
||||
@@ -51,7 +51,7 @@ func listenTCP(host string, port int, localPort int, mode string, t *P2PTunnel)
|
||||
if compareVersion(t.config.peerVersion, SyncServerTimeVersion) < 0 {
|
||||
gLog.Printf(LvDEBUG, "peer version %s less than %s", t.config.peerVersion, SyncServerTimeVersion)
|
||||
} else {
|
||||
ts := time.Duration(int64(t.punchTs) + t.pn.dt - time.Now().UnixNano())
|
||||
ts := time.Duration(int64(t.punchTs) + GNetwork.dt - time.Now().UnixNano())
|
||||
gLog.Printf(LvDEBUG, "sleep %d ms", ts/time.Millisecond)
|
||||
time.Sleep(ts)
|
||||
}
|
||||
@@ -72,7 +72,7 @@ func listenTCP(host string, port int, localPort int, mode string, t *P2PTunnel)
|
||||
utcp.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, buff)
|
||||
return utcp, nil
|
||||
}
|
||||
t.pn.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
|
||||
GNetwork.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
|
||||
tid := t.id
|
||||
if compareVersion(t.config.peerVersion, PublicIPVersion) < 0 { // old version
|
||||
ipBytes := net.ParseIP(t.config.peerIP).To4()
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ func (vl *v4Listener) handleConnection(c net.Conn) {
|
||||
utcp.SetReadDeadline(time.Now().Add(UnderlayTCPConnectTimeout))
|
||||
_, buff, err := utcp.ReadBuffer()
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "utcp.ReadBuffer error:", err)
|
||||
gLog.Println(LvERROR, "utcp.ReadBuffer error:", err)
|
||||
}
|
||||
utcp.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, buff)
|
||||
var tid uint64
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 23 KiB |
@@ -8,17 +8,16 @@ require (
|
||||
github.com/openp2p-cn/go-reuseport v0.3.2
|
||||
github.com/openp2p-cn/service v1.0.0
|
||||
github.com/openp2p-cn/totp v0.0.0-20230421034602-0f3320ffb25e
|
||||
github.com/openp2p-cn/wireguard-go v0.0.20240223
|
||||
github.com/quic-go/quic-go v0.34.0
|
||||
github.com/vishvananda/netlink v1.1.0
|
||||
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54
|
||||
github.com/xtaci/kcp-go/v5 v5.5.17
|
||||
golang.org/x/sys v0.21.0
|
||||
golang.org/x/sys v0.26.0
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/mock v1.7.0-rc.1 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
github.com/kardianos/service v1.2.2 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
@@ -30,13 +29,13 @@ require (
|
||||
github.com/templexxx/cpu v0.1.0 // indirect
|
||||
github.com/templexxx/xorsimd v0.4.2 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect
|
||||
golang.org/x/crypto v0.24.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
|
||||
golang.org/x/mod v0.18.0 // indirect
|
||||
golang.org/x/net v0.26.0 // indirect
|
||||
golang.org/x/tools v0.22.0 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
|
||||
golang.org/x/crypto v0.28.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/tools v0.26.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20241128011400-745828301c93 // indirect
|
||||
)
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package main
|
||||
|
||||
// On Windows env
|
||||
// cd lib
|
||||
// go build -o openp2p.dll -buildmode=c-shared openp2p.go
|
||||
// caller example see example/dll
|
||||
import (
|
||||
op "openp2p/core"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user