diff --git a/.gitignore b/.gitignore index caa7e5a..556b6bb 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,5 @@ wintun.dll .vscode/ app/.idea/ *_debug_bin* -cmd/openp2p \ No newline at end of file +cmd/openp2p +vendor/ \ No newline at end of file diff --git a/README-ZH.md b/README-ZH.md index 947603d..eecc438 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -96,7 +96,7 @@ Windows默认会阻止没有花钱买它家证书签名过的程序,选择“ 服务端有个调度模型,根据带宽、ping值、稳定性、服务时长,尽可能地使共享节点均匀地提供服务。连接共享节点使用TOTP密码,hmac-sha256算法校验,它是一次性密码,和我们平时使用的手机验证码或银行密码器一样的原理。 ## 编译 -go version go1.18.1+ +go version 1.20 only (支持win7) cd到代码根目录,执行 ``` make diff --git a/README.md b/README.md index 6ed7887..77a440d 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/core/common_test.go b/core/common_test.go index 16572b9..66ac093 100644 --- a/core/common_test.go +++ b/core/common_test.go @@ -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) + } +} diff --git a/core/config.go b/core/config.go index 54a0895..8f6a8c7 100644 --- a/core/config.go +++ b/core/config.go @@ -78,12 +78,13 @@ type Config struct { 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 { @@ -92,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 { @@ -122,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 { @@ -230,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].PeerNode == app.PeerNode) { // memapp if i == len(c.Apps)-1 { c.Apps = c.Apps[:i] } else { @@ -249,7 +248,6 @@ func (c *Config) delete(app AppConfig) { return } } - } func (c *Config) save() { @@ -280,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 @@ -463,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 } diff --git a/core/optun_darwin.go b/core/optun_darwin.go index b83b5e9..e6d4535 100644 --- a/core/optun_darwin.go +++ b/core/optun_darwin.go @@ -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 diff --git a/core/optun_linux.go b/core/optun_linux.go index 7670ace..bae0946 100644 --- a/core/optun_linux.go +++ b/core/optun_linux.go @@ -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 diff --git a/core/optun_windows.go b/core/optun_windows.go index c1a13ef..947b55f 100644 --- a/core/optun_windows.go +++ b/core/optun_windows.go @@ -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 diff --git a/core/p2pnetwork.go b/core/p2pnetwork.go index 440821c..3ae14b3 100644 --- a/core/p2pnetwork.go +++ b/core/p2pnetwork.go @@ -115,6 +115,7 @@ func (pn *P2PNetwork) run() { pn.write(MsgHeartbeat, 0, "") case <-pn.restartCh: gLog.Printf(LvDEBUG, "got restart channel") + GNetwork.sdwan.reset() pn.online = false pn.wgReconnect.Wait() // wait read/autorunapp goroutine end delay := ClientAPITimeout + time.Duration(rand.Int()%pn.loginMaxDelaySeconds)*time.Second @@ -124,6 +125,7 @@ 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.LogPeerNode()) pn.apps.Range(func(id, i interface{}) bool { diff --git a/core/p2ptunnel.go b/core/p2ptunnel.go index 36e4ad7..e4ed9ce 100644 --- a/core/p2ptunnel.go +++ b/core/p2ptunnel.go @@ -426,7 +426,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 +455,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{} @@ -512,14 +512,14 @@ func (t *P2PTunnel) connectUnderlayTCP6() (c underlay, err error) { t.pn.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)) diff --git a/core/ping.go b/core/ping.go new file mode 100644 index 0000000..1882aa6 --- /dev/null +++ b/core/ping.go @@ -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) + } +} diff --git a/core/protocol.go b/core/protocol.go index 737d0f0..c9ca1bd 100644 --- a/core/protocol.go +++ b/core/protocol.go @@ -10,7 +10,7 @@ import ( "time" ) -const OpenP2PVersion = "3.19.0" +const OpenP2PVersion = "3.21.8" const ProductName string = "openp2p" const LeastSupportVersion = "3.0.0" const SyncServerTimeVersion = "3.9.0" @@ -495,7 +495,7 @@ type SDWANInfo struct { ForceRelay int32 `json:"forceRelay,omitempty"` PunchPriority int32 `json:"punchPriority,omitempty"` Enable int32 `json:"enable,omitempty"` - Nodes []SDWANNode + Nodes []*SDWANNode } const ( diff --git a/core/sdwan.go b/core/sdwan.go index 033bedc..ac96e92 100644 --- a/core/sdwan.go +++ b/core/sdwan.go @@ -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) } } diff --git a/core/v4listener.go b/core/v4listener.go index 05caa92..7931908 100644 --- a/core/v4listener.go +++ b/core/v4listener.go @@ -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