mirror of
https://github.com/openp2p-cn/openp2p.git
synced 2025-09-27 04:56:03 +08:00
515 lines
13 KiB
Go
515 lines
13 KiB
Go
package openp2p
|
|
|
|
import (
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var gConf Config
|
|
|
|
type AppConfig struct {
|
|
// required
|
|
AppName string
|
|
Protocol string
|
|
UnderlayProtocol string
|
|
PunchPriority int // bitwise DisableTCP|DisableUDP|TCPFirst 0:tcp and udp both enable, udp first
|
|
Whitelist string
|
|
SrcPort int
|
|
PeerNode string
|
|
DstPort int
|
|
DstHost string
|
|
PeerUser string
|
|
RelayNode string
|
|
ForceRelay int // default:0 disable;1 enable
|
|
Enabled int // default:1
|
|
// runtime info
|
|
relayMode string // private|public
|
|
peerVersion string
|
|
peerToken uint64
|
|
peerNatType int
|
|
peerLanIP string
|
|
hasIPv4 int
|
|
peerIPv6 string
|
|
hasUPNPorNATPMP int
|
|
peerIP string
|
|
peerConeNatPort int
|
|
retryNum int
|
|
retryTime time.Time
|
|
nextRetryTime time.Time
|
|
shareBandwidth int
|
|
errMsg string
|
|
connectTime time.Time
|
|
fromToken uint64
|
|
linkMode string
|
|
isUnderlayServer int
|
|
}
|
|
|
|
const (
|
|
PunchPriorityTCPFirst = 1
|
|
PunchPriorityUDPDisable = 1 << 1
|
|
PunchPriorityTCPDisable = 1 << 2
|
|
)
|
|
|
|
func (c *AppConfig) ID() uint64 {
|
|
if c.SrcPort == 0 { // memapp
|
|
return NodeNameToID(c.PeerNode)
|
|
}
|
|
if c.Protocol == "tcp" {
|
|
return uint64(c.SrcPort) * 10
|
|
}
|
|
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
|
|
}
|
|
|
|
func (c *Config) getSDWAN() SDWANInfo {
|
|
c.sdwanMtx.Lock()
|
|
defer c.sdwanMtx.Unlock()
|
|
return c.sdwan
|
|
}
|
|
|
|
func (c *Config) getDelNodes() []*SDWANNode {
|
|
c.sdwanMtx.Lock()
|
|
defer c.sdwanMtx.Unlock()
|
|
return c.delNodes
|
|
}
|
|
|
|
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{}
|
|
for _, oldNode := range c.sdwan.Nodes {
|
|
isDeleted := true
|
|
for _, newNode := range s.Nodes {
|
|
if oldNode.Name == newNode.Name && oldNode.IP == newNode.IP && oldNode.Resource == newNode.Resource && c.sdwan.Mode == s.Mode && c.sdwan.CentralNode == s.CentralNode {
|
|
isDeleted = false
|
|
break
|
|
}
|
|
}
|
|
if isDeleted {
|
|
c.delNodes = append(c.delNodes, oldNode)
|
|
}
|
|
}
|
|
// get new-old
|
|
c.addNodes = []*SDWANNode{}
|
|
for _, newNode := range s.Nodes {
|
|
isNew := true
|
|
for _, oldNode := range c.sdwan.Nodes {
|
|
if oldNode.Name == newNode.Name && oldNode.IP == newNode.IP && oldNode.Resource == newNode.Resource && c.sdwan.Mode == s.Mode && c.sdwan.CentralNode == s.CentralNode {
|
|
isNew = false
|
|
break
|
|
}
|
|
}
|
|
if isNew {
|
|
c.addNodes = append(c.addNodes, newNode)
|
|
}
|
|
}
|
|
c.sdwan = s
|
|
}
|
|
|
|
func (c *Config) switchApp(app AppConfig, enabled int) {
|
|
c.mtx.Lock()
|
|
defer c.mtx.Unlock()
|
|
for i := 0; i < len(c.Apps); i++ {
|
|
if c.Apps[i].Protocol == app.Protocol && c.Apps[i].SrcPort == app.SrcPort {
|
|
c.Apps[i].Enabled = enabled
|
|
c.Apps[i].retryNum = 0
|
|
c.Apps[i].nextRetryTime = time.Now()
|
|
break
|
|
}
|
|
}
|
|
c.save()
|
|
}
|
|
|
|
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 ", app.config.LogPeerNode())
|
|
app.config.retryNum = 0
|
|
app.config.nextRetryTime = time.Now()
|
|
app.retryRelayNum = 0
|
|
app.nextRetryRelayTime = time.Now()
|
|
app.hbMtx.Lock()
|
|
app.hbTimeRelay = time.Now().Add(-TunnelHeartbeatTime * 3)
|
|
app.hbMtx.Unlock()
|
|
}
|
|
if app.config.RelayNode == peerNode {
|
|
gLog.Println(LvDEBUG, "retry app ", app.config.LogPeerNode())
|
|
app.retryRelayNum = 0
|
|
app.nextRetryRelayTime = time.Now()
|
|
app.hbMtx.Lock()
|
|
app.hbTimeRelay = time.Now().Add(-TunnelHeartbeatTime * 3)
|
|
app.hbMtx.Unlock()
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
|
|
func (c *Config) retryAllApp() {
|
|
GNetwork.apps.Range(func(id, i interface{}) bool {
|
|
app := i.(*p2pApp)
|
|
gLog.Println(LvDEBUG, "retry app ", app.config.LogPeerNode())
|
|
app.config.retryNum = 0
|
|
app.config.nextRetryTime = time.Now()
|
|
app.retryRelayNum = 0
|
|
app.nextRetryRelayTime = time.Now()
|
|
app.hbMtx.Lock()
|
|
defer app.hbMtx.Unlock()
|
|
app.hbTimeRelay = time.Now().Add(-TunnelHeartbeatTime * 3)
|
|
return true
|
|
})
|
|
}
|
|
|
|
func (c *Config) retryAllMemApp() {
|
|
GNetwork.apps.Range(func(id, i interface{}) bool {
|
|
app := i.(*p2pApp)
|
|
if app.config.SrcPort != 0 {
|
|
return true
|
|
}
|
|
gLog.Println(LvDEBUG, "retry app ", app.config.LogPeerNode())
|
|
app.config.retryNum = 0
|
|
app.config.nextRetryTime = time.Now()
|
|
app.retryRelayNum = 0
|
|
app.nextRetryRelayTime = time.Now()
|
|
app.hbMtx.Lock()
|
|
defer app.hbMtx.Unlock()
|
|
app.hbTimeRelay = time.Now().Add(-TunnelHeartbeatTime * 3)
|
|
return true
|
|
})
|
|
}
|
|
|
|
func (c *Config) add(app AppConfig, override bool) {
|
|
c.mtx.Lock()
|
|
defer c.mtx.Unlock()
|
|
defer c.save()
|
|
if override {
|
|
for i := 0; i < len(c.Apps); i++ {
|
|
if c.Apps[i].PeerNode == app.PeerNode && c.Apps[i].Protocol == app.Protocol && c.Apps[i].SrcPort == app.SrcPort {
|
|
c.Apps[i] = &app // override it
|
|
return
|
|
}
|
|
}
|
|
}
|
|
c.Apps = append(c.Apps, &app)
|
|
}
|
|
|
|
func (c *Config) delete(app AppConfig) {
|
|
c.mtx.Lock()
|
|
defer c.mtx.Unlock()
|
|
defer c.save()
|
|
for i := 0; i < len(c.Apps); i++ {
|
|
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 {
|
|
c.Apps = append(c.Apps[:i], c.Apps[i+1:]...)
|
|
}
|
|
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 {
|
|
gLog.Println(LvERROR, "save config.json error:", err)
|
|
}
|
|
}
|
|
|
|
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 {
|
|
gLog.Println(LvERROR, "save config.json0 error:", err)
|
|
}
|
|
}
|
|
|
|
func init() {
|
|
gConf.LogLevel = int(LvINFO)
|
|
gConf.MaxLogSize = 1024 * 1024
|
|
gConf.Network.ShareBandwidth = 10
|
|
gConf.Network.ServerHost = "api.openp2p.cn"
|
|
gConf.Network.ServerPort = WsPort
|
|
|
|
}
|
|
|
|
func (c *Config) load() error {
|
|
c.mtx.Lock()
|
|
defer c.mtx.Unlock()
|
|
data, err := os.ReadFile("config.json")
|
|
if err != nil {
|
|
return c.loadCache()
|
|
}
|
|
err = json.Unmarshal(data, &c)
|
|
if err != nil {
|
|
gLog.Println(LvERROR, "parse config.json error:", err)
|
|
// try cache
|
|
return c.loadCache()
|
|
}
|
|
// load ok. cache it
|
|
var filteredApps []*AppConfig // filter memapp
|
|
for _, app := range c.Apps {
|
|
if app.SrcPort != 0 {
|
|
filteredApps = append(filteredApps, app)
|
|
}
|
|
}
|
|
c.Apps = filteredApps
|
|
c.saveCache()
|
|
return err
|
|
}
|
|
|
|
func (c *Config) loadCache() error {
|
|
data, err := os.ReadFile("config.json0")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = json.Unmarshal(data, &c)
|
|
if err != nil {
|
|
gLog.Println(LvERROR, "parse config.json0 error:", err)
|
|
}
|
|
return err
|
|
}
|
|
|
|
// deal with multi-thread r/w
|
|
func (c *Config) setToken(token uint64) {
|
|
c.mtx.Lock()
|
|
defer c.mtx.Unlock()
|
|
defer c.save()
|
|
if token != 0 {
|
|
c.Network.Token = token
|
|
}
|
|
}
|
|
func (c *Config) setUser(user string) {
|
|
c.mtx.Lock()
|
|
defer c.mtx.Unlock()
|
|
defer c.save()
|
|
c.Network.User = user
|
|
}
|
|
func (c *Config) setNode(node string) {
|
|
c.mtx.Lock()
|
|
defer c.mtx.Unlock()
|
|
defer c.save()
|
|
c.Network.Node = node
|
|
c.Network.nodeID = NodeNameToID(c.Network.Node)
|
|
}
|
|
func (c *Config) nodeID() uint64 {
|
|
c.mtx.Lock()
|
|
defer c.mtx.Unlock()
|
|
if c.Network.nodeID == 0 {
|
|
c.Network.nodeID = NodeNameToID(c.Network.Node)
|
|
}
|
|
return c.Network.nodeID
|
|
}
|
|
func (c *Config) setShareBandwidth(bw int) {
|
|
c.mtx.Lock()
|
|
defer c.mtx.Unlock()
|
|
defer c.save()
|
|
c.Network.ShareBandwidth = bw
|
|
}
|
|
func (c *Config) setIPv6(v6 string) {
|
|
c.mtx.Lock()
|
|
defer c.mtx.Unlock()
|
|
c.Network.publicIPv6 = v6
|
|
}
|
|
func (c *Config) IPv6() string {
|
|
c.mtx.Lock()
|
|
defer c.mtx.Unlock()
|
|
return c.Network.publicIPv6
|
|
}
|
|
|
|
type NetworkConfig struct {
|
|
// local info
|
|
Token uint64
|
|
Node string
|
|
nodeID uint64
|
|
User string
|
|
localIP string
|
|
mac string
|
|
os string
|
|
publicIP string
|
|
natType int
|
|
hasIPv4 int
|
|
publicIPv6 string // must lowwer-case not save json
|
|
hasUPNPorNATPMP int
|
|
ShareBandwidth int
|
|
// server info
|
|
ServerHost string
|
|
ServerPort int
|
|
UDPPort1 int
|
|
UDPPort2 int
|
|
TCPPort int
|
|
}
|
|
|
|
func parseParams(subCommand string, cmd string) {
|
|
fset := flag.NewFlagSet(subCommand, flag.ExitOnError)
|
|
serverHost := fset.String("serverhost", "api.openp2p.cn", "server host ")
|
|
serverPort := fset.Int("serverport", WsPort, "server port ")
|
|
// serverHost := flag.String("serverhost", "127.0.0.1", "server host ") // for debug
|
|
token := fset.Uint64("token", 0, "token")
|
|
node := fset.String("node", "", "node name. 8-31 characters. if not set, it will be hostname")
|
|
peerNode := fset.String("peernode", "", "peer node name that you want to connect")
|
|
dstIP := fset.String("dstip", "127.0.0.1", "destination ip ")
|
|
whiteList := fset.String("whitelist", "", "whitelist for p2pApp ")
|
|
dstPort := fset.Int("dstport", 0, "destination port ")
|
|
srcPort := fset.Int("srcport", 0, "source port ")
|
|
tcpPort := fset.Int("tcpport", 0, "tcp port for upnp or publicip")
|
|
protocol := fset.String("protocol", "tcp", "tcp or udp")
|
|
underlayProtocol := fset.String("underlay_protocol", "quic", "quic or kcp")
|
|
punchPriority := fset.Int("punch_priority", 0, "bitwise DisableTCP|DisableUDP|UDPFirst 0:tcp and udp both enable, tcp first")
|
|
appName := fset.String("appname", "", "app name")
|
|
relayNode := fset.String("relaynode", "", "relaynode")
|
|
shareBandwidth := fset.Int("sharebandwidth", 10, "N mbps share bandwidth limit, private network no limit")
|
|
daemonMode := fset.Bool("d", false, "daemonMode")
|
|
notVerbose := fset.Bool("nv", false, "not log console")
|
|
newconfig := fset.Bool("newconfig", false, "not load existing config.json")
|
|
logLevel := fset.Int("loglevel", 1, "0:debug 1:info 2:warn 3:error")
|
|
maxLogSize := fset.Int("maxlogsize", 1024*1024, "default 1MB")
|
|
if cmd == "" {
|
|
if subCommand == "" { // no subcommand
|
|
fset.Parse(os.Args[1:])
|
|
} else {
|
|
fset.Parse(os.Args[2:])
|
|
}
|
|
} else {
|
|
gLog.Println(LvINFO, "cmd=", cmd)
|
|
args := strings.Split(cmd, " ")
|
|
fset.Parse(args)
|
|
}
|
|
|
|
gLog.setMaxSize(int64(*maxLogSize))
|
|
config := AppConfig{Enabled: 1}
|
|
config.PeerNode = *peerNode
|
|
config.DstHost = *dstIP
|
|
config.Whitelist = *whiteList
|
|
config.DstPort = *dstPort
|
|
config.SrcPort = *srcPort
|
|
config.Protocol = *protocol
|
|
config.UnderlayProtocol = *underlayProtocol
|
|
config.PunchPriority = *punchPriority
|
|
config.AppName = *appName
|
|
config.RelayNode = *relayNode
|
|
if !*newconfig {
|
|
gConf.load() // load old config. otherwise will clear all apps
|
|
}
|
|
if config.SrcPort != 0 { // filter memapp
|
|
gConf.add(config, true)
|
|
}
|
|
// gConf.mtx.Lock() // when calling this func it's single-thread no lock
|
|
gConf.daemonMode = *daemonMode
|
|
// spec paramters in commandline will always be used
|
|
fset.Visit(func(f *flag.Flag) {
|
|
if f.Name == "sharebandwidth" {
|
|
gConf.Network.ShareBandwidth = *shareBandwidth
|
|
}
|
|
if f.Name == "node" {
|
|
gConf.setNode(*node)
|
|
}
|
|
if f.Name == "serverhost" {
|
|
gConf.Network.ServerHost = *serverHost
|
|
}
|
|
if f.Name == "loglevel" {
|
|
gConf.LogLevel = *logLevel
|
|
}
|
|
if f.Name == "maxlogsize" {
|
|
gConf.MaxLogSize = *maxLogSize
|
|
}
|
|
if f.Name == "tcpport" {
|
|
gConf.Network.TCPPort = *tcpPort
|
|
}
|
|
if f.Name == "token" {
|
|
gConf.setToken(*token)
|
|
}
|
|
})
|
|
// set default value
|
|
if gConf.Network.ServerHost == "" {
|
|
gConf.Network.ServerHost = *serverHost
|
|
}
|
|
if *node != "" {
|
|
gConf.setNode(*node)
|
|
} else {
|
|
envNode := os.Getenv("OPENP2P_NODE")
|
|
if envNode != "" {
|
|
gConf.setNode(envNode)
|
|
}
|
|
if gConf.Network.Node == "" { // if node name not set. use os.Hostname
|
|
gConf.setNode(defaultNodeName())
|
|
}
|
|
}
|
|
if gConf.Network.TCPPort == 0 {
|
|
if *tcpPort == 0 {
|
|
p := int(gConf.nodeID()%15000 + 50000)
|
|
tcpPort = &p
|
|
}
|
|
gConf.Network.TCPPort = *tcpPort
|
|
}
|
|
if *token == 0 {
|
|
envToken := os.Getenv("OPENP2P_TOKEN")
|
|
if envToken != "" {
|
|
if n, err := strconv.ParseUint(envToken, 10, 64); n != 0 && err == nil {
|
|
gConf.setToken(n)
|
|
}
|
|
}
|
|
}
|
|
gConf.Network.ServerPort = *serverPort
|
|
gConf.Network.UDPPort1 = UDPPort1
|
|
gConf.Network.UDPPort2 = UDPPort2
|
|
gLog.setLevel(LogLevel(gConf.LogLevel))
|
|
if *notVerbose {
|
|
gLog.setMode(LogFile)
|
|
}
|
|
// gConf.mtx.Unlock()
|
|
gConf.save()
|
|
}
|