diff --git a/README-ZH.md b/README-ZH.md index 1d19806..2a42894 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -40,11 +40,6 @@ P2P直连可以让你的设备跑满带宽。不论你的设备在任何网络 ![image](/doc/images/install.png) - Windows默认会阻止没有花钱买它家证书签名过的程序,选择“仍要运行”即可。 - - ![image](/doc/images/win10warn.png) - - ![image](/doc/images/stillrun.png) ### 3.新建P2P应用 ![image](/doc/images/devices.png) diff --git a/README.md b/README.md index c5e6125..7ac4843 100644 --- a/README.md +++ b/README.md @@ -43,11 +43,6 @@ Download on local and remote computers and double-click to run, one-click instal ![image](/doc/images/install_en.png) - By default, Windows will block programs that have not been signed by the Microsoft's certificate, and you can select "Run anyway". - - ![image](/doc/images/win10warn_en.png) - - ![image](/doc/images/stillrun_en.png) ### 3.New P2PApp ![image](/doc/images/devices_en.png) diff --git a/USAGE-ZH.md b/USAGE-ZH.md index 00e925f..8d67b7a 100644 --- a/USAGE-ZH.md +++ b/USAGE-ZH.md @@ -17,6 +17,12 @@ >* -sharebandwidth: 作为共享节点时提供带宽,默认10mbps. 如果是光纤大带宽,设置越大效果越好. 0表示不共享,该节点只在私有的P2P网络使用。不加入共享的P2P网络,这样也意味着无法使用别人的共享节点 >* -loglevel: 需要查看更多调试日志,设置0;默认是1 +### 在docker容器里运行openp2p +我们暂时还没提供官方docker镜像,你可以在随便一个容器里运行 +``` +nohup ./openp2p -d -node OFFICEPC1 -token TOKEN & +#这里由于一般的镜像都精简过,install系统服务会失败,所以使用直接daemon模式后台运行 +``` ## 连接 ``` ./openp2p -d -node HOMEPC123 -token TOKEN -appname OfficeWindowsRemote -peernode OFFICEPC1 -dstip 127.0.0.1 -dstport 3389 -srcport 23389 diff --git a/USAGE.md b/USAGE.md index 1da3009..f73b0cb 100644 --- a/USAGE.md +++ b/USAGE.md @@ -19,6 +19,13 @@ Or >* -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 >* -loglevel: Need to view more debug logs, set 0; the default is 1 +### Run in Docker container +We don't provide official docker image yet, you can run it in any container +``` +nohup ./openp2p -d -node OFFICEPC1 -token TOKEN & +# Since many docker images have been simplified, the install system service will fail, so the daemon mode is used to run in the background +``` + ## Connect ``` ./openp2p -d -node HOMEPC123 -token TOKEN -appname OfficeWindowsRemote -peernode OFFICEPC1 -dstip 127.0.0.1 -dstport 3389 -srcport 23389 diff --git a/config.go b/config.go index 0d168ab..3465554 100644 --- a/config.go +++ b/config.go @@ -30,13 +30,14 @@ type AppConfig struct { peerConeNatPort int retryNum int retryTime time.Time + nextRetryTime time.Time shareBandwidth int } // TODO: add loglevel, maxlogfilesize type Config struct { Network NetworkConfig `json:"network"` - Apps []AppConfig `json:"apps"` + Apps []*AppConfig `json:"apps"` LogLevel int mtx sync.Mutex @@ -48,27 +49,29 @@ func (c *Config) switchApp(app AppConfig, enabled int) { for i := 0; i < len(c.Apps); i++ { if c.Apps[i].Protocol == app.Protocol && c.Apps[i].SrcPort == app.SrcPort { c.Apps[i].Enabled = enabled + c.Apps[i].retryNum = 0 + c.Apps[i].nextRetryTime = time.Now() return } } } -func (c *Config) add(app AppConfig, force bool) { +func (c *Config) add(app AppConfig, override bool) { c.mtx.Lock() defer c.mtx.Unlock() if app.SrcPort == 0 || app.DstPort == 0 { gLog.Println(LevelERROR, "invalid app ", app) return } - for i := 0; i < len(c.Apps); i++ { - if c.Apps[i].Protocol == app.Protocol && c.Apps[i].SrcPort == app.SrcPort { - if force { - c.Apps[i] = app + if override { + for i := 0; i < len(c.Apps); i++ { + if c.Apps[i].Protocol == app.Protocol && c.Apps[i].SrcPort == app.SrcPort { + c.Apps[i] = &app // override it + return } - return } } - c.Apps = append(c.Apps, app) + c.Apps = append(c.Apps, &app) } func (c *Config) delete(app AppConfig) { @@ -142,11 +145,8 @@ func parseParams() { srcPort := flag.Int("srcport", 0, "source port ") protocol := flag.String("protocol", "tcp", "tcp or udp") appName := flag.String("appname", "", "app name") - flag.Bool("noshare", false, "deprecated. uses -sharebandwidth 0") // Deprecated, rm later - shareBandwidth := flag.Int("sharebandwidth", 10, "N mbps share bandwidth limit, private node no limit") - flag.Bool("f", false, "deprecated. config file") // Deprecated, rm later + shareBandwidth := flag.Int("sharebandwidth", 10, "N mbps share bandwidth limit, private network no limit") daemonMode := flag.Bool("d", false, "daemonMode") - flag.Bool("bydaemon", false, "start by daemon") // Deprecated, rm later logLevel := flag.Int("loglevel", 1, "0:debug 1:info 2:warn 3:error") flag.Parse() diff --git a/daemon.go b/daemon.go index 34571f0..5ce94a9 100644 --- a/daemon.go +++ b/daemon.go @@ -110,6 +110,8 @@ func (d *daemon) Control(ctrlComm string, exeAbsPath string, args []string) erro // listen and build p2papp: // ./openp2p install -node hhd1207-222 -token YOUR-TOKEN -sharebandwidth 0 -peernode hhdhome-n1 -dstip 127.0.0.1 -dstport 50022 -protocol tcp -srcport 22 func install() { + gLog.Println(LevelINFO, "openp2p start. version: ", OpenP2PVersion) + gLog.Println(LevelINFO, "Contact: QQ Group: 16947733, Email: openp2p.cn@gmail.com") gLog.Println(LevelINFO, "install start") defer gLog.Println(LevelINFO, "install end") // auto uninstall @@ -127,8 +129,7 @@ func install() { srcPort := installFlag.Int("srcport", 0, "source port ") protocol := installFlag.String("protocol", "tcp", "tcp or udp") appName := flag.String("appname", "", "app name") - installFlag.Bool("noshare", false, "deprecated. uses -sharebandwidth 0") - shareBandwidth := installFlag.Int("sharebandwidth", 10, "N mbps share bandwidth limit, private node no limit") + shareBandwidth := installFlag.Int("sharebandwidth", 10, "N mbps share bandwidth limit, private network no limit") logLevel := installFlag.Int("loglevel", 1, "0:debug 1:info 2:warn 3:error") installFlag.Parse(os.Args[2:]) if *node != "" && len(*node) < 8 { @@ -195,7 +196,6 @@ func install() { dst.Close() // install system service - // args := []string{""} gLog.Println(LevelINFO, "targetPath:", targetPath) err = d.Control("install", targetPath, []string{"-d"}) if err == nil { diff --git a/doc/images/stillrun.png b/doc/images/stillrun.png index 4a9a958..ee9eb76 100644 Binary files a/doc/images/stillrun.png and b/doc/images/stillrun.png differ diff --git a/doc/images/stillrun_en.png b/doc/images/stillrun_en.png index f2e138e..7db54ca 100644 Binary files a/doc/images/stillrun_en.png and b/doc/images/stillrun_en.png differ diff --git a/doc/images/win10warn.png b/doc/images/win10warn.png index cf3ebc4..acc9f1d 100644 Binary files a/doc/images/win10warn.png and b/doc/images/win10warn.png differ diff --git a/doc/images/win10warn_en.png b/doc/images/win10warn_en.png index c5b1408..2666997 100644 Binary files a/doc/images/win10warn_en.png and b/doc/images/win10warn_en.png differ diff --git a/handlepush.go b/handlepush.go index d42c6ac..579c2df 100644 --- a/handlepush.go +++ b/handlepush.go @@ -115,18 +115,23 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error { defer gConf.mtx.Unlock() for _, config := range gConf.Apps { appActive := 0 + relayNode := "" + relayMode := "" i, ok := pn.apps.Load(fmt.Sprintf("%s%d", config.Protocol, config.SrcPort)) if ok { app := i.(*p2pApp) if app.isActive() { appActive = 1 } + relayNode = app.relayNode + relayMode = app.relayMode } appInfo := AppInfo{ - AppName: config.AppName, - Protocol: config.Protocol, - SrcPort: config.SrcPort, - // RelayNode: relayNode, + AppName: config.AppName, + Protocol: config.Protocol, + SrcPort: config.SrcPort, + RelayNode: relayNode, + RelayMode: relayMode, PeerNode: config.PeerNode, DstHost: config.DstHost, DstPort: config.DstPort, diff --git a/openp2p.go b/openp2p.go index 61ef2dd..6c4524e 100644 --- a/openp2p.go +++ b/openp2p.go @@ -13,7 +13,7 @@ func main() { binDir := filepath.Dir(os.Args[0]) os.Chdir(binDir) // for system service gLog = InitLogger(binDir, "openp2p", LevelDEBUG, 1024*1024, LogFileAndConsole) - gLog.Println(LevelINFO, "openp2p start. version: ", OpenP2PVersion) + // TODO: install sub command, deamon process if len(os.Args) > 1 { switch os.Args[1] { @@ -41,7 +41,8 @@ func main() { } else { installByFilename() } - + gLog.Println(LevelINFO, "openp2p start. version: ", OpenP2PVersion) + gLog.Println(LevelINFO, "Contact: QQ Group: 16947733, Email: openp2p.cn@gmail.com") parseParams() gLog.Println(LevelINFO, &gConf) setFirewall() diff --git a/p2papp.go b/p2papp.go index bceabbf..43f6862 100644 --- a/p2papp.go +++ b/p2papp.go @@ -16,6 +16,7 @@ type p2pApp struct { tunnel *P2PTunnel rtid uint64 relayNode string + relayMode string hbTime time.Time hbMtx sync.Mutex running bool @@ -45,7 +46,12 @@ func (app *p2pApp) updateHeartbeat() { func (app *p2pApp) listenTCP() error { var err error - app.listener, err = net.Listen("tcp4", fmt.Sprintf("0.0.0.0:%d", app.config.SrcPort)) + if app.config.Protocol == "udp" { + app.listener, err = net.Listen("udp4", fmt.Sprintf("0.0.0.0:%d", app.config.SrcPort)) + } else { + app.listener, err = net.Listen("tcp4", fmt.Sprintf("0.0.0.0:%d", app.config.SrcPort)) + } + if err != nil { gLog.Printf(LevelERROR, "listen error:%s", err) return err @@ -108,13 +114,10 @@ func (app *p2pApp) listen() error { go app.relayHeartbeatLoop() } for app.running { - if app.config.Protocol == "udp" { - app.listenTCP() - } else { - app.listenTCP() - } + + app.listenTCP() + time.Sleep(time.Second * 5) - // TODO: listen UDP } return nil } diff --git a/p2pnetwork.go b/p2pnetwork.go index 236ef4f..1602cd8 100644 --- a/p2pnetwork.go +++ b/p2pnetwork.go @@ -7,6 +7,7 @@ import ( "encoding/json" "errors" "fmt" + "math" "math/rand" "net" "net/url" @@ -96,6 +97,9 @@ func (pn *P2PNetwork) runAll() { gConf.mtx.Lock() defer gConf.mtx.Unlock() for _, config := range gConf.Apps { + if config.nextRetryTime.After(time.Now()) { + continue + } if config.Enabled == 0 { continue } @@ -116,19 +120,22 @@ func (pn *P2PNetwork) runAll() { continue } if appExist && !appActive { - gLog.Printf(LevelINFO, "detect app %s disconnect, reconnecting...", config.AppName) - pn.DeleteApp(config) - if config.retryTime.Add(time.Minute * 15).Before(time.Now()) { + pn.DeleteApp(*config) + } + if config.retryNum > 0 { + gLog.Printf(LevelINFO, "detect app %s disconnect, reconnecting the %d times...", config.AppName, config.retryNum) + if time.Now().Add(-time.Minute * 15).After(config.retryTime) { // normal lasts 15min config.retryNum = 0 } - config.retryNum++ - config.retryTime = time.Now() - if config.retryNum > MaxRetry { - gLog.Printf(LevelERROR, "app %s%d retry more than %d times, exit.", config.Protocol, config.SrcPort, MaxRetry) - continue - } } - go pn.AddApp(config) + config.retryNum++ + config.retryTime = time.Now() + increase := math.Pow(1.3, float64(config.retryNum)) + if increase > 900 { + increase = 900 + } + config.nextRetryTime = time.Now().Add(time.Second * time.Duration(increase)) // exponential increase retry time. 1.3^x + go pn.AddApp(*config) } } func (pn *P2PNetwork) autorunApp() { @@ -145,23 +152,23 @@ func (pn *P2PNetwork) autorunApp() { gLog.Println(LevelINFO, "autorunApp end") } -func (pn *P2PNetwork) addRelayTunnel(config AppConfig, appid uint64, appkey uint64) (*P2PTunnel, uint64, error) { +func (pn *P2PNetwork) addRelayTunnel(config AppConfig, appid uint64, appkey uint64) (*P2PTunnel, uint64, string, error) { gLog.Printf(LevelINFO, "addRelayTunnel to %s start", config.PeerNode) defer gLog.Printf(LevelINFO, "addRelayTunnel to %s end", config.PeerNode) pn.write(MsgRelay, MsgRelayNodeReq, &RelayNodeReq{config.PeerNode}) head, body := pn.read("", MsgRelay, MsgRelayNodeRsp, time.Second*10) if head == nil { - return nil, 0, errors.New("read MsgRelayNodeRsp error") + return nil, 0, "", errors.New("read MsgRelayNodeRsp error") } rsp := RelayNodeRsp{} err := json.Unmarshal(body, &rsp) if err != nil { gLog.Printf(LevelERROR, "wrong RelayNodeRsp:%s", err) - return nil, 0, errors.New("unmarshal MsgRelayNodeRsp error") + return nil, 0, "", errors.New("unmarshal MsgRelayNodeRsp error") } if rsp.RelayName == "" || rsp.RelayToken == 0 { gLog.Printf(LevelERROR, "MsgRelayNodeReq error") - return nil, 0, errors.New("MsgRelayNodeReq error") + return nil, 0, "", errors.New("MsgRelayNodeReq error") } gLog.Printf(LevelINFO, "got relay node:%s", rsp.RelayName) relayConfig := config @@ -170,7 +177,7 @@ func (pn *P2PNetwork) addRelayTunnel(config AppConfig, appid uint64, appkey uint t, err := pn.addDirectTunnel(relayConfig, 0) if err != nil { gLog.Println(LevelERROR, "direct connect error:", err) - return nil, 0, err + return nil, 0, "", err } // notify peer addRelayTunnel req := AddRelayTunnelReq{ @@ -187,15 +194,15 @@ func (pn *P2PNetwork) addRelayTunnel(config AppConfig, appid uint64, appkey uint head, body = pn.read(config.PeerNode, MsgPush, MsgPushAddRelayTunnelRsp, PeerAddRelayTimeount) // TODO: const value if head == nil { gLog.Printf(LevelERROR, "read MsgPushAddRelayTunnelRsp error") - return nil, 0, errors.New("read MsgPushAddRelayTunnelRsp error") + return nil, 0, "", errors.New("read MsgPushAddRelayTunnelRsp error") } rspID := TunnelMsg{} err = json.Unmarshal(body, &rspID) if err != nil { gLog.Printf(LevelERROR, "wrong RelayNodeRsp:%s", err) - return nil, 0, errors.New("unmarshal MsgRelayNodeRsp error") + return nil, 0, "", errors.New("unmarshal MsgRelayNodeRsp error") } - return t, rspID.ID, err + return t, rspID.ID, rsp.Mode, err } func (pn *P2PNetwork) AddApp(config AppConfig) error { @@ -215,24 +222,26 @@ func (pn *P2PNetwork) AddApp(config AppConfig) error { } appID := rand.Uint64() appKey := uint64(0) - t, err := pn.addDirectTunnel(config, 0) var rtid uint64 relayNode := "" + relayMode := "" peerNatType := NATUnknown peerIP := "" errMsg := "" - if err != nil && err == ErrorHandshake { - gLog.Println(LevelERROR, "direct connect failed, try to relay") - appKey = rand.Uint64() - t, rtid, err = pn.addRelayTunnel(config, appID, appKey) - if t != nil { - relayNode = t.config.PeerNode - } - } + t, err := pn.addDirectTunnel(config, 0) if t != nil { peerNatType = t.config.peerNatType peerIP = t.config.peerIP } + if err != nil && err == ErrorHandshake { + gLog.Println(LevelERROR, "direct connect failed, try to relay") + appKey = rand.Uint64() + t, rtid, relayMode, err = pn.addRelayTunnel(config, appID, appKey) + if t != nil { + relayNode = t.config.PeerNode + } + } + if err != nil { errMsg = err.Error() } @@ -261,6 +270,7 @@ func (pn *P2PNetwork) AddApp(config AppConfig) error { config: config, rtid: rtid, relayNode: relayNode, + relayMode: relayMode, hbTime: time.Now()} pn.apps.Store(fmt.Sprintf("%s%d", config.Protocol, config.SrcPort), &app) if err == nil { diff --git a/p2ptunnel.go b/p2ptunnel.go index b9e56b4..ec2dd15 100644 --- a/p2ptunnel.go +++ b/p2ptunnel.go @@ -333,32 +333,35 @@ func (t *P2PTunnel) readLoop() { overlayID := req.ID gLog.Printf(LevelDEBUG, "App:%d overlayID:%d connect %+v", req.AppID, overlayID, req) - if req.Protocol == "tcp" { - conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", req.DstIP, req.DstPort), time.Second*5) - if err != nil { - gLog.Println(LevelERROR, err) - continue - } - otcp := overlayTCP{ - tunnel: t, - conn: conn, - id: overlayID, - isClient: false, - rtid: req.RelayTunnelID, - appID: req.AppID, - appKey: GetKey(req.AppID), - } - // calc key bytes for encrypt - if otcp.appKey != 0 { - encryptKey := make([]byte, 16) - binary.LittleEndian.PutUint64(encryptKey, otcp.appKey) - binary.LittleEndian.PutUint64(encryptKey[8:], otcp.appKey) - otcp.appKeyBytes = encryptKey - } - - t.overlayConns.Store(otcp.id, &otcp) - go otcp.run() + var conn net.Conn + if req.Protocol == "udp" { + conn, err = net.DialTimeout("udp", fmt.Sprintf("%s:%d", req.DstIP, req.DstPort), time.Second*5) + } else { + conn, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", req.DstIP, req.DstPort), time.Second*5) } + if err != nil { + gLog.Println(LevelERROR, err) + continue + } + otcp := overlayTCP{ + tunnel: t, + conn: conn, + id: overlayID, + isClient: false, + rtid: req.RelayTunnelID, + appID: req.AppID, + appKey: GetKey(req.AppID), + } + // calc key bytes for encrypt + if otcp.appKey != 0 { + encryptKey := make([]byte, 16) + binary.LittleEndian.PutUint64(encryptKey, otcp.appKey) + binary.LittleEndian.PutUint64(encryptKey[8:], otcp.appKey) + otcp.appKeyBytes = encryptKey + } + + t.overlayConns.Store(otcp.id, &otcp) + go otcp.run() case MsgOverlayDisconnectReq: req := OverlayDisconnectReq{} err := json.Unmarshal(body, &req) diff --git a/protocol.go b/protocol.go index ddf2d5e..3180f82 100644 --- a/protocol.go +++ b/protocol.go @@ -10,7 +10,7 @@ import ( "time" ) -const OpenP2PVersion = "1.1.0" +const OpenP2PVersion = "1.2.0" const ProducnName string = "openp2p" type openP2PHeader struct { @@ -236,6 +236,7 @@ type RelayNodeReq struct { } type RelayNodeRsp struct { + Mode string `json:"mode,omitempty"` // private,public RelayName string `json:"relayName,omitempty"` RelayToken uint64 `json:"relayToken,omitempty"` } @@ -294,6 +295,7 @@ type AppInfo struct { PeerIP string `json:"peerIP,omitempty"` ShareBandwidth int `json:"shareBandWidth,omitempty"` RelayNode string `json:"relayNode,omitempty"` + RelayMode string `json:"relayMode,omitempty"` Version string `json:"version,omitempty"` RetryTime string `json:"retryTime,omitempty"` IsActive int `json:"isActive,omitempty"`