mirror of
https://github.com/openp2p-cn/openp2p.git
synced 2026-04-09 22:08:02 +08:00
1.5.5
This commit is contained in:
@@ -95,6 +95,7 @@ Windows默认会阻止没有花钱买它家证书签名过的程序,选择“
|
||||
服务端有个调度模型,根据带宽、ping值、稳定性、服务时长,尽可能地使共享节点均匀地提供服务。连接共享节点使用TOTP密码,hmac-sha256算法校验,它是一次性密码,和我们平时使用的手机验证码或银行密码器一样的原理。
|
||||
|
||||
## 编译
|
||||
go version go1.18.1+
|
||||
cd到代码根目录,执行
|
||||
```
|
||||
export GOPROXY=https://goproxy.io,direct
|
||||
|
||||
@@ -103,6 +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+
|
||||
cd root directory of the socure code and execute
|
||||
```
|
||||
export GOPROXY=https://goproxy.io,direct
|
||||
|
||||
39
common.go
39
common.go
@@ -12,6 +12,8 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -77,6 +79,7 @@ func pkcs7UnPadding(origData []byte, dataLen int) ([]byte, error) {
|
||||
return origData[:(dataLen - unPadLen)], nil
|
||||
}
|
||||
|
||||
// AES-CBC
|
||||
func encryptBytes(key []byte, out, in []byte, plainLen int) ([]byte, error) {
|
||||
if len(key) == 0 {
|
||||
return in[:plainLen], nil
|
||||
@@ -122,20 +125,20 @@ func netInfo() *NetInfo {
|
||||
client := &http.Client{Transport: tr, Timeout: time.Second * 10}
|
||||
r, err := client.Get("https://ifconfig.co/json")
|
||||
if err != nil {
|
||||
gLog.Println(LevelINFO, "netInfo error:", err)
|
||||
gLog.Println(LvINFO, "netInfo error:", err)
|
||||
continue
|
||||
}
|
||||
defer r.Body.Close()
|
||||
buf := make([]byte, 1024*64)
|
||||
n, err := r.Body.Read(buf)
|
||||
if err != nil {
|
||||
gLog.Println(LevelINFO, "netInfo error:", err)
|
||||
gLog.Println(LvINFO, "netInfo error:", err)
|
||||
continue
|
||||
}
|
||||
rsp := NetInfo{}
|
||||
err = json.Unmarshal(buf[:n], &rsp)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "wrong NetInfo:%s", err)
|
||||
gLog.Printf(LvERROR, "wrong NetInfo:%s", err)
|
||||
continue
|
||||
}
|
||||
return &rsp
|
||||
@@ -158,3 +161,33 @@ func defaultNodeName() string {
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
const EQUAL int = 0
|
||||
const GREATER int = 1
|
||||
const LESS int = -1
|
||||
|
||||
func compareVersion(v1, v2 string) int {
|
||||
if v1 == v2 {
|
||||
return EQUAL
|
||||
}
|
||||
v1Arr := strings.Split(v1, ".")
|
||||
v2Arr := strings.Split(v2, ".")
|
||||
for i, subVer := range v1Arr {
|
||||
if len(v2Arr) <= i {
|
||||
return GREATER
|
||||
}
|
||||
subv1, _ := strconv.Atoi(subVer)
|
||||
subv2, _ := strconv.Atoi(v2Arr[i])
|
||||
if subv1 > subv2 {
|
||||
return GREATER
|
||||
}
|
||||
if subv1 < subv2 {
|
||||
return LESS
|
||||
}
|
||||
}
|
||||
return LESS
|
||||
}
|
||||
|
||||
func IsIPv6(address string) bool {
|
||||
return strings.Count(address, ":") >= 2
|
||||
}
|
||||
|
||||
@@ -43,3 +43,29 @@ func TestAESCBC(t *testing.T) {
|
||||
func TestNetInfo(t *testing.T) {
|
||||
log.Println(netInfo())
|
||||
}
|
||||
|
||||
func assertCompareVersion(t *testing.T, v1 string, v2 string, result int) {
|
||||
if compareVersion(v1, v2) != result {
|
||||
t.Errorf("compare version %s %s fail\n", v1, v2)
|
||||
}
|
||||
}
|
||||
func TestCompareVersion(t *testing.T) {
|
||||
// test =
|
||||
assertCompareVersion(t, "0.98.0", "0.98.0", EQUAL)
|
||||
assertCompareVersion(t, "0.98", "0.98", EQUAL)
|
||||
assertCompareVersion(t, "1.4.0", "1.4.0", EQUAL)
|
||||
assertCompareVersion(t, "1.5.0", "1.5.0", EQUAL)
|
||||
// test >
|
||||
assertCompareVersion(t, "0.98.0.22345", "0.98.0.12345", GREATER)
|
||||
assertCompareVersion(t, "1.98.0.12345", "0.98", GREATER)
|
||||
assertCompareVersion(t, "10.98.0.12345", "9.98.0.12345", GREATER)
|
||||
assertCompareVersion(t, "1.4.0", "0.98.0.12345", GREATER)
|
||||
assertCompareVersion(t, "1.4", "0.98.0.12345", GREATER)
|
||||
assertCompareVersion(t, "1", "0.98.0.12345", GREATER)
|
||||
// test <
|
||||
assertCompareVersion(t, "0.98.0.12345", "0.98.0.12346", LESS)
|
||||
assertCompareVersion(t, "9.98.0.12345", "10.98.0.12345", LESS)
|
||||
assertCompareVersion(t, "1.4.2", "1.5.0", LESS)
|
||||
assertCompareVersion(t, "", "1.5.0", LESS)
|
||||
|
||||
}
|
||||
|
||||
95
config.go
95
config.go
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -23,8 +24,12 @@ type AppConfig struct {
|
||||
PeerUser string
|
||||
Enabled int // default:1
|
||||
// runtime info
|
||||
peerVersion string
|
||||
peerToken uint64
|
||||
peerNatType int
|
||||
hasIPv4 int
|
||||
IPv6 string
|
||||
hasUPNPorNATPMP int
|
||||
peerIP string
|
||||
peerConeNatPort int
|
||||
retryNum int
|
||||
@@ -61,7 +66,7 @@ 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)
|
||||
gLog.Println(LvERROR, "invalid app ", app)
|
||||
return
|
||||
}
|
||||
if override {
|
||||
@@ -95,7 +100,7 @@ func (c *Config) save() {
|
||||
data, _ := json.MarshalIndent(c, "", " ")
|
||||
err := ioutil.WriteFile("config.json", data, 0644)
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "save config.json error:", err)
|
||||
gLog.Println(LvERROR, "save config.json error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +116,7 @@ func (c *Config) load() error {
|
||||
}
|
||||
err = json.Unmarshal(data, &c)
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "parse config.json error:", err)
|
||||
gLog.Println(LvERROR, "parse config.json error:", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -139,16 +144,19 @@ func (c *Config) setShareBandwidth(bw int) {
|
||||
|
||||
type NetworkConfig struct {
|
||||
// local info
|
||||
Token uint64
|
||||
Node string
|
||||
User string
|
||||
localIP string
|
||||
ipv6 string
|
||||
mac string
|
||||
os string
|
||||
publicIP string
|
||||
natType int
|
||||
ShareBandwidth int
|
||||
Token uint64
|
||||
Node string
|
||||
User string
|
||||
localIP string
|
||||
ipv6 string
|
||||
mac string
|
||||
os string
|
||||
publicIP string
|
||||
natType int
|
||||
hasIPv4 int
|
||||
IPv6 string
|
||||
hasUPNPorNATPMP int
|
||||
ShareBandwidth int
|
||||
// server info
|
||||
ServerHost string
|
||||
ServerPort int
|
||||
@@ -156,22 +164,28 @@ type NetworkConfig struct {
|
||||
UDPPort2 int
|
||||
}
|
||||
|
||||
func parseParams() {
|
||||
serverHost := flag.String("serverhost", "api.openp2p.cn", "server host ")
|
||||
func parseParams(subCommand string) {
|
||||
fset := flag.NewFlagSet(subCommand, flag.ExitOnError)
|
||||
serverHost := fset.String("serverhost", "api.openp2p.cn", "server host ")
|
||||
// serverHost := flag.String("serverhost", "127.0.0.1", "server host ") // for debug
|
||||
node := flag.String("node", "", "node name. 8-31 characters")
|
||||
token := flag.Uint64("token", 0, "token")
|
||||
peerNode := flag.String("peernode", "", "peer node name that you want to connect")
|
||||
dstIP := flag.String("dstip", "127.0.0.1", "destination ip ")
|
||||
dstPort := flag.Int("dstport", 0, "destination port ")
|
||||
srcPort := flag.Int("srcport", 0, "source port ")
|
||||
protocol := flag.String("protocol", "tcp", "tcp or udp")
|
||||
appName := flag.String("appname", "", "app name")
|
||||
shareBandwidth := flag.Int("sharebandwidth", 10, "N mbps share bandwidth limit, private network no limit")
|
||||
daemonMode := flag.Bool("d", false, "daemonMode")
|
||||
notVerbose := flag.Bool("nv", false, "not log console")
|
||||
logLevel := flag.Int("loglevel", 1, "0:debug 1:info 2:warn 3:error")
|
||||
flag.Parse()
|
||||
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 ")
|
||||
dstPort := fset.Int("dstport", 0, "destination port ")
|
||||
srcPort := fset.Int("srcport", 0, "source port ")
|
||||
protocol := fset.String("protocol", "tcp", "tcp or udp")
|
||||
appName := fset.String("appname", "", "app name")
|
||||
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")
|
||||
if subCommand == "" { // no subcommand
|
||||
fset.Parse(os.Args[1:])
|
||||
} else {
|
||||
fset.Parse(os.Args[2:])
|
||||
}
|
||||
|
||||
config := AppConfig{Enabled: 1}
|
||||
config.PeerNode = *peerNode
|
||||
@@ -180,14 +194,17 @@ func parseParams() {
|
||||
config.SrcPort = *srcPort
|
||||
config.Protocol = *protocol
|
||||
config.AppName = *appName
|
||||
gConf.load()
|
||||
if !*newconfig {
|
||||
gConf.load() // load old config. otherwise will clear all apps
|
||||
}
|
||||
gConf.LogLevel = *logLevel
|
||||
if config.SrcPort != 0 {
|
||||
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
|
||||
flag.Visit(func(f *flag.Flag) {
|
||||
fset.Visit(func(f *flag.Flag) {
|
||||
if f.Name == "sharebandwidth" {
|
||||
gConf.Network.ShareBandwidth = *shareBandwidth
|
||||
}
|
||||
@@ -205,16 +222,20 @@ func parseParams() {
|
||||
if gConf.Network.ServerHost == "" {
|
||||
gConf.Network.ServerHost = *serverHost
|
||||
}
|
||||
if gConf.Network.Node == "" {
|
||||
if *node == "" { // config and param's node both empty
|
||||
hostname := defaultNodeName()
|
||||
node = &hostname
|
||||
}
|
||||
gConf.Network.Node = *node
|
||||
}
|
||||
if *token != 0 {
|
||||
gConf.Network.Token = *token
|
||||
}
|
||||
if *node != "" {
|
||||
if len(*node) < 8 {
|
||||
gLog.Println(LvERROR, ErrNodeTooShort)
|
||||
os.Exit(9)
|
||||
}
|
||||
gConf.Network.Node = *node
|
||||
} else {
|
||||
if gConf.Network.Node == "" { // if node name not set. use os.Hostname
|
||||
gConf.Network.Node = defaultNodeName()
|
||||
}
|
||||
}
|
||||
if gConf.LogLevel == IntValueNotSet {
|
||||
gConf.LogLevel = *logLevel
|
||||
}
|
||||
|
||||
188
daemon.go
188
daemon.go
@@ -1,13 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kardianos/service"
|
||||
@@ -19,34 +15,34 @@ type daemon struct {
|
||||
}
|
||||
|
||||
func (d *daemon) Start(s service.Service) error {
|
||||
gLog.Println(LevelINFO, "daemon start")
|
||||
gLog.Println(LvINFO, "daemon start")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *daemon) Stop(s service.Service) error {
|
||||
gLog.Println(LevelINFO, "service stop")
|
||||
gLog.Println(LvINFO, "service stop")
|
||||
d.running = false
|
||||
if d.proc != nil {
|
||||
gLog.Println(LevelINFO, "stop worker")
|
||||
gLog.Println(LvINFO, "stop worker")
|
||||
d.proc.Kill()
|
||||
}
|
||||
if service.Interactive() {
|
||||
gLog.Println(LevelINFO, "stop daemon")
|
||||
gLog.Println(LvINFO, "stop daemon")
|
||||
os.Exit(0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *daemon) run() {
|
||||
gLog.Println(LevelINFO, "daemon run start")
|
||||
defer gLog.Println(LevelINFO, "daemon run end")
|
||||
gLog.Println(LvINFO, "daemon run start")
|
||||
defer gLog.Println(LvINFO, "daemon run end")
|
||||
d.running = true
|
||||
binPath, _ := os.Executable()
|
||||
mydir, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
gLog.Println(LevelINFO, mydir)
|
||||
gLog.Println(LvINFO, mydir)
|
||||
conf := &service.Config{
|
||||
Name: ProducnName,
|
||||
DisplayName: ProducnName,
|
||||
@@ -71,14 +67,14 @@ func (d *daemon) run() {
|
||||
dumpFile := filepath.Join("log", "dump.log")
|
||||
f, err := os.Create(filepath.Join(tmpDump))
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "start worker error:%s", err)
|
||||
gLog.Printf(LvERROR, "start worker error:%s", err)
|
||||
return
|
||||
}
|
||||
gLog.Println(LevelINFO, "start worker process, args:", args)
|
||||
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}}
|
||||
p, err := os.StartProcess(binPath, args, execSpec)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "start worker error:%s", err)
|
||||
gLog.Printf(LvERROR, "start worker error:%s", err)
|
||||
return
|
||||
}
|
||||
d.proc = p
|
||||
@@ -87,12 +83,12 @@ func (d *daemon) run() {
|
||||
time.Sleep(time.Second)
|
||||
err = os.Rename(tmpDump, dumpFile)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "rename dump error:%s", err)
|
||||
gLog.Printf(LvERROR, "rename dump error:%s", err)
|
||||
}
|
||||
if !d.running {
|
||||
return
|
||||
}
|
||||
gLog.Printf(LevelERROR, "worker stop, restart it after 10s")
|
||||
gLog.Printf(LvERROR, "worker stop, restart it after 10s")
|
||||
time.Sleep(time.Second * 10)
|
||||
}
|
||||
}
|
||||
@@ -117,163 +113,3 @@ func (d *daemon) Control(ctrlComm string, exeAbsPath string, args []string) erro
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// examples:
|
||||
// listen:
|
||||
// ./openp2p install -node hhd1207-222 -token YOUR-TOKEN -sharebandwidth 0
|
||||
// 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
|
||||
err := os.MkdirAll(defaultInstallPath, 0775)
|
||||
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "MkdirAll %s error:%s", defaultInstallPath, err)
|
||||
return
|
||||
}
|
||||
err = os.Chdir(defaultInstallPath)
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "cd error:", err)
|
||||
return
|
||||
}
|
||||
|
||||
uninstall()
|
||||
// save config file
|
||||
installFlag := flag.NewFlagSet("install", flag.ExitOnError)
|
||||
serverHost := installFlag.String("serverhost", "api.openp2p.cn", "server host ")
|
||||
// serverHost := flag.String("serverhost", "127.0.0.1", "server host ") // for debug
|
||||
token := installFlag.Uint64("token", 0, "token")
|
||||
node := installFlag.String("node", "", "node name. 8-31 characters. if not set, it will be hostname")
|
||||
peerNode := installFlag.String("peernode", "", "peer node name that you want to connect")
|
||||
dstIP := installFlag.String("dstip", "127.0.0.1", "destination ip ")
|
||||
dstPort := installFlag.Int("dstport", 0, "destination port ")
|
||||
srcPort := installFlag.Int("srcport", 0, "source port ")
|
||||
protocol := installFlag.String("protocol", "tcp", "tcp or udp")
|
||||
appName := flag.String("appname", "", "app name")
|
||||
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:])
|
||||
|
||||
gConf.load() // load old config. otherwise will clear all apps
|
||||
gConf.LogLevel = *logLevel
|
||||
gConf.Network.ServerHost = *serverHost
|
||||
gConf.Network.Token = *token
|
||||
if *node != "" {
|
||||
if len(*node) < 8 {
|
||||
gLog.Println(LevelERROR, ErrNodeTooShort)
|
||||
os.Exit(9)
|
||||
}
|
||||
gConf.Network.Node = *node
|
||||
} else {
|
||||
if gConf.Network.Node == "" { // if node name not set. use os.Hostname
|
||||
gConf.Network.Node = defaultNodeName()
|
||||
}
|
||||
}
|
||||
gConf.Network.ServerPort = 27183
|
||||
gConf.Network.UDPPort1 = 27182
|
||||
gConf.Network.UDPPort2 = 27183
|
||||
gConf.Network.ShareBandwidth = *shareBandwidth
|
||||
config := AppConfig{Enabled: 1}
|
||||
config.PeerNode = *peerNode
|
||||
config.DstHost = *dstIP
|
||||
config.DstPort = *dstPort
|
||||
config.SrcPort = *srcPort
|
||||
config.Protocol = *protocol
|
||||
config.AppName = *appName
|
||||
if config.SrcPort != 0 {
|
||||
gConf.add(config, true)
|
||||
}
|
||||
gConf.save()
|
||||
targetPath := filepath.Join(defaultInstallPath, defaultBinName)
|
||||
d := daemon{}
|
||||
// copy files
|
||||
|
||||
binPath, _ := os.Executable()
|
||||
src, errFiles := os.Open(binPath) // can not use args[0], on Windows call openp2p is ok(=openp2p.exe)
|
||||
if errFiles != nil {
|
||||
gLog.Printf(LevelERROR, "os.OpenFile %s error:%s", os.Args[0], errFiles)
|
||||
return
|
||||
}
|
||||
|
||||
dst, errFiles := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0775)
|
||||
if errFiles != nil {
|
||||
gLog.Printf(LevelERROR, "os.OpenFile %s error:%s", targetPath, errFiles)
|
||||
return
|
||||
}
|
||||
|
||||
_, errFiles = io.Copy(dst, src)
|
||||
if errFiles != nil {
|
||||
gLog.Printf(LevelERROR, "io.Copy error:%s", errFiles)
|
||||
return
|
||||
}
|
||||
src.Close()
|
||||
dst.Close()
|
||||
|
||||
// install system service
|
||||
gLog.Println(LevelINFO, "targetPath:", targetPath)
|
||||
err = d.Control("install", targetPath, []string{"-d"})
|
||||
if err == nil {
|
||||
gLog.Println(LevelINFO, "install system service ok.")
|
||||
}
|
||||
time.Sleep(time.Second * 2)
|
||||
err = d.Control("start", targetPath, []string{"-d"})
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "start openp2p service error:", err)
|
||||
} else {
|
||||
gLog.Println(LevelINFO, "start openp2p service ok.")
|
||||
}
|
||||
}
|
||||
|
||||
func installByFilename() {
|
||||
params := strings.Split(filepath.Base(os.Args[0]), "-")
|
||||
if len(params) < 4 {
|
||||
return
|
||||
}
|
||||
serverHost := params[1]
|
||||
token := params[2]
|
||||
gLog.Println(LevelINFO, "install start")
|
||||
targetPath := os.Args[0]
|
||||
args := []string{"install"}
|
||||
args = append(args, "-serverhost")
|
||||
args = append(args, serverHost)
|
||||
args = append(args, "-token")
|
||||
args = append(args, token)
|
||||
env := os.Environ()
|
||||
cmd := exec.Command(targetPath, args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Env = env
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "install by filename, start process error:", err)
|
||||
return
|
||||
}
|
||||
gLog.Println(LevelINFO, "install end")
|
||||
fmt.Println("Press the Any Key to exit")
|
||||
fmt.Scanln()
|
||||
os.Exit(0)
|
||||
}
|
||||
func uninstall() {
|
||||
gLog.Println(LevelINFO, "uninstall start")
|
||||
defer gLog.Println(LevelINFO, "uninstall end")
|
||||
d := daemon{}
|
||||
err := d.Control("stop", "", nil)
|
||||
if err != nil { // service maybe not install
|
||||
return
|
||||
}
|
||||
err = d.Control("uninstall", "", nil)
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "uninstall system service error:", err)
|
||||
} else {
|
||||
gLog.Println(LevelINFO, "uninstall system service ok.")
|
||||
}
|
||||
binPath := filepath.Join(defaultInstallPath, defaultBinName)
|
||||
os.Remove(binPath + "0")
|
||||
os.Remove(binPath)
|
||||
// os.RemoveAll(defaultInstallPath) // reserve config.json
|
||||
}
|
||||
|
||||
21
go.mod
21
go.mod
@@ -1,10 +1,27 @@
|
||||
module openp2p
|
||||
|
||||
go 1.16
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/kardianos/service v1.2.0
|
||||
github.com/lucas-clemente/quic-go v0.24.0
|
||||
github.com/lucas-clemente/quic-go v0.27.0
|
||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cheekybits/genny v1.0.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/onsi/ginkgo v1.16.4 // indirect
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect
|
||||
golang.org/x/mod v0.4.2 // indirect
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect
|
||||
golang.org/x/tools v0.1.1 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
)
|
||||
|
||||
@@ -17,37 +17,38 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gLog.Printf(LevelDEBUG, "handle push msg type:%d, push header:%+v", subType, pushHead)
|
||||
gLog.Printf(LvDEBUG, "handle push msg type:%d, push header:%+v", subType, pushHead)
|
||||
switch subType {
|
||||
case MsgPushConnectReq:
|
||||
req := PushConnectReq{}
|
||||
err := json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "wrong MsgPushConnectReq:%s", err)
|
||||
gLog.Printf(LvERROR, "wrong MsgPushConnectReq:%s", err)
|
||||
return err
|
||||
}
|
||||
gLog.Printf(LevelINFO, "%s is connecting...", req.From)
|
||||
gLog.Println(LevelDEBUG, "push connect response to ", req.From)
|
||||
gLog.Printf(LvINFO, "%s is connecting...", req.From)
|
||||
gLog.Println(LvDEBUG, "push connect response to ", req.From)
|
||||
// verify totp token or token
|
||||
if VerifyTOTP(req.Token, pn.config.Token, time.Now().Unix()+(pn.serverTs-pn.localTs)) || // localTs may behind, auto adjust ts
|
||||
VerifyTOTP(req.Token, pn.config.Token, time.Now().Unix()) ||
|
||||
(req.FromToken == pn.config.Token) {
|
||||
gLog.Printf(LevelINFO, "Access Granted\n")
|
||||
gLog.Printf(LvINFO, "Access Granted\n")
|
||||
config := AppConfig{}
|
||||
config.peerNatType = req.NatType
|
||||
config.peerConeNatPort = req.ConeNatPort
|
||||
config.peerIP = req.FromIP
|
||||
config.PeerNode = req.From
|
||||
config.peerVersion = req.Version
|
||||
// share relay node will limit bandwidth
|
||||
if req.FromToken != pn.config.Token {
|
||||
gLog.Printf(LevelINFO, "set share bandwidth %d mbps", pn.config.ShareBandwidth)
|
||||
gLog.Printf(LvINFO, "set share bandwidth %d mbps", pn.config.ShareBandwidth)
|
||||
config.shareBandwidth = pn.config.ShareBandwidth
|
||||
}
|
||||
// go pn.AddTunnel(config, req.ID)
|
||||
go pn.addDirectTunnel(config, req.ID)
|
||||
break
|
||||
}
|
||||
gLog.Println(LevelERROR, "Access Denied:", req.From)
|
||||
gLog.Println(LvERROR, "Access Denied:", req.From)
|
||||
rsp := PushConnectRsp{
|
||||
Error: 1,
|
||||
Detail: fmt.Sprintf("connect to %s error: Access Denied", pn.config.Node),
|
||||
@@ -59,19 +60,19 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
|
||||
rsp := PushRsp{}
|
||||
err := json.Unmarshal(msg[openP2PHeaderSize:], &rsp)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "wrong pushRsp:%s", err)
|
||||
gLog.Printf(LvERROR, "wrong pushRsp:%s", err)
|
||||
return err
|
||||
}
|
||||
if rsp.Error == 0 {
|
||||
gLog.Printf(LevelDEBUG, "push ok, detail:%s", rsp.Detail)
|
||||
gLog.Printf(LvDEBUG, "push ok, detail:%s", rsp.Detail)
|
||||
} else {
|
||||
gLog.Printf(LevelERROR, "push error:%d, detail:%s", rsp.Error, rsp.Detail)
|
||||
gLog.Printf(LvERROR, "push error:%d, detail:%s", rsp.Error, rsp.Detail)
|
||||
}
|
||||
case MsgPushAddRelayTunnelReq:
|
||||
req := AddRelayTunnelReq{}
|
||||
err := json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "wrong RelayNodeRsp:%s", err)
|
||||
gLog.Printf(LvERROR, "wrong RelayNodeRsp:%s", err)
|
||||
return err
|
||||
}
|
||||
config := AppConfig{}
|
||||
@@ -83,12 +84,19 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
|
||||
// notify peer relay ready
|
||||
msg := TunnelMsg{ID: t.id}
|
||||
pn.push(r.From, MsgPushAddRelayTunnelRsp, msg)
|
||||
SaveKey(req.AppID, req.AppKey)
|
||||
}
|
||||
|
||||
}(req)
|
||||
case MsgPushAPPKey:
|
||||
req := APPKeySync{}
|
||||
err := json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req)
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "wrong APPKeySync:%s", err)
|
||||
return err
|
||||
}
|
||||
SaveKey(req.AppID, req.AppKey)
|
||||
case MsgPushUpdate:
|
||||
gLog.Println(LevelINFO, "MsgPushUpdate")
|
||||
gLog.Println(LvINFO, "MsgPushUpdate")
|
||||
update() // download new version first, then exec ./openp2p update
|
||||
targetPath := filepath.Join(defaultInstallPath, defaultBinName)
|
||||
args := []string{"update"}
|
||||
@@ -104,11 +112,11 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
|
||||
}
|
||||
return err
|
||||
case MsgPushRestart:
|
||||
gLog.Println(LevelINFO, "MsgPushRestart")
|
||||
gLog.Println(LvINFO, "MsgPushRestart")
|
||||
os.Exit(0)
|
||||
return err
|
||||
case MsgPushReportApps:
|
||||
gLog.Println(LevelINFO, "MsgPushReportApps")
|
||||
gLog.Println(LvINFO, "MsgPushReportApps")
|
||||
req := ReportApps{}
|
||||
gConf.mtx.Lock()
|
||||
defer gConf.mtx.Unlock()
|
||||
@@ -147,11 +155,11 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
|
||||
}
|
||||
pn.write(MsgReport, MsgReportApps, &req)
|
||||
case MsgPushEditApp:
|
||||
gLog.Println(LevelINFO, "MsgPushEditApp")
|
||||
gLog.Println(LvINFO, "MsgPushEditApp")
|
||||
newApp := AppInfo{}
|
||||
err := json.Unmarshal(msg[openP2PHeaderSize:], &newApp)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "wrong MsgPushEditApp:%s %s", err, string(msg[openP2PHeaderSize:]))
|
||||
gLog.Printf(LvERROR, "wrong MsgPushEditApp:%s %s", err, string(msg[openP2PHeaderSize:]))
|
||||
return err
|
||||
}
|
||||
oldConf := AppConfig{Enabled: 1}
|
||||
@@ -175,11 +183,11 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
|
||||
// pn.AddApp(config)
|
||||
// TODO: report result
|
||||
case MsgPushEditNode:
|
||||
gLog.Println(LevelINFO, "MsgPushEditNode")
|
||||
gLog.Println(LvINFO, "MsgPushEditNode")
|
||||
req := EditNode{}
|
||||
err := json.Unmarshal(msg[openP2PHeaderSize:], &req)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "wrong MsgPushEditNode:%s %s", err, string(msg[openP2PHeaderSize:]))
|
||||
gLog.Printf(LvERROR, "wrong MsgPushEditNode:%s %s", err, string(msg[openP2PHeaderSize:]))
|
||||
return err
|
||||
}
|
||||
gConf.setNode(req.NewName)
|
||||
@@ -188,15 +196,15 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
|
||||
// TODO: hot reload
|
||||
os.Exit(0)
|
||||
case MsgPushSwitchApp:
|
||||
gLog.Println(LevelINFO, "MsgPushSwitchApp")
|
||||
gLog.Println(LvINFO, "MsgPushSwitchApp")
|
||||
app := AppInfo{}
|
||||
err := json.Unmarshal(msg[openP2PHeaderSize:], &app)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "wrong MsgPushSwitchApp:%s %s", err, string(msg[openP2PHeaderSize:]))
|
||||
gLog.Printf(LvERROR, "wrong MsgPushSwitchApp:%s %s", err, string(msg[openP2PHeaderSize:]))
|
||||
return err
|
||||
}
|
||||
config := AppConfig{Enabled: app.Enabled, SrcPort: app.SrcPort, Protocol: app.Protocol}
|
||||
gLog.Println(LevelINFO, app.AppName, " switch to ", app.Enabled)
|
||||
gLog.Println(LvINFO, app.AppName, " switch to ", app.Enabled)
|
||||
gConf.switchApp(config, app.Enabled)
|
||||
if app.Enabled == 0 {
|
||||
// disable APP
|
||||
|
||||
58
holepunch.go
58
holepunch.go
@@ -11,8 +11,8 @@ import (
|
||||
)
|
||||
|
||||
func handshakeC2C(t *P2PTunnel) (err error) {
|
||||
gLog.Printf(LevelDEBUG, "handshakeC2C %s:%d:%d to %s:%d", t.pn.config.Node, t.coneLocalPort, t.coneNatPort, t.config.peerIP, t.config.peerConeNatPort)
|
||||
defer gLog.Printf(LevelDEBUG, "handshakeC2C ok")
|
||||
gLog.Printf(LvDEBUG, "handshakeC2C %s:%d:%d to %s:%d", t.pn.config.Node, t.coneLocalPort, t.coneNatPort, t.config.peerIP, t.config.peerConeNatPort)
|
||||
defer gLog.Printf(LvDEBUG, "handshakeC2C ok")
|
||||
conn, err := net.ListenUDP("udp", t.la)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -20,40 +20,40 @@ func handshakeC2C(t *P2PTunnel) (err error) {
|
||||
defer conn.Close()
|
||||
_, err = UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id})
|
||||
if err != nil {
|
||||
gLog.Println(LevelDEBUG, "handshakeC2C write MsgPunchHandshake error:", err)
|
||||
gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshake error:", err)
|
||||
return err
|
||||
}
|
||||
ra, head, _, _, err := UDPRead(conn, 5000)
|
||||
if err != nil {
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
gLog.Println(LevelDEBUG, err, ", return this error when ip was not reachable, retry read")
|
||||
gLog.Println(LvDEBUG, err, ", return this error when ip was not reachable, retry read")
|
||||
ra, head, _, _, err = UDPRead(conn, 5000)
|
||||
if err != nil {
|
||||
gLog.Println(LevelDEBUG, "handshakeC2C read MsgPunchHandshake error:", err)
|
||||
gLog.Println(LvDEBUG, "handshakeC2C read MsgPunchHandshake error:", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
t.ra, _ = net.ResolveUDPAddr("udp", ra.String())
|
||||
// cone server side
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake {
|
||||
gLog.Printf(LevelDEBUG, "read %d handshake ", t.id)
|
||||
gLog.Printf(LvDEBUG, "read %d handshake ", t.id)
|
||||
UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
_, head, _, _, err = UDPRead(conn, 5000)
|
||||
if err != nil {
|
||||
gLog.Println(LevelDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err)
|
||||
gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err)
|
||||
return err
|
||||
}
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
|
||||
gLog.Printf(LevelDEBUG, "read %d handshake ack ", t.id)
|
||||
gLog.Printf(LvDEBUG, "read %d handshake ack ", t.id)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// cone client side will only read handshake ack
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
|
||||
gLog.Printf(LevelDEBUG, "read %d handshake ack ", t.id)
|
||||
gLog.Printf(LvDEBUG, "read %d handshake ack ", t.id)
|
||||
_, err = UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
if err != nil {
|
||||
gLog.Println(LevelDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err)
|
||||
gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -61,8 +61,8 @@ func handshakeC2C(t *P2PTunnel) (err error) {
|
||||
}
|
||||
|
||||
func handshakeC2S(t *P2PTunnel) error {
|
||||
gLog.Printf(LevelDEBUG, "handshakeC2S start")
|
||||
defer gLog.Printf(LevelDEBUG, "handshakeC2S end")
|
||||
gLog.Printf(LvDEBUG, "handshakeC2S start")
|
||||
defer gLog.Printf(LvDEBUG, "handshakeC2S end")
|
||||
// even if read timeout, continue handshake
|
||||
t.pn.read(t.config.PeerNode, MsgPush, MsgPushHandshakeStart, SymmetricHandshakeAckTimeout)
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
@@ -73,7 +73,7 @@ func handshakeC2S(t *P2PTunnel) error {
|
||||
}
|
||||
defer conn.Close()
|
||||
go func() error {
|
||||
gLog.Printf(LevelDEBUG, "send symmetric handshake to %s from %d:%d start", t.config.peerIP, t.coneLocalPort, t.coneNatPort)
|
||||
gLog.Printf(LvDEBUG, "send symmetric handshake to %s from %d:%d start", t.config.peerIP, t.coneLocalPort, t.coneNatPort)
|
||||
for i := 0; i < SymmetricHandshakeNum; i++ {
|
||||
// TODO: auto calc cost time
|
||||
time.Sleep(SymmetricHandshakeInterval)
|
||||
@@ -83,35 +83,35 @@ func handshakeC2S(t *P2PTunnel) error {
|
||||
}
|
||||
_, err = UDPWrite(conn, dst, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id})
|
||||
if err != nil {
|
||||
gLog.Println(LevelDEBUG, "handshakeC2S write MsgPunchHandshake error:", err)
|
||||
gLog.Println(LvDEBUG, "handshakeC2S write MsgPunchHandshake error:", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
gLog.Println(LevelDEBUG, "send symmetric handshake end")
|
||||
gLog.Println(LvDEBUG, "send symmetric handshake end")
|
||||
return nil
|
||||
}()
|
||||
deadline := time.Now().Add(SymmetricHandshakeAckTimeout)
|
||||
err = conn.SetReadDeadline(deadline)
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "SymmetricHandshakeAckTimeout SetReadDeadline error")
|
||||
gLog.Println(LvERROR, "SymmetricHandshakeAckTimeout SetReadDeadline error")
|
||||
return err
|
||||
}
|
||||
// read response of the punching hole ok port
|
||||
result := make([]byte, 1024)
|
||||
_, dst, err := conn.ReadFrom(result)
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "handshakeC2S wait timeout")
|
||||
gLog.Println(LvERROR, "handshakeC2S wait timeout")
|
||||
return err
|
||||
}
|
||||
head := &openP2PHeader{}
|
||||
err = binary.Read(bytes.NewReader(result[:openP2PHeaderSize]), binary.LittleEndian, head)
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "parse p2pheader error:", err)
|
||||
gLog.Println(LvERROR, "parse p2pheader error:", err)
|
||||
return err
|
||||
}
|
||||
t.ra, _ = net.ResolveUDPAddr("udp", dst.String())
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
|
||||
gLog.Printf(LevelDEBUG, "handshakeC2S read %d handshake ack %s", t.id, dst.String())
|
||||
gLog.Printf(LvDEBUG, "handshakeC2S read %d handshake ack %s", t.id, dst.String())
|
||||
_, err = UDPWrite(conn, dst, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
return err
|
||||
}
|
||||
@@ -119,11 +119,11 @@ func handshakeC2S(t *P2PTunnel) error {
|
||||
}
|
||||
|
||||
func handshakeS2C(t *P2PTunnel) error {
|
||||
gLog.Printf(LevelDEBUG, "handshakeS2C start")
|
||||
defer gLog.Printf(LevelDEBUG, "handshakeS2C end")
|
||||
gLog.Printf(LvDEBUG, "handshakeS2C start")
|
||||
defer gLog.Printf(LvDEBUG, "handshakeS2C end")
|
||||
gotCh := make(chan *net.UDPAddr, 5)
|
||||
// sequencely udp send handshake, do not parallel send
|
||||
gLog.Printf(LevelDEBUG, "send symmetric handshake to %s:%d start", t.config.peerIP, t.config.peerConeNatPort)
|
||||
gLog.Printf(LvDEBUG, "send symmetric handshake to %s:%d start", t.config.peerIP, t.config.peerConeNatPort)
|
||||
gotIt := false
|
||||
gotMtx := sync.Mutex{}
|
||||
for i := 0; i < SymmetricHandshakeNum; i++ {
|
||||
@@ -132,7 +132,7 @@ func handshakeS2C(t *P2PTunnel) error {
|
||||
go func(t *P2PTunnel) error {
|
||||
conn, err := net.ListenUDP("udp", nil)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelDEBUG, "listen error")
|
||||
gLog.Printf(LvDEBUG, "listen error")
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
@@ -150,15 +150,15 @@ func handshakeS2C(t *P2PTunnel) error {
|
||||
gotIt = true
|
||||
t.la, _ = net.ResolveUDPAddr("udp", conn.LocalAddr().String())
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake {
|
||||
gLog.Printf(LevelDEBUG, "handshakeS2C read %d handshake ", t.id)
|
||||
gLog.Printf(LvDEBUG, "handshakeS2C read %d handshake ", t.id)
|
||||
UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
|
||||
_, head, _, _, err = UDPRead(conn, 5000)
|
||||
if err != nil {
|
||||
gLog.Println(LevelDEBUG, "handshakeS2C handshake error")
|
||||
gLog.Println(LvDEBUG, "handshakeS2C handshake error")
|
||||
return err
|
||||
}
|
||||
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck {
|
||||
gLog.Printf(LevelDEBUG, "handshakeS2C read %d handshake ack %s", t.id, conn.LocalAddr().String())
|
||||
gLog.Printf(LvDEBUG, "handshakeS2C read %d handshake ack %s", t.id, conn.LocalAddr().String())
|
||||
gotCh <- t.la
|
||||
return nil
|
||||
}
|
||||
@@ -166,15 +166,15 @@ func handshakeS2C(t *P2PTunnel) error {
|
||||
return nil
|
||||
}(t)
|
||||
}
|
||||
gLog.Printf(LevelDEBUG, "send symmetric handshake end")
|
||||
gLog.Println(LevelDEBUG, "handshakeS2C ready, notify peer connect")
|
||||
gLog.Printf(LvDEBUG, "send symmetric handshake end")
|
||||
gLog.Println(LvDEBUG, "handshakeS2C ready, notify peer connect")
|
||||
t.pn.push(t.config.PeerNode, MsgPushHandshakeStart, TunnelMsg{ID: t.id})
|
||||
|
||||
select {
|
||||
case <-time.After(SymmetricHandshakeAckTimeout):
|
||||
return fmt.Errorf("wait handshake failed")
|
||||
case la := <-gotCh:
|
||||
gLog.Println(LevelDEBUG, "symmetric handshake ok", la)
|
||||
gLog.Println(LvDEBUG, "symmetric handshake ok", la)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
127
install.go
Normal file
127
install.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// examples:
|
||||
// listen:
|
||||
// ./openp2p install -node hhd1207-222 -token YOUR-TOKEN -sharebandwidth 0
|
||||
// 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(LvINFO, "openp2p start. version: ", OpenP2PVersion)
|
||||
gLog.Println(LvINFO, "Contact: QQ Group: 16947733, Email: openp2p.cn@gmail.com")
|
||||
gLog.Println(LvINFO, "install start")
|
||||
defer gLog.Println(LvINFO, "install end")
|
||||
// auto uninstall
|
||||
err := os.MkdirAll(defaultInstallPath, 0775)
|
||||
|
||||
if err != nil {
|
||||
gLog.Printf(LvERROR, "MkdirAll %s error:%s", defaultInstallPath, err)
|
||||
return
|
||||
}
|
||||
err = os.Chdir(defaultInstallPath)
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "cd error:", err)
|
||||
return
|
||||
}
|
||||
|
||||
uninstall()
|
||||
// save config file
|
||||
parseParams("install")
|
||||
targetPath := filepath.Join(defaultInstallPath, defaultBinName)
|
||||
d := daemon{}
|
||||
// copy files
|
||||
|
||||
binPath, _ := os.Executable()
|
||||
src, errFiles := os.Open(binPath) // can not use args[0], on Windows call openp2p is ok(=openp2p.exe)
|
||||
if errFiles != nil {
|
||||
gLog.Printf(LvERROR, "os.OpenFile %s error:%s", os.Args[0], errFiles)
|
||||
return
|
||||
}
|
||||
|
||||
dst, errFiles := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0775)
|
||||
if errFiles != nil {
|
||||
gLog.Printf(LvERROR, "os.OpenFile %s error:%s", targetPath, errFiles)
|
||||
return
|
||||
}
|
||||
|
||||
_, errFiles = io.Copy(dst, src)
|
||||
if errFiles != nil {
|
||||
gLog.Printf(LvERROR, "io.Copy error:%s", errFiles)
|
||||
return
|
||||
}
|
||||
src.Close()
|
||||
dst.Close()
|
||||
|
||||
// install system service
|
||||
gLog.Println(LvINFO, "targetPath:", targetPath)
|
||||
err = d.Control("install", targetPath, []string{"-d"})
|
||||
if err == nil {
|
||||
gLog.Println(LvINFO, "install system service ok.")
|
||||
}
|
||||
time.Sleep(time.Second * 2)
|
||||
err = d.Control("start", targetPath, []string{"-d"})
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "start openp2p service error:", err)
|
||||
} else {
|
||||
gLog.Println(LvINFO, "start openp2p service ok.")
|
||||
}
|
||||
}
|
||||
|
||||
func installByFilename() {
|
||||
params := strings.Split(filepath.Base(os.Args[0]), "-")
|
||||
if len(params) < 4 {
|
||||
return
|
||||
}
|
||||
serverHost := params[1]
|
||||
token := params[2]
|
||||
gLog.Println(LvINFO, "install start")
|
||||
targetPath := os.Args[0]
|
||||
args := []string{"install"}
|
||||
args = append(args, "-serverhost")
|
||||
args = append(args, serverHost)
|
||||
args = append(args, "-token")
|
||||
args = append(args, token)
|
||||
env := os.Environ()
|
||||
cmd := exec.Command(targetPath, args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Env = env
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "install by filename, start process error:", err)
|
||||
return
|
||||
}
|
||||
gLog.Println(LvINFO, "install end")
|
||||
fmt.Println("Press the Any Key to exit")
|
||||
fmt.Scanln()
|
||||
os.Exit(0)
|
||||
}
|
||||
func uninstall() {
|
||||
gLog.Println(LvINFO, "uninstall start")
|
||||
defer gLog.Println(LvINFO, "uninstall end")
|
||||
d := daemon{}
|
||||
err := d.Control("stop", "", nil)
|
||||
if err != nil { // service maybe not install
|
||||
return
|
||||
}
|
||||
err = d.Control("uninstall", "", nil)
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "uninstall system service error:", err)
|
||||
} else {
|
||||
gLog.Println(LvINFO, "uninstall system service ok.")
|
||||
}
|
||||
binPath := filepath.Join(defaultInstallPath, defaultBinName)
|
||||
os.Remove(binPath + "0")
|
||||
os.Remove(binPath)
|
||||
// os.RemoveAll(defaultInstallPath) // reserve config.json
|
||||
}
|
||||
129
log.go
129
log.go
@@ -8,17 +8,15 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// LogLevel ...
|
||||
type LogLevel int
|
||||
|
||||
var gLog *V8log
|
||||
var gLog *logger
|
||||
|
||||
// LevelDEBUG ...
|
||||
const (
|
||||
LevelDEBUG LogLevel = iota
|
||||
LevelINFO
|
||||
LevelWARN
|
||||
LevelERROR
|
||||
LvDEBUG LogLevel = iota
|
||||
LvINFO
|
||||
LvWARN
|
||||
LvERROR
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -30,10 +28,10 @@ func init() {
|
||||
logFileNames = make(map[LogLevel]string)
|
||||
loglevel = make(map[LogLevel]string)
|
||||
logFileNames[0] = ".log"
|
||||
loglevel[LevelDEBUG] = "DEBUG"
|
||||
loglevel[LevelINFO] = "INFO"
|
||||
loglevel[LevelWARN] = "WARN"
|
||||
loglevel[LevelERROR] = "ERROR"
|
||||
loglevel[LvDEBUG] = "DEBUG"
|
||||
loglevel[LvINFO] = "INFO"
|
||||
loglevel[LvWARN] = "WARN"
|
||||
loglevel[LvERROR] = "ERROR"
|
||||
|
||||
}
|
||||
|
||||
@@ -43,25 +41,21 @@ const (
|
||||
LogFileAndConsole
|
||||
)
|
||||
|
||||
// V8log ...
|
||||
type V8log struct {
|
||||
type logger struct {
|
||||
loggers map[LogLevel]*log.Logger
|
||||
files map[LogLevel]*os.File
|
||||
level LogLevel
|
||||
stopSig chan bool
|
||||
logDir string
|
||||
mtx *sync.Mutex
|
||||
stoped bool
|
||||
lineEnding string
|
||||
pid int
|
||||
maxLogSize int64
|
||||
mode int
|
||||
}
|
||||
|
||||
// InitLogger ...
|
||||
func InitLogger(path string, filePrefix string, level LogLevel, maxLogSize int64, mode int) *V8log {
|
||||
logger := make(map[LogLevel]*log.Logger)
|
||||
openedfile := make(map[LogLevel]*os.File)
|
||||
func NewLogger(path string, filePrefix string, level LogLevel, maxLogSize int64, mode int) *logger {
|
||||
loggers := make(map[LogLevel]*log.Logger)
|
||||
logfiles := make(map[LogLevel]*os.File)
|
||||
var (
|
||||
logdir string
|
||||
)
|
||||
@@ -71,15 +65,15 @@ func InitLogger(path string, filePrefix string, level LogLevel, maxLogSize int64
|
||||
logdir = path + "/log/"
|
||||
}
|
||||
os.MkdirAll(logdir, 0777)
|
||||
for l := range logFileNames {
|
||||
logFilePath := logdir + filePrefix + logFileNames[l]
|
||||
for lv := range logFileNames {
|
||||
logFilePath := logdir + filePrefix + logFileNames[lv]
|
||||
f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
os.Chmod(logFilePath, 0666)
|
||||
openedfile[l] = f
|
||||
logger[l] = log.New(f, "", log.LstdFlags)
|
||||
logfiles[lv] = f
|
||||
loggers[lv] = log.New(f, "", log.LstdFlags)
|
||||
}
|
||||
var le string
|
||||
if runtime.GOOS == "windows" {
|
||||
@@ -87,97 +81,84 @@ func InitLogger(path string, filePrefix string, level LogLevel, maxLogSize int64
|
||||
} else {
|
||||
le = "\n"
|
||||
}
|
||||
pLog := &V8log{logger, openedfile, level, make(chan bool, 10), logdir, &sync.Mutex{}, false, le, os.Getpid(), maxLogSize, mode}
|
||||
pLog := &logger{loggers, logfiles, level, logdir, &sync.Mutex{}, le, os.Getpid(), maxLogSize, mode}
|
||||
go pLog.checkFile()
|
||||
return pLog
|
||||
}
|
||||
|
||||
func (vl *V8log) setLevel(level LogLevel) {
|
||||
vl.mtx.Lock()
|
||||
defer vl.mtx.Unlock()
|
||||
vl.level = level
|
||||
func (l *logger) setLevel(level LogLevel) {
|
||||
l.mtx.Lock()
|
||||
defer l.mtx.Unlock()
|
||||
l.level = level
|
||||
}
|
||||
func (vl *V8log) setMode(mode int) {
|
||||
vl.mtx.Lock()
|
||||
defer vl.mtx.Unlock()
|
||||
vl.mode = mode
|
||||
func (l *logger) setMode(mode int) {
|
||||
l.mtx.Lock()
|
||||
defer l.mtx.Unlock()
|
||||
l.mode = mode
|
||||
}
|
||||
|
||||
func (vl *V8log) checkFile() {
|
||||
if vl.maxLogSize <= 0 {
|
||||
func (l *logger) checkFile() {
|
||||
if l.maxLogSize <= 0 {
|
||||
return
|
||||
}
|
||||
ticker := time.NewTicker(time.Minute)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
vl.mtx.Lock()
|
||||
for l, logFile := range vl.files {
|
||||
l.mtx.Lock()
|
||||
for lv, logFile := range l.files {
|
||||
f, e := logFile.Stat()
|
||||
if e != nil {
|
||||
continue
|
||||
}
|
||||
if f.Size() <= vl.maxLogSize {
|
||||
if f.Size() <= l.maxLogSize {
|
||||
continue
|
||||
}
|
||||
logFile.Close()
|
||||
fname := f.Name()
|
||||
backupPath := vl.logDir + fname + ".0"
|
||||
backupPath := l.logDir + fname + ".0"
|
||||
os.Remove(backupPath)
|
||||
os.Rename(vl.logDir+fname, backupPath)
|
||||
newFile, e := os.OpenFile(vl.logDir+fname, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
os.Rename(l.logDir+fname, backupPath)
|
||||
newFile, e := os.OpenFile(l.logDir+fname, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
if e == nil {
|
||||
vl.loggers[l].SetOutput(newFile)
|
||||
vl.files[l] = newFile
|
||||
l.loggers[lv].SetOutput(newFile)
|
||||
l.files[lv] = newFile
|
||||
}
|
||||
|
||||
}
|
||||
vl.mtx.Unlock()
|
||||
case <-vl.stopSig:
|
||||
}
|
||||
if vl.stoped {
|
||||
break
|
||||
l.mtx.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Printf Warning: report error log depends on this Print format.
|
||||
func (vl *V8log) Printf(level LogLevel, format string, params ...interface{}) {
|
||||
vl.mtx.Lock()
|
||||
defer vl.mtx.Unlock()
|
||||
if vl.stoped {
|
||||
func (l *logger) Printf(level LogLevel, format string, params ...interface{}) {
|
||||
l.mtx.Lock()
|
||||
defer l.mtx.Unlock()
|
||||
if level < l.level {
|
||||
return
|
||||
}
|
||||
if level < vl.level {
|
||||
return
|
||||
}
|
||||
pidAndLevel := []interface{}{vl.pid, loglevel[level]}
|
||||
pidAndLevel := []interface{}{l.pid, loglevel[level]}
|
||||
params = append(pidAndLevel, params...)
|
||||
if vl.mode == LogFile || vl.mode == LogFileAndConsole {
|
||||
vl.loggers[0].Printf("%d %s "+format+vl.lineEnding, params...)
|
||||
if l.mode == LogFile || l.mode == LogFileAndConsole {
|
||||
l.loggers[0].Printf("%d %s "+format+l.lineEnding, params...)
|
||||
}
|
||||
if vl.mode == LogConsole || vl.mode == LogFileAndConsole {
|
||||
log.Printf("%d %s "+format+vl.lineEnding, params...)
|
||||
if l.mode == LogConsole || l.mode == LogFileAndConsole {
|
||||
log.Printf("%d %s "+format+l.lineEnding, params...)
|
||||
}
|
||||
}
|
||||
|
||||
// Println ...
|
||||
func (vl *V8log) Println(level LogLevel, params ...interface{}) {
|
||||
vl.mtx.Lock()
|
||||
defer vl.mtx.Unlock()
|
||||
if vl.stoped {
|
||||
func (l *logger) Println(level LogLevel, params ...interface{}) {
|
||||
l.mtx.Lock()
|
||||
defer l.mtx.Unlock()
|
||||
if level < l.level {
|
||||
return
|
||||
}
|
||||
if level < vl.level {
|
||||
return
|
||||
}
|
||||
pidAndLevel := []interface{}{vl.pid, " ", loglevel[level], " "}
|
||||
pidAndLevel := []interface{}{l.pid, " ", loglevel[level], " "}
|
||||
params = append(pidAndLevel, params...)
|
||||
params = append(params, vl.lineEnding)
|
||||
if vl.mode == LogFile || vl.mode == LogFileAndConsole {
|
||||
vl.loggers[0].Print(params...)
|
||||
params = append(params, l.lineEnding)
|
||||
if l.mode == LogFile || l.mode == LogFileAndConsole {
|
||||
l.loggers[0].Print(params...)
|
||||
}
|
||||
if vl.mode == LogConsole || vl.mode == LogFileAndConsole {
|
||||
if l.mode == LogConsole || l.mode == LogFileAndConsole {
|
||||
log.Print(params...)
|
||||
}
|
||||
}
|
||||
|
||||
83
nat.go
83
nat.go
@@ -3,47 +3,69 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func natTest(serverHost string, serverPort int, localPort int, echoPort int) (publicIP string, isPublicIP int, publicPort int, err error) {
|
||||
func natTest(serverHost string, serverPort int, localPort int, echoPort int) (publicIP string, hasPublicIP int, hasUPNPorNATPMP int, publicPort int, err error) {
|
||||
conn, err := net.ListenPacket("udp", fmt.Sprintf(":%d", localPort))
|
||||
if err != nil {
|
||||
return "", 0, 0, err
|
||||
return "", 0, 0, 0, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
dst, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", serverHost, serverPort))
|
||||
if err != nil {
|
||||
return "", 0, 0, err
|
||||
return "", 0, 0, 0, err
|
||||
}
|
||||
|
||||
// The connection can write data to the desired address.
|
||||
msg, err := newMessage(MsgNATDetect, 0, &NatDetectReq{SrcPort: localPort, EchoPort: echoPort})
|
||||
_, err = conn.WriteTo(msg, dst)
|
||||
if err != nil {
|
||||
return "", 0, 0, err
|
||||
return "", 0, 0, 0, err
|
||||
}
|
||||
deadline := time.Now().Add(NatTestTimeout)
|
||||
err = conn.SetReadDeadline(deadline)
|
||||
if err != nil {
|
||||
return "", 0, 0, err
|
||||
return "", 0, 0, 0, err
|
||||
}
|
||||
buffer := make([]byte, 1024)
|
||||
nRead, _, err := conn.ReadFrom(buffer)
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "NAT detect error:", err)
|
||||
return "", 0, 0, err
|
||||
gLog.Println(LvERROR, "NAT detect error:", err)
|
||||
return "", 0, 0, 0, err
|
||||
}
|
||||
natRsp := NatDetectRsp{}
|
||||
err = json.Unmarshal(buffer[openP2PHeaderSize:nRead], &natRsp)
|
||||
|
||||
hasPublicIP = 0
|
||||
hasUPNPorNATPMP = 0
|
||||
// testing for public ip
|
||||
if echoPort != 0 {
|
||||
for {
|
||||
gLog.Printf(LevelDEBUG, "public ip test start %s:%d", natRsp.IP, echoPort)
|
||||
for i := 0; i < 2; i++ {
|
||||
if i == 1 {
|
||||
// test upnp or nat-pmp
|
||||
nat, err := Discover()
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "could not perform UPNP discover:", err)
|
||||
break
|
||||
}
|
||||
ext, err := nat.GetExternalAddress()
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "could not perform UPNP external address:", err)
|
||||
break
|
||||
}
|
||||
log.Println("PublicIP:", ext)
|
||||
|
||||
externalPort, err := nat.AddPortMapping("udp", echoPort, echoPort, "openp2p", 60)
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "could not add udp UPNP port mapping", externalPort)
|
||||
break
|
||||
}
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "public ip test start %s:%d", natRsp.IP, echoPort)
|
||||
conn, err := net.ListenUDP("udp", nil)
|
||||
if err != nil {
|
||||
break
|
||||
@@ -60,56 +82,59 @@ func natTest(serverHost string, serverPort int, localPort int, echoPort int) (pu
|
||||
conn.SetReadDeadline(time.Now().Add(PublicIPEchoTimeout))
|
||||
_, _, err = conn.ReadFromUDP(buf)
|
||||
if err == nil {
|
||||
gLog.Println(LevelDEBUG, "public ip:YES")
|
||||
natRsp.IsPublicIP = 1
|
||||
} else {
|
||||
gLog.Println(LevelDEBUG, "public ip:NO")
|
||||
if i == 1 {
|
||||
gLog.Println(LvDEBUG, "UPNP or NAT-PMP:YES")
|
||||
hasUPNPorNATPMP = 1
|
||||
} else {
|
||||
gLog.Println(LvDEBUG, "public ip:YES")
|
||||
hasPublicIP = 1
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return natRsp.IP, natRsp.IsPublicIP, natRsp.Port, nil
|
||||
return natRsp.IP, hasPublicIP, hasUPNPorNATPMP, natRsp.Port, nil
|
||||
}
|
||||
|
||||
func getNATType(host string, udp1 int, udp2 int) (publicIP string, NATType int, err error) {
|
||||
func getNATType(host string, udp1 int, udp2 int) (publicIP string, NATType int, hasUPNPorNATPMP int, err error) {
|
||||
// the random local port may be used by other.
|
||||
localPort := int(rand.Uint32()%10000 + 50000)
|
||||
echoPort := int(rand.Uint32()%10000 + 50000)
|
||||
go echo(echoPort)
|
||||
ip1, isPublicIP, port1, err := natTest(host, udp1, localPort, echoPort)
|
||||
gLog.Printf(LevelDEBUG, "local port:%d nat port:%d", localPort, port1)
|
||||
ip1, hasPublicIP, hasUPNPorNATPMP, port1, err := natTest(host, udp1, localPort, echoPort)
|
||||
gLog.Printf(LvDEBUG, "local port:%d nat port:%d", localPort, port1)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
return "", 0, hasUPNPorNATPMP, err
|
||||
}
|
||||
if isPublicIP == 1 {
|
||||
return ip1, NATNone, nil
|
||||
if hasPublicIP == 1 || hasUPNPorNATPMP == 1 {
|
||||
return ip1, NATNone, hasUPNPorNATPMP, nil
|
||||
}
|
||||
ip2, _, port2, err := natTest(host, udp2, localPort, 0) // 2rd nat test not need testing publicip
|
||||
gLog.Printf(LevelDEBUG, "local port:%d nat port:%d", localPort, port2)
|
||||
ip2, _, _, port2, err := natTest(host, udp2, localPort, 0) // 2rd nat test not need testing publicip
|
||||
gLog.Printf(LvDEBUG, "local port:%d nat port:%d", localPort, port2)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
return "", 0, hasUPNPorNATPMP, err
|
||||
}
|
||||
if ip1 != ip2 {
|
||||
return "", 0, fmt.Errorf("ip have changed, please retry again")
|
||||
return "", 0, hasUPNPorNATPMP, fmt.Errorf("ip have changed, please retry again")
|
||||
}
|
||||
natType := NATSymmetric
|
||||
if port1 == port2 {
|
||||
natType = NATCone
|
||||
}
|
||||
//TODO: NATNone
|
||||
return ip1, natType, nil
|
||||
return ip1, natType, hasUPNPorNATPMP, nil
|
||||
}
|
||||
|
||||
func echo(echoPort int) {
|
||||
conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: echoPort})
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "echo server listen error:", err)
|
||||
gLog.Println(LvERROR, "echo server listen error:", err)
|
||||
return
|
||||
}
|
||||
buf := make([]byte, 1600)
|
||||
defer conn.Close()
|
||||
// wait 5s for echo testing
|
||||
conn.SetReadDeadline(time.Now().Add(time.Second * 5))
|
||||
conn.SetReadDeadline(time.Now().Add(time.Second * 30))
|
||||
n, addr, err := conn.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
20
openp2p.go
20
openp2p.go
@@ -12,7 +12,7 @@ func main() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
binDir := filepath.Dir(os.Args[0])
|
||||
os.Chdir(binDir) // for system service
|
||||
gLog = InitLogger(binDir, "openp2p", LevelDEBUG, 1024*1024, LogFileAndConsole)
|
||||
gLog = NewLogger(binDir, "openp2p", LvDEBUG, 1024*1024, LogFileAndConsole)
|
||||
// TODO: install sub command, deamon process
|
||||
if len(os.Args) > 1 {
|
||||
switch os.Args[1] {
|
||||
@@ -20,14 +20,14 @@ func main() {
|
||||
fmt.Println(OpenP2PVersion)
|
||||
return
|
||||
case "update":
|
||||
gLog = InitLogger(filepath.Dir(os.Args[0]), "openp2p", LevelDEBUG, 1024*1024, LogFileAndConsole)
|
||||
gLog = NewLogger(filepath.Dir(os.Args[0]), "openp2p", LvDEBUG, 1024*1024, LogFileAndConsole)
|
||||
targetPath := filepath.Join(defaultInstallPath, defaultBinName)
|
||||
d := daemon{}
|
||||
err := d.Control("restart", targetPath, nil)
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "restart service error:", err)
|
||||
gLog.Println(LvERROR, "restart service error:", err)
|
||||
} else {
|
||||
gLog.Println(LevelINFO, "restart service ok.")
|
||||
gLog.Println(LvINFO, "restart service ok.")
|
||||
}
|
||||
return
|
||||
case "install":
|
||||
@@ -40,9 +40,9 @@ func main() {
|
||||
} else {
|
||||
installByFilename()
|
||||
}
|
||||
parseParams()
|
||||
gLog.Println(LevelINFO, "openp2p start. version: ", OpenP2PVersion)
|
||||
gLog.Println(LevelINFO, "Contact: QQ Group: 16947733, Email: openp2p.cn@gmail.com")
|
||||
parseParams("")
|
||||
gLog.Println(LvINFO, "openp2p start. version: ", OpenP2PVersion)
|
||||
gLog.Println(LvINFO, "Contact: QQ Group: 16947733, Email: openp2p.cn@gmail.com")
|
||||
|
||||
if gConf.daemonMode {
|
||||
d := daemon{}
|
||||
@@ -50,14 +50,14 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
gLog.Println(LevelINFO, &gConf)
|
||||
gLog.Println(LvINFO, &gConf)
|
||||
setFirewall()
|
||||
network := P2PNetworkInstance(&gConf.Network)
|
||||
if ok := network.Connect(30000); !ok {
|
||||
gLog.Println(LevelERROR, "P2PNetwork login error")
|
||||
gLog.Println(LvERROR, "P2PNetwork login error")
|
||||
return
|
||||
}
|
||||
gLog.Println(LevelINFO, "waiting for connection...")
|
||||
gLog.Println(LvINFO, "waiting for connection...")
|
||||
forever := make(chan bool)
|
||||
<-forever
|
||||
}
|
||||
|
||||
10
overlay.go
10
overlay.go
@@ -40,8 +40,8 @@ type overlayConn struct {
|
||||
}
|
||||
|
||||
func (oConn *overlayConn) run() {
|
||||
gLog.Printf(LevelDEBUG, "%d overlayConn run start", oConn.id)
|
||||
defer gLog.Printf(LevelDEBUG, "%d overlayConn run end", oConn.id)
|
||||
gLog.Printf(LvDEBUG, "%d overlayConn run start", oConn.id)
|
||||
defer gLog.Printf(LvDEBUG, "%d overlayConn run end", oConn.id)
|
||||
oConn.running = true
|
||||
oConn.lastReadUDPTs = time.Now()
|
||||
buffer := make([]byte, ReadBuffLen+PaddingSize)
|
||||
@@ -58,7 +58,7 @@ func (oConn *overlayConn) run() {
|
||||
continue
|
||||
}
|
||||
// overlay tcp connection normal close, debug log
|
||||
gLog.Printf(LevelDEBUG, "overlayConn %d read error:%s,close it", oConn.id, err)
|
||||
gLog.Printf(LvDEBUG, "overlayConn %d read error:%s,close it", oConn.id, err)
|
||||
break
|
||||
}
|
||||
payload := buff[:dataLen]
|
||||
@@ -68,13 +68,13 @@ func (oConn *overlayConn) run() {
|
||||
writeBytes := append(tunnelHead.Bytes(), payload...)
|
||||
if oConn.rtid == 0 {
|
||||
oConn.tunnel.conn.WriteBytes(MsgP2P, MsgOverlayData, writeBytes)
|
||||
gLog.Printf(LevelDEBUG, "write overlay data to %d:%d bodylen=%d", oConn.rtid, oConn.id, len(writeBytes))
|
||||
gLog.Printf(LvDEBUG, "write overlay data to %d:%d bodylen=%d", oConn.rtid, oConn.id, len(writeBytes))
|
||||
} else {
|
||||
// write raley data
|
||||
all := append(relayHead.Bytes(), encodeHeader(MsgP2P, MsgOverlayData, uint32(len(writeBytes)))...)
|
||||
all = append(all, writeBytes...)
|
||||
oConn.tunnel.conn.WriteBytes(MsgP2P, MsgRelayData, all)
|
||||
gLog.Printf(LevelDEBUG, "write relay data to %d:%d bodylen=%d", oConn.rtid, oConn.id, len(writeBytes))
|
||||
gLog.Printf(LvDEBUG, "write relay data to %d:%d bodylen=%d", oConn.rtid, oConn.id, len(writeBytes))
|
||||
}
|
||||
}
|
||||
if oConn.connTCP != nil {
|
||||
|
||||
30
p2papp.go
30
p2papp.go
@@ -17,7 +17,7 @@ type p2pApp struct {
|
||||
listener net.Listener
|
||||
listenerUDP *net.UDPConn
|
||||
tunnel *P2PTunnel
|
||||
rtid uint64
|
||||
rtid uint64 // relay tunnelID
|
||||
relayNode string
|
||||
relayMode string
|
||||
hbTime time.Time
|
||||
@@ -48,19 +48,19 @@ func (app *p2pApp) updateHeartbeat() {
|
||||
}
|
||||
|
||||
func (app *p2pApp) listenTCP() error {
|
||||
gLog.Printf(LevelDEBUG, "tcp accept on port %d start", app.config.SrcPort)
|
||||
defer gLog.Printf(LevelDEBUG, "tcp accept on port %d end", app.config.SrcPort)
|
||||
gLog.Printf(LvDEBUG, "tcp accept on port %d start", app.config.SrcPort)
|
||||
defer gLog.Printf(LvDEBUG, "tcp accept on port %d end", app.config.SrcPort)
|
||||
var err error
|
||||
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)
|
||||
gLog.Printf(LvERROR, "listen error:%s", err)
|
||||
return err
|
||||
}
|
||||
for app.running {
|
||||
conn, err := app.listener.Accept()
|
||||
if err != nil {
|
||||
if app.running {
|
||||
gLog.Printf(LevelERROR, "%d accept error:%s", app.tunnel.id, err)
|
||||
gLog.Printf(LvERROR, "%d accept error:%s", app.id, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -81,7 +81,7 @@ func (app *p2pApp) listenTCP() error {
|
||||
oConn.appKeyBytes = encryptKey
|
||||
}
|
||||
app.tunnel.overlayConns.Store(oConn.id, &oConn)
|
||||
gLog.Printf(LevelDEBUG, "Accept TCP overlayID:%d", oConn.id)
|
||||
gLog.Printf(LvDEBUG, "Accept TCP overlayID:%d", oConn.id)
|
||||
// tell peer connect
|
||||
req := OverlayConnectReq{ID: oConn.id,
|
||||
Token: app.tunnel.pn.config.Token,
|
||||
@@ -106,12 +106,12 @@ func (app *p2pApp) listenTCP() error {
|
||||
}
|
||||
|
||||
func (app *p2pApp) listenUDP() error {
|
||||
gLog.Printf(LevelDEBUG, "udp accept on port %d start", app.config.SrcPort)
|
||||
defer gLog.Printf(LevelDEBUG, "udp accept on port %d end", app.config.SrcPort)
|
||||
gLog.Printf(LvDEBUG, "udp accept on port %d start", app.config.SrcPort)
|
||||
defer gLog.Printf(LvDEBUG, "udp accept on port %d end", app.config.SrcPort)
|
||||
var err error
|
||||
app.listenerUDP, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: app.config.SrcPort})
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "listen error:%s", err)
|
||||
gLog.Printf(LvERROR, "listen error:%s", err)
|
||||
return err
|
||||
}
|
||||
buffer := make([]byte, 64*1024)
|
||||
@@ -123,7 +123,7 @@ func (app *p2pApp) listenUDP() error {
|
||||
if ne, ok := err.(net.Error); ok && ne.Timeout() {
|
||||
continue
|
||||
} else {
|
||||
gLog.Printf(LevelERROR, "udp read failed:%s", err)
|
||||
gLog.Printf(LvERROR, "udp read failed:%s", err)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
@@ -161,7 +161,7 @@ func (app *p2pApp) listenUDP() error {
|
||||
oConn.appKeyBytes = encryptKey
|
||||
}
|
||||
app.tunnel.overlayConns.Store(oConn.id, &oConn)
|
||||
gLog.Printf(LevelDEBUG, "Accept UDP overlayID:%d", oConn.id)
|
||||
gLog.Printf(LvDEBUG, "Accept UDP overlayID:%d", oConn.id)
|
||||
// tell peer connect
|
||||
req := OverlayConnectReq{ID: oConn.id,
|
||||
Token: app.tunnel.pn.config.Token,
|
||||
@@ -196,8 +196,8 @@ func (app *p2pApp) listenUDP() error {
|
||||
}
|
||||
|
||||
func (app *p2pApp) listen() error {
|
||||
gLog.Printf(LevelINFO, "LISTEN ON PORT %s:%d START", app.config.Protocol, app.config.SrcPort)
|
||||
defer gLog.Printf(LevelINFO, "LISTEN ON PORT %s:%d END", app.config.Protocol, app.config.SrcPort)
|
||||
gLog.Printf(LvINFO, "LISTEN ON PORT %s:%d START", app.config.Protocol, app.config.SrcPort)
|
||||
defer gLog.Printf(LvINFO, "LISTEN ON PORT %s:%d END", app.config.Protocol, app.config.SrcPort)
|
||||
app.wg.Add(1)
|
||||
defer app.wg.Done()
|
||||
app.running = true
|
||||
@@ -233,8 +233,8 @@ func (app *p2pApp) close() {
|
||||
func (app *p2pApp) relayHeartbeatLoop() {
|
||||
app.wg.Add(1)
|
||||
defer app.wg.Done()
|
||||
gLog.Printf(LevelDEBUG, "relayHeartbeat to %d start", app.rtid)
|
||||
defer gLog.Printf(LevelDEBUG, "relayHeartbeat to %d end", app.rtid)
|
||||
gLog.Printf(LvDEBUG, "relayHeartbeat to %d start", app.rtid)
|
||||
defer gLog.Printf(LvDEBUG, "relayHeartbeat to %d end", app.rtid)
|
||||
relayHead := new(bytes.Buffer)
|
||||
binary.Write(relayHead, binary.LittleEndian, app.rtid)
|
||||
req := RelayHeartbeat{RelayTunnelID: app.tunnel.id,
|
||||
|
||||
139
p2pnetwork.go
139
p2pnetwork.go
@@ -9,7 +9,6 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -76,7 +75,7 @@ func (pn *P2PNetwork) run() {
|
||||
time.Sleep(NetworkHeartbeatTime)
|
||||
err := pn.init()
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "P2PNetwork init error:", err)
|
||||
gLog.Println(LvERROR, "P2PNetwork init error:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,7 +122,7 @@ func (pn *P2PNetwork) runAll() {
|
||||
pn.DeleteApp(*config)
|
||||
}
|
||||
if config.retryNum > 0 {
|
||||
gLog.Printf(LevelINFO, "detect app %s(%d) disconnect, reconnecting the %d times...", config.AppName, appID, config.retryNum)
|
||||
gLog.Printf(LvINFO, "detect app %s(%d) disconnect, reconnecting the %d times...", config.AppName, appID, config.retryNum)
|
||||
if time.Now().Add(-time.Minute * 15).After(config.retryTime) { // normal lasts 15min
|
||||
config.retryNum = 0
|
||||
}
|
||||
@@ -145,7 +144,7 @@ func (pn *P2PNetwork) runAll() {
|
||||
}
|
||||
}
|
||||
func (pn *P2PNetwork) autorunApp() {
|
||||
gLog.Println(LevelINFO, "autorunApp start")
|
||||
gLog.Println(LvINFO, "autorunApp start")
|
||||
for pn.running {
|
||||
time.Sleep(time.Second)
|
||||
if !pn.online {
|
||||
@@ -154,12 +153,12 @@ func (pn *P2PNetwork) autorunApp() {
|
||||
pn.runAll()
|
||||
time.Sleep(time.Second * 10)
|
||||
}
|
||||
gLog.Println(LevelINFO, "autorunApp end")
|
||||
gLog.Println(LvINFO, "autorunApp end")
|
||||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
pn.write(MsgRelay, MsgRelayNodeReq, &RelayNodeReq{config.PeerNode})
|
||||
head, body := pn.read("", MsgRelay, MsgRelayNodeRsp, time.Second*10)
|
||||
if head == nil {
|
||||
@@ -168,20 +167,20 @@ func (pn *P2PNetwork) addRelayTunnel(config AppConfig, appid uint64, appkey uint
|
||||
rsp := RelayNodeRsp{}
|
||||
err := json.Unmarshal(body, &rsp)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "wrong RelayNodeRsp:%s", err)
|
||||
gLog.Printf(LvERROR, "wrong RelayNodeRsp:%s", err)
|
||||
return nil, 0, "", errors.New("unmarshal MsgRelayNodeRsp error")
|
||||
}
|
||||
if rsp.RelayName == "" || rsp.RelayToken == 0 {
|
||||
gLog.Printf(LevelERROR, "MsgRelayNodeReq error")
|
||||
gLog.Printf(LvERROR, "MsgRelayNodeReq error")
|
||||
return nil, 0, "", errors.New("MsgRelayNodeReq error")
|
||||
}
|
||||
gLog.Printf(LevelINFO, "got relay node:%s", rsp.RelayName)
|
||||
gLog.Printf(LvINFO, "got relay node:%s", rsp.RelayName)
|
||||
relayConfig := config
|
||||
relayConfig.PeerNode = rsp.RelayName
|
||||
relayConfig.peerToken = rsp.RelayToken
|
||||
t, err := pn.addDirectTunnel(relayConfig, 0)
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "direct connect error:", err)
|
||||
gLog.Println(LvERROR, "direct connect error:", err)
|
||||
return nil, 0, "", err
|
||||
}
|
||||
// notify peer addRelayTunnel
|
||||
@@ -189,22 +188,20 @@ func (pn *P2PNetwork) addRelayTunnel(config AppConfig, appid uint64, appkey uint
|
||||
From: pn.config.Node,
|
||||
RelayName: rsp.RelayName,
|
||||
RelayToken: rsp.RelayToken,
|
||||
AppID: appid,
|
||||
AppKey: appkey,
|
||||
}
|
||||
gLog.Printf(LevelINFO, "push relay %s---------%s", config.PeerNode, rsp.RelayName)
|
||||
gLog.Printf(LvINFO, "push relay %s---------%s", config.PeerNode, rsp.RelayName)
|
||||
pn.push(config.PeerNode, MsgPushAddRelayTunnelReq, &req)
|
||||
|
||||
// wait relay ready
|
||||
head, body = pn.read(config.PeerNode, MsgPush, MsgPushAddRelayTunnelRsp, PeerAddRelayTimeount) // TODO: const value
|
||||
if head == nil {
|
||||
gLog.Printf(LevelERROR, "read MsgPushAddRelayTunnelRsp error")
|
||||
gLog.Printf(LvERROR, "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)
|
||||
gLog.Printf(LvERROR, "wrong RelayNodeRsp:%s", err)
|
||||
return nil, 0, "", errors.New("unmarshal MsgRelayNodeRsp error")
|
||||
}
|
||||
return t, rspID.ID, rsp.Mode, err
|
||||
@@ -212,8 +209,8 @@ func (pn *P2PNetwork) addRelayTunnel(config AppConfig, appid uint64, appkey uint
|
||||
|
||||
// use *AppConfig to save status
|
||||
func (pn *P2PNetwork) AddApp(config AppConfig) error {
|
||||
gLog.Printf(LevelINFO, "addApp %s to %s:%s:%d start", config.AppName, config.PeerNode, config.DstHost, config.DstPort)
|
||||
defer gLog.Printf(LevelINFO, "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.PeerNode, config.DstHost, config.DstPort)
|
||||
defer gLog.Printf(LvINFO, "addApp %s to %s:%s:%d end", config.AppName, config.PeerNode, config.DstHost, config.DstPort)
|
||||
if !pn.online {
|
||||
return errors.New("P2PNetwork offline")
|
||||
}
|
||||
@@ -240,9 +237,8 @@ func (pn *P2PNetwork) AddApp(config AppConfig) error {
|
||||
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)
|
||||
gLog.Println(LvERROR, "direct connect failed, try to relay")
|
||||
t, rtid, relayMode, err = pn.addRelayTunnel(config)
|
||||
if t != nil {
|
||||
relayNode = t.config.PeerNode
|
||||
}
|
||||
@@ -269,6 +265,16 @@ func (pn *P2PNetwork) AddApp(config AppConfig) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rtid != 0 || t.conn.Protocol() == "tcp" {
|
||||
// sync appkey
|
||||
appKey = rand.Uint64()
|
||||
req := APPKeySync{
|
||||
AppID: appID,
|
||||
AppKey: appKey,
|
||||
}
|
||||
gLog.Printf(LvINFO, "sync appkey to %s", config.PeerNode)
|
||||
pn.push(config.PeerNode, MsgPushAPPKey, &req)
|
||||
}
|
||||
app := p2pApp{
|
||||
id: appID,
|
||||
key: appKey,
|
||||
@@ -286,21 +292,21 @@ func (pn *P2PNetwork) AddApp(config AppConfig) error {
|
||||
}
|
||||
|
||||
func (pn *P2PNetwork) DeleteApp(config AppConfig) {
|
||||
gLog.Printf(LevelINFO, "DeleteApp %s%d start", config.Protocol, config.SrcPort)
|
||||
defer gLog.Printf(LevelINFO, "DeleteApp %s%d end", config.Protocol, config.SrcPort)
|
||||
gLog.Printf(LvINFO, "DeleteApp %s%d start", config.Protocol, config.SrcPort)
|
||||
defer gLog.Printf(LvINFO, "DeleteApp %s%d end", config.Protocol, config.SrcPort)
|
||||
// close the apps of this config
|
||||
i, ok := pn.apps.Load(fmt.Sprintf("%s%d", config.Protocol, config.SrcPort))
|
||||
if ok {
|
||||
app := i.(*p2pApp)
|
||||
gLog.Printf(LevelINFO, "app %s exist, delete it", fmt.Sprintf("%s%d", config.Protocol, config.SrcPort))
|
||||
gLog.Printf(LvINFO, "app %s exist, delete it", fmt.Sprintf("%s%d", config.Protocol, config.SrcPort))
|
||||
app.close()
|
||||
pn.apps.Delete(fmt.Sprintf("%s%d", config.Protocol, config.SrcPort))
|
||||
}
|
||||
}
|
||||
|
||||
func (pn *P2PNetwork) addDirectTunnel(config AppConfig, tid uint64) (*P2PTunnel, error) {
|
||||
gLog.Printf(LevelDEBUG, "addDirectTunnel %s%d to %s:%s:%d start", config.Protocol, config.SrcPort, config.PeerNode, config.DstHost, config.DstPort)
|
||||
defer gLog.Printf(LevelDEBUG, "addDirectTunnel %s%d to %s:%s:%d end", config.Protocol, config.SrcPort, config.PeerNode, config.DstHost, config.DstPort)
|
||||
gLog.Printf(LvDEBUG, "addDirectTunnel %s%d to %s:%s:%d start", config.Protocol, config.SrcPort, config.PeerNode, config.DstHost, config.DstPort)
|
||||
defer gLog.Printf(LvDEBUG, "addDirectTunnel %s%d to %s:%s:%d end", config.Protocol, config.SrcPort, config.PeerNode, config.DstHost, config.DstPort)
|
||||
isClient := false
|
||||
// client side tid=0, assign random uint64
|
||||
if tid == 0 {
|
||||
@@ -320,11 +326,11 @@ func (pn *P2PNetwork) addDirectTunnel(config AppConfig, tid uint64) (*P2PTunnel,
|
||||
}
|
||||
|
||||
// client side checking
|
||||
gLog.Println(LevelINFO, "tunnel already exist ", config.PeerNode)
|
||||
gLog.Println(LvINFO, "tunnel already exist ", config.PeerNode)
|
||||
isActive := t.checkActive()
|
||||
// inactive, close it
|
||||
if !isActive {
|
||||
gLog.Println(LevelINFO, "but it's not active, close it ", config.PeerNode)
|
||||
gLog.Println(LvINFO, "but it's not active, close it ", config.PeerNode)
|
||||
t.close()
|
||||
} else {
|
||||
// active
|
||||
@@ -346,47 +352,40 @@ func (pn *P2PNetwork) addDirectTunnel(config AppConfig, tid uint64) (*P2PTunnel,
|
||||
t.init()
|
||||
if isClient {
|
||||
if err := t.connect(); err != nil {
|
||||
gLog.Println(LevelERROR, "p2pTunnel connect error:", err)
|
||||
gLog.Println(LvERROR, "p2pTunnel connect error:", err)
|
||||
return t, err
|
||||
}
|
||||
} else {
|
||||
rsp := PushConnectRsp{
|
||||
Error: 0,
|
||||
Detail: "connect ok",
|
||||
To: t.config.PeerNode,
|
||||
From: pn.config.Node,
|
||||
NatType: pn.config.natType,
|
||||
FromIP: pn.config.publicIP,
|
||||
ConeNatPort: t.coneNatPort,
|
||||
ID: t.id}
|
||||
t.pn.push(t.config.PeerNode, MsgPushConnectRsp, rsp)
|
||||
if err := t.listen(); err != nil {
|
||||
gLog.Println(LevelERROR, "p2pTunnel listen error:", err)
|
||||
gLog.Println(LvERROR, "p2pTunnel listen error:", err)
|
||||
return t, err
|
||||
}
|
||||
}
|
||||
}
|
||||
// store it when success
|
||||
gLog.Printf(LevelDEBUG, "store tunnel %d", tid)
|
||||
gLog.Printf(LvDEBUG, "store tunnel %d", tid)
|
||||
pn.allTunnels.Store(tid, t)
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (pn *P2PNetwork) init() error {
|
||||
gLog.Println(LevelINFO, "init start")
|
||||
gLog.Println(LvINFO, "init start")
|
||||
var err error
|
||||
for {
|
||||
// detect nat type
|
||||
pn.config.publicIP, pn.config.natType, err = getNATType(pn.config.ServerHost, pn.config.UDPPort1, pn.config.UDPPort2)
|
||||
// TODO rm test s2s
|
||||
pn.config.publicIP, pn.config.natType, pn.config.hasUPNPorNATPMP, err = getNATType(pn.config.ServerHost, pn.config.UDPPort1, pn.config.UDPPort2)
|
||||
// for testcase
|
||||
if strings.Contains(pn.config.Node, "openp2pS2STest") {
|
||||
pn.config.natType = NATSymmetric
|
||||
}
|
||||
if strings.Contains(pn.config.Node, "openp2pC2CTest") {
|
||||
pn.config.natType = NATCone
|
||||
}
|
||||
if err != nil {
|
||||
gLog.Println(LevelDEBUG, "detect NAT type error:", err)
|
||||
gLog.Println(LvDEBUG, "detect NAT type error:", err)
|
||||
break
|
||||
}
|
||||
gLog.Println(LevelDEBUG, "detect NAT type:", pn.config.natType, " publicIP:", pn.config.publicIP)
|
||||
gLog.Println(LvDEBUG, "detect NAT type:", pn.config.natType, " publicIP:", pn.config.publicIP)
|
||||
gatewayURL := fmt.Sprintf("%s:%d", pn.config.ServerHost, pn.config.ServerPort)
|
||||
forwardPath := "/openp2p/v1/login"
|
||||
config := tls.Config{InsecureSkipVerify: true} // let's encrypt root cert "DST Root CA X3" expired at 2021/09/29. many old system(windows server 2008 etc) will not trust our cert
|
||||
@@ -419,28 +418,30 @@ func (pn *P2PNetwork) init() error {
|
||||
pn.config.os = getOsName()
|
||||
|
||||
req := ReportBasic{
|
||||
Mac: pn.config.mac,
|
||||
LanIP: pn.config.localIP,
|
||||
OS: pn.config.os,
|
||||
Version: OpenP2PVersion,
|
||||
Mac: pn.config.mac,
|
||||
LanIP: pn.config.localIP,
|
||||
OS: pn.config.os,
|
||||
HasIPv4: pn.config.hasIPv4,
|
||||
HasUPNPorNATPMP: pn.config.hasUPNPorNATPMP,
|
||||
Version: OpenP2PVersion,
|
||||
}
|
||||
rsp := netInfo()
|
||||
gLog.Println(LevelDEBUG, "netinfo:", rsp)
|
||||
gLog.Println(LvDEBUG, "netinfo:", rsp)
|
||||
if rsp != nil && rsp.Country != "" {
|
||||
if len(rsp.IP) == net.IPv6len {
|
||||
if IsIPv6(rsp.IP.String()) {
|
||||
pn.config.ipv6 = rsp.IP.String()
|
||||
req.IPv6 = rsp.IP.String()
|
||||
}
|
||||
req.NetInfo = *rsp
|
||||
}
|
||||
pn.write(MsgReport, MsgReportBasic, &req)
|
||||
gLog.Println(LevelDEBUG, "P2PNetwork init ok")
|
||||
gLog.Println(LvDEBUG, "P2PNetwork init ok")
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
// init failed, retry
|
||||
pn.restartCh <- true
|
||||
gLog.Println(LevelERROR, "P2PNetwork init error:", err)
|
||||
gLog.Println(LvERROR, "P2PNetwork init error:", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -449,7 +450,7 @@ func (pn *P2PNetwork) handleMessage(t int, msg []byte) {
|
||||
head := openP2PHeader{}
|
||||
err := binary.Read(bytes.NewReader(msg[:openP2PHeaderSize]), binary.LittleEndian, &head)
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "handleMessage error:", err)
|
||||
gLog.Println(LvERROR, "handleMessage error:", err)
|
||||
return
|
||||
}
|
||||
switch head.MainType {
|
||||
@@ -458,11 +459,11 @@ func (pn *P2PNetwork) handleMessage(t int, msg []byte) {
|
||||
rsp := LoginRsp{}
|
||||
err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "wrong login response:%s", err)
|
||||
gLog.Printf(LvERROR, "wrong login response:%s", err)
|
||||
return
|
||||
}
|
||||
if rsp.Error != 0 {
|
||||
gLog.Printf(LevelERROR, "login error:%d, detail:%s", rsp.Error, rsp.Detail)
|
||||
gLog.Printf(LvERROR, "login error:%d, detail:%s", rsp.Error, rsp.Detail)
|
||||
pn.running = false
|
||||
} else {
|
||||
pn.serverTs = rsp.Ts
|
||||
@@ -472,10 +473,10 @@ func (pn *P2PNetwork) handleMessage(t int, msg []byte) {
|
||||
gConf.setUser(rsp.User)
|
||||
gConf.save()
|
||||
pn.localTs = time.Now().Unix()
|
||||
gLog.Printf(LevelINFO, "login ok. user=%s,Server ts=%d, local ts=%d", rsp.User, rsp.Ts, pn.localTs)
|
||||
gLog.Printf(LvINFO, "login ok. user=%s,Server ts=%d, local ts=%d", rsp.User, rsp.Ts, pn.localTs)
|
||||
}
|
||||
case MsgHeartbeat:
|
||||
gLog.Printf(LevelDEBUG, "P2PNetwork heartbeat ok")
|
||||
gLog.Printf(LvDEBUG, "P2PNetwork heartbeat ok")
|
||||
case MsgPush:
|
||||
handlePush(pn, head.SubType, msg)
|
||||
default:
|
||||
@@ -488,21 +489,21 @@ func (pn *P2PNetwork) handleMessage(t int, msg []byte) {
|
||||
}
|
||||
|
||||
func (pn *P2PNetwork) readLoop() {
|
||||
gLog.Printf(LevelDEBUG, "P2PNetwork readLoop start")
|
||||
gLog.Printf(LvDEBUG, "P2PNetwork readLoop start")
|
||||
pn.wg.Add(1)
|
||||
defer pn.wg.Done()
|
||||
for pn.running {
|
||||
pn.conn.SetReadDeadline(time.Now().Add(NetworkHeartbeatTime + 10*time.Second))
|
||||
t, msg, err := pn.conn.ReadMessage()
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "P2PNetwork read error:%s", err)
|
||||
gLog.Printf(LvERROR, "P2PNetwork read error:%s", err)
|
||||
pn.conn.Close()
|
||||
pn.restartCh <- true
|
||||
break
|
||||
}
|
||||
pn.handleMessage(t, msg)
|
||||
}
|
||||
gLog.Printf(LevelDEBUG, "P2PNetwork readLoop end")
|
||||
gLog.Printf(LvDEBUG, "P2PNetwork readLoop end")
|
||||
}
|
||||
|
||||
func (pn *P2PNetwork) write(mainType uint16, subType uint16, packet interface{}) error {
|
||||
@@ -516,14 +517,14 @@ func (pn *P2PNetwork) write(mainType uint16, subType uint16, packet interface{})
|
||||
pn.writeMtx.Lock()
|
||||
defer pn.writeMtx.Unlock()
|
||||
if err = pn.conn.WriteMessage(websocket.BinaryMessage, msg); err != nil {
|
||||
gLog.Printf(LevelERROR, "write msgType %d,%d error:%s", mainType, subType, err)
|
||||
gLog.Printf(LvERROR, "write msgType %d,%d error:%s", mainType, subType, err)
|
||||
pn.conn.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (pn *P2PNetwork) relay(to uint64, body []byte) error {
|
||||
gLog.Printf(LevelDEBUG, "relay data to %d", to)
|
||||
gLog.Printf(LvDEBUG, "relay data to %d", to)
|
||||
i, ok := pn.allTunnels.Load(to)
|
||||
if !ok {
|
||||
return nil
|
||||
@@ -537,7 +538,7 @@ func (pn *P2PNetwork) relay(to uint64, body []byte) error {
|
||||
}
|
||||
|
||||
func (pn *P2PNetwork) push(to string, subType uint16, packet interface{}) error {
|
||||
gLog.Printf(LevelDEBUG, "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")
|
||||
}
|
||||
@@ -559,7 +560,7 @@ func (pn *P2PNetwork) push(to string, subType uint16, packet interface{}) error
|
||||
pn.writeMtx.Lock()
|
||||
defer pn.writeMtx.Unlock()
|
||||
if err = pn.conn.WriteMessage(websocket.BinaryMessage, pushMsg); err != nil {
|
||||
gLog.Printf(LevelERROR, "push to %s error:%s", to, err)
|
||||
gLog.Printf(LvERROR, "push to %s error:%s", to, err)
|
||||
pn.conn.Close()
|
||||
}
|
||||
return err
|
||||
@@ -578,13 +579,13 @@ func (pn *P2PNetwork) read(node string, mainType uint16, subType uint16, timeout
|
||||
for {
|
||||
select {
|
||||
case <-time.After(timeout):
|
||||
gLog.Printf(LevelERROR, "wait msg%d:%d timeout", mainType, subType)
|
||||
gLog.Printf(LvERROR, "wait msg%d:%d timeout", mainType, subType)
|
||||
return
|
||||
case msg := <-ch:
|
||||
head = &openP2PHeader{}
|
||||
err := binary.Read(bytes.NewReader(msg[:openP2PHeaderSize]), binary.LittleEndian, head)
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "read msg error:", err)
|
||||
gLog.Println(LvERROR, "read msg error:", err)
|
||||
break
|
||||
}
|
||||
if head.MainType != mainType || head.SubType != subType {
|
||||
|
||||
367
p2ptunnel.go
367
p2ptunnel.go
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
type P2PTunnel struct {
|
||||
pn *P2PNetwork
|
||||
conn p2pConn
|
||||
conn underlay
|
||||
hbTime time.Time
|
||||
hbMtx sync.Mutex
|
||||
hbTimeRelay time.Time
|
||||
@@ -25,7 +25,7 @@ type P2PTunnel struct {
|
||||
id uint64
|
||||
running bool
|
||||
runMtx sync.Mutex
|
||||
isServer bool // 0:server 1:client
|
||||
tunnelServer bool // different from underlayServer
|
||||
coneLocalPort int
|
||||
coneNatPort int
|
||||
}
|
||||
@@ -39,29 +39,47 @@ func (t *P2PTunnel) init() {
|
||||
localPort := int(rand.Uint32()%10000 + 50000)
|
||||
if t.pn.config.natType == NATCone {
|
||||
// prepare one random cone hole
|
||||
_, _, port1, _ := natTest(t.pn.config.ServerHost, t.pn.config.UDPPort1, localPort, 0)
|
||||
_, _, _, port1, _ := natTest(t.pn.config.ServerHost, t.pn.config.UDPPort1, localPort, 0)
|
||||
t.coneLocalPort = localPort
|
||||
t.coneNatPort = port1
|
||||
t.la = &net.UDPAddr{IP: net.ParseIP(t.pn.config.localIP), Port: t.coneLocalPort}
|
||||
} else {
|
||||
t.coneLocalPort = localPort
|
||||
t.coneNatPort = localPort // NATNONE or symmetric doesn't need coneNatPort
|
||||
t.coneNatPort = localPort // symmetric doesn't need coneNatPort
|
||||
if t.pn.config.hasUPNPorNATPMP == 1 {
|
||||
nat, err := Discover()
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "could not perform UPNP discover:", err)
|
||||
} else {
|
||||
externalPort, err := nat.AddPortMapping("tcp", localPort, localPort, "openp2p", 30) // timeout the connection still alive, make the timeout short
|
||||
if err != nil {
|
||||
gLog.Println(LvDEBUG, "could not add udp UPNP port mapping", externalPort)
|
||||
}
|
||||
}
|
||||
}
|
||||
t.la = &net.UDPAddr{IP: net.ParseIP(t.pn.config.localIP), Port: t.coneLocalPort}
|
||||
}
|
||||
gLog.Printf(LevelDEBUG, "prepare punching port %d:%d", t.coneLocalPort, t.coneNatPort)
|
||||
gLog.Printf(LvDEBUG, "prepare punching port %d:%d", t.coneLocalPort, t.coneNatPort)
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) connect() error {
|
||||
gLog.Printf(LevelDEBUG, "start p2pTunnel to %s ", t.config.PeerNode)
|
||||
t.isServer = false
|
||||
gLog.Printf(LvDEBUG, "start p2pTunnel to %s ", t.config.PeerNode)
|
||||
t.tunnelServer = false
|
||||
appKey := uint64(0)
|
||||
req := PushConnectReq{
|
||||
Token: t.config.peerToken,
|
||||
From: t.pn.config.Node,
|
||||
FromToken: t.pn.config.Token,
|
||||
FromIP: t.pn.config.publicIP,
|
||||
ConeNatPort: t.coneNatPort,
|
||||
NatType: t.pn.config.natType,
|
||||
ID: t.id}
|
||||
Token: t.config.peerToken,
|
||||
From: t.pn.config.Node,
|
||||
FromToken: t.pn.config.Token,
|
||||
FromIP: t.pn.config.publicIP,
|
||||
ConeNatPort: t.coneNatPort,
|
||||
NatType: t.pn.config.natType,
|
||||
HasIPv4: t.pn.config.hasIPv4,
|
||||
IPv6: t.pn.config.IPv6,
|
||||
HasUPNPorNATPMP: t.pn.config.hasUPNPorNATPMP,
|
||||
ID: t.id,
|
||||
AppKey: appKey,
|
||||
Version: OpenP2PVersion,
|
||||
}
|
||||
t.pn.push(t.config.PeerNode, MsgPushConnectReq, req)
|
||||
head, body := t.pn.read(t.config.PeerNode, MsgPush, MsgPushConnectRsp, time.Second*10)
|
||||
if head == nil {
|
||||
@@ -70,19 +88,23 @@ func (t *P2PTunnel) connect() error {
|
||||
rsp := PushConnectRsp{}
|
||||
err := json.Unmarshal(body, &rsp)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "wrong MsgPushConnectRsp:%s", err)
|
||||
gLog.Printf(LvERROR, "wrong MsgPushConnectRsp:%s", err)
|
||||
return err
|
||||
}
|
||||
// gLog.Println(LevelINFO, rsp)
|
||||
if rsp.Error != 0 {
|
||||
return errors.New(rsp.Detail)
|
||||
}
|
||||
t.config.peerNatType = int(rsp.NatType)
|
||||
t.config.peerNatType = rsp.NatType
|
||||
t.config.hasIPv4 = rsp.HasIPv4
|
||||
t.config.IPv6 = rsp.IPv6
|
||||
t.config.hasUPNPorNATPMP = rsp.HasUPNPorNATPMP
|
||||
t.config.peerVersion = rsp.Version
|
||||
t.config.peerConeNatPort = rsp.ConeNatPort
|
||||
t.config.peerIP = rsp.FromIP
|
||||
err = t.handshake()
|
||||
err = t.start()
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "handshake error:", err)
|
||||
gLog.Println(LvERROR, "handshake error:", err)
|
||||
err = ErrorHandshake
|
||||
}
|
||||
return err
|
||||
@@ -135,6 +157,20 @@ func (t *P2PTunnel) close() {
|
||||
t.pn.allTunnels.Delete(t.id)
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) start() error {
|
||||
if !t.isSupportTCP() {
|
||||
if err := t.handshake(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err := t.connectUnderlay()
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) handshake() error {
|
||||
if t.config.peerConeNatPort > 0 {
|
||||
var err error
|
||||
@@ -143,7 +179,7 @@ func (t *P2PTunnel) handshake() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
gLog.Println(LevelDEBUG, "handshake to ", t.config.PeerNode)
|
||||
gLog.Println(LvDEBUG, "handshake to ", t.config.PeerNode)
|
||||
var err error
|
||||
// TODO: handle NATNone, nodes with public ip has no punching
|
||||
if (t.pn.config.natType == NATCone && t.config.peerNatType == NATCone) || (t.pn.config.natType == NATNone || t.config.peerNatType == NATNone) {
|
||||
@@ -159,50 +195,70 @@ func (t *P2PTunnel) handshake() error {
|
||||
return errors.New("unknown error")
|
||||
}
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "punch handshake error:", err)
|
||||
return err
|
||||
}
|
||||
gLog.Printf(LevelDEBUG, "handshake to %s ok", t.config.PeerNode)
|
||||
err = t.run()
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, err)
|
||||
gLog.Println(LvERROR, "punch handshake error:", err)
|
||||
return err
|
||||
}
|
||||
gLog.Printf(LvDEBUG, "handshake to %s ok", t.config.PeerNode)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) run() error {
|
||||
if t.isServer {
|
||||
qConn, e := listenQuic(t.la.String(), TunnelIdleTimeout)
|
||||
if e != nil {
|
||||
gLog.Println(LevelINFO, "listen quic error:", e, ", retry...")
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
qConn, e = listenQuic(t.la.String(), TunnelIdleTimeout)
|
||||
if e != nil {
|
||||
return fmt.Errorf("listen quic error:%s", e)
|
||||
}
|
||||
func (t *P2PTunnel) connectUnderlay() (err error) {
|
||||
if !t.isSupportTCP() {
|
||||
t.conn, err = t.connectUnderlayQuic()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.pn.push(t.config.PeerNode, MsgPushQuicConnect, nil)
|
||||
e = qConn.Accept()
|
||||
if e != nil {
|
||||
} else {
|
||||
// TODO: udp or tcp first?
|
||||
// prepare a la ra for udp
|
||||
// t.conn, err = t.connectUnderlayQuic()
|
||||
// TODO: support ipv6
|
||||
// if t.pn.config.hasIPv4 == 1 || t.config.hasIPv4 == 1 {
|
||||
t.conn, err = t.connectUnderlayTCP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// }
|
||||
// if IsIPv6(t.pn.config.IPv6) && IsIPv6(t.config.IPv6) { // both have ipv6
|
||||
// t.conn, err = t.connectUnderlayTCP6()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
}
|
||||
t.setRun(true)
|
||||
go t.readLoop()
|
||||
go t.heartbeatLoop()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) connectUnderlayQuic() (c underlay, err error) {
|
||||
gLog.Println(LvINFO, "connectUnderlayQuic start")
|
||||
defer gLog.Println(LvINFO, "connectUnderlayQuic end")
|
||||
var qConn *underlayQUIC
|
||||
if t.isUnderlayServer() {
|
||||
time.Sleep(time.Millisecond * 10) // punching udp port will need some times in some env
|
||||
qConn, err = listenQuic(t.la.String(), TunnelIdleTimeout)
|
||||
if err != nil {
|
||||
gLog.Println(LvINFO, "listen quic error:", err, ", retry...")
|
||||
}
|
||||
t.pn.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
|
||||
err = qConn.Accept()
|
||||
if err != nil {
|
||||
qConn.CloseListener()
|
||||
return fmt.Errorf("accept quic error:%s", e)
|
||||
return nil, fmt.Errorf("accept quic error:%s", err)
|
||||
}
|
||||
_, buff, err := qConn.ReadMessage()
|
||||
if e != nil {
|
||||
_, buff, err := qConn.ReadBuffer()
|
||||
if err != nil {
|
||||
qConn.listener.Close()
|
||||
return fmt.Errorf("read start msg error:%s", err)
|
||||
return nil, fmt.Errorf("read start msg error:%s", err)
|
||||
}
|
||||
if buff != nil {
|
||||
gLog.Println(LevelDEBUG, string(buff))
|
||||
gLog.Println(LvDEBUG, string(buff))
|
||||
}
|
||||
qConn.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, []byte("OpenP2P,hello2"))
|
||||
gLog.Println(LevelDEBUG, "quic connection ok")
|
||||
t.conn = qConn
|
||||
t.setRun(true)
|
||||
go t.readLoop()
|
||||
go t.writeLoop()
|
||||
return nil
|
||||
gLog.Println(LvDEBUG, "quic connection ok")
|
||||
return qConn, nil
|
||||
}
|
||||
|
||||
//else
|
||||
@@ -211,44 +267,133 @@ func (t *P2PTunnel) run() error {
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
conn, e = net.ListenUDP("udp", t.la)
|
||||
if e != nil {
|
||||
return fmt.Errorf("quic listen error:%s", e)
|
||||
return nil, fmt.Errorf("quic listen error:%s", e)
|
||||
}
|
||||
}
|
||||
t.pn.read(t.config.PeerNode, MsgPush, MsgPushQuicConnect, time.Second*5)
|
||||
gLog.Println(LevelDEBUG, "quic dial to ", t.ra.String())
|
||||
qConn, e := dialQuic(conn, t.ra, TunnelIdleTimeout)
|
||||
t.pn.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, time.Second*5)
|
||||
gLog.Println(LvDEBUG, "quic dial to ", t.ra.String())
|
||||
qConn, e = dialQuic(conn, t.ra, TunnelIdleTimeout)
|
||||
if e != nil {
|
||||
return fmt.Errorf("quic dial to %s error:%s", t.ra.String(), e)
|
||||
return nil, fmt.Errorf("quic dial to %s error:%s", t.ra.String(), e)
|
||||
}
|
||||
handshakeBegin := time.Now()
|
||||
qConn.WriteBytes(MsgP2P, MsgTunnelHandshake, []byte("OpenP2P,hello"))
|
||||
_, buff, err := qConn.ReadMessage()
|
||||
_, buff, err := qConn.ReadBuffer()
|
||||
if e != nil {
|
||||
qConn.listener.Close()
|
||||
return fmt.Errorf("read MsgTunnelHandshake error:%s", err)
|
||||
return nil, fmt.Errorf("read MsgTunnelHandshake error:%s", err)
|
||||
}
|
||||
if buff != nil {
|
||||
gLog.Println(LevelDEBUG, string(buff))
|
||||
gLog.Println(LvDEBUG, string(buff))
|
||||
}
|
||||
|
||||
gLog.Println(LevelINFO, "rtt=", time.Since(handshakeBegin))
|
||||
gLog.Println(LevelDEBUG, "quic connection ok")
|
||||
t.conn = qConn
|
||||
t.setRun(true)
|
||||
go t.readLoop()
|
||||
go t.writeLoop()
|
||||
return nil
|
||||
gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin))
|
||||
gLog.Println(LvDEBUG, "quic connection ok")
|
||||
return qConn, nil
|
||||
}
|
||||
|
||||
// websocket
|
||||
func (t *P2PTunnel) connectUnderlayTCP() (c underlay, err error) {
|
||||
gLog.Println(LvINFO, "connectUnderlayTCP start")
|
||||
defer gLog.Println(LvINFO, "connectUnderlayTCP end")
|
||||
var qConn *underlayTCP
|
||||
if t.isUnderlayServer() {
|
||||
t.pn.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
|
||||
qConn, err = listenTCP(t.coneNatPort, TunnelIdleTimeout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listen TCP error:%s", err)
|
||||
}
|
||||
_, buff, err := qConn.ReadBuffer()
|
||||
if err != nil {
|
||||
qConn.listener.Close()
|
||||
return nil, fmt.Errorf("read start msg error:%s", err)
|
||||
}
|
||||
if buff != nil {
|
||||
gLog.Println(LvDEBUG, string(buff))
|
||||
}
|
||||
qConn.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, []byte("OpenP2P,hello2"))
|
||||
gLog.Println(LvDEBUG, "TCP connection ok")
|
||||
return qConn, nil
|
||||
}
|
||||
|
||||
//else
|
||||
t.pn.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, time.Second*5)
|
||||
gLog.Println(LvDEBUG, "TCP dial to ", t.ra.String())
|
||||
qConn, err = dialTCP(t.config.peerIP, t.config.peerConeNatPort)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("TCP dial to %s error:%s", t.ra.String(), err)
|
||||
}
|
||||
handshakeBegin := time.Now()
|
||||
qConn.WriteBytes(MsgP2P, MsgTunnelHandshake, []byte("OpenP2P,hello"))
|
||||
_, buff, err := qConn.ReadBuffer()
|
||||
if err != nil {
|
||||
qConn.listener.Close()
|
||||
return nil, fmt.Errorf("read MsgTunnelHandshake error:%s", err)
|
||||
}
|
||||
if buff != nil {
|
||||
gLog.Println(LvDEBUG, string(buff))
|
||||
}
|
||||
|
||||
gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin))
|
||||
gLog.Println(LvDEBUG, "TCP connection ok")
|
||||
return qConn, nil
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) connectUnderlayTCP6() (c underlay, err error) {
|
||||
gLog.Println(LvINFO, "connectUnderlayTCP start")
|
||||
defer gLog.Println(LvINFO, "connectUnderlayTCP end")
|
||||
var qConn *underlayTCP6
|
||||
if t.isUnderlayServer() {
|
||||
t.pn.push(t.config.PeerNode, MsgPushUnderlayConnect, nil)
|
||||
qConn, err = listenTCP6(t.coneNatPort, TunnelIdleTimeout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listen TCP error:%s", err)
|
||||
}
|
||||
_, buff, err := qConn.ReadBuffer()
|
||||
if err != nil {
|
||||
qConn.listener.Close()
|
||||
return nil, fmt.Errorf("read start msg error:%s", err)
|
||||
}
|
||||
if buff != nil {
|
||||
gLog.Println(LvDEBUG, string(buff))
|
||||
}
|
||||
qConn.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, []byte("OpenP2P,hello2"))
|
||||
gLog.Println(LvDEBUG, "TCP connection ok")
|
||||
return qConn, nil
|
||||
}
|
||||
|
||||
//else
|
||||
t.pn.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, time.Second*5)
|
||||
gLog.Println(LvDEBUG, "TCP dial to ", t.ra.String())
|
||||
qConn, err = dialTCP6(t.config.IPv6, t.config.peerConeNatPort)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("TCP dial to %s error:%s", t.ra.String(), err)
|
||||
}
|
||||
handshakeBegin := time.Now()
|
||||
qConn.WriteBytes(MsgP2P, MsgTunnelHandshake, []byte("OpenP2P,hello"))
|
||||
_, buff, err := qConn.ReadBuffer()
|
||||
if err != nil {
|
||||
qConn.listener.Close()
|
||||
return nil, fmt.Errorf("read MsgTunnelHandshake error:%s", err)
|
||||
}
|
||||
if buff != nil {
|
||||
gLog.Println(LvDEBUG, string(buff))
|
||||
}
|
||||
|
||||
gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin))
|
||||
gLog.Println(LvDEBUG, "TCP connection ok")
|
||||
return qConn, nil
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) readLoop() {
|
||||
decryptData := make([]byte, ReadBuffLen+PaddingSize) // 16 bytes for padding
|
||||
gLog.Printf(LevelDEBUG, "%d tunnel readloop start", t.id)
|
||||
gLog.Printf(LvDEBUG, "%d tunnel readloop start", t.id)
|
||||
for t.isRuning() {
|
||||
t.conn.SetReadDeadline(time.Now().Add(TunnelIdleTimeout))
|
||||
head, body, err := t.conn.ReadMessage()
|
||||
head, body, err := t.conn.ReadBuffer()
|
||||
if err != nil {
|
||||
if t.isRuning() {
|
||||
gLog.Printf(LevelERROR, "%d tunnel read error:%s", t.id, err)
|
||||
gLog.Printf(LvERROR, "%d tunnel read error:%s", t.id, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -258,22 +403,22 @@ func (t *P2PTunnel) readLoop() {
|
||||
switch head.SubType {
|
||||
case MsgTunnelHeartbeat:
|
||||
t.conn.WriteBytes(MsgP2P, MsgTunnelHeartbeatAck, nil)
|
||||
gLog.Printf(LevelDEBUG, "%d read tunnel heartbeat", t.id)
|
||||
gLog.Printf(LvDEBUG, "%d read tunnel heartbeat", t.id)
|
||||
case MsgTunnelHeartbeatAck:
|
||||
t.hbMtx.Lock()
|
||||
t.hbTime = time.Now()
|
||||
t.hbMtx.Unlock()
|
||||
gLog.Printf(LevelDEBUG, "%d read tunnel heartbeat ack", t.id)
|
||||
gLog.Printf(LvDEBUG, "%d read tunnel heartbeat ack", t.id)
|
||||
case MsgOverlayData:
|
||||
if len(body) < overlayHeaderSize {
|
||||
continue
|
||||
}
|
||||
overlayID := binary.LittleEndian.Uint64(body[:8])
|
||||
gLog.Printf(LevelDEBUG, "%d tunnel read overlay data %d", t.id, overlayID)
|
||||
gLog.Printf(LvDEBUG, "%d tunnel read overlay data %d", t.id, overlayID)
|
||||
s, ok := t.overlayConns.Load(overlayID)
|
||||
if !ok {
|
||||
// debug level, when overlay connection closed, always has some packet not found tunnel
|
||||
gLog.Printf(LevelDEBUG, "%d tunnel not found overlay connection %d", t.id, overlayID)
|
||||
gLog.Printf(LvDEBUG, "%d tunnel not found overlay connection %d", t.id, overlayID)
|
||||
continue
|
||||
}
|
||||
overlayConn, ok := s.(*overlayConn)
|
||||
@@ -287,10 +432,10 @@ func (t *P2PTunnel) readLoop() {
|
||||
}
|
||||
_, err = overlayConn.Write(payload)
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "overlay write error:", err)
|
||||
gLog.Println(LvERROR, "overlay write error:", err)
|
||||
}
|
||||
case MsgRelayData:
|
||||
gLog.Printf(LevelDEBUG, "got relay data datalen=%d", head.DataLen)
|
||||
gLog.Printf(LvDEBUG, "got relay data datalen=%d", head.DataLen)
|
||||
if len(body) < 8 {
|
||||
continue
|
||||
}
|
||||
@@ -300,10 +445,10 @@ func (t *P2PTunnel) readLoop() {
|
||||
req := RelayHeartbeat{}
|
||||
err := json.Unmarshal(body, &req)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "wrong RelayHeartbeat:%s", err)
|
||||
gLog.Printf(LvERROR, "wrong RelayHeartbeat:%s", err)
|
||||
continue
|
||||
}
|
||||
gLog.Printf(LevelDEBUG, "got MsgRelayHeartbeat from %d:%d", req.RelayTunnelID, req.AppID)
|
||||
gLog.Printf(LvDEBUG, "got MsgRelayHeartbeat from %d:%d", req.RelayTunnelID, req.AppID)
|
||||
relayHead := new(bytes.Buffer)
|
||||
binary.Write(relayHead, binary.LittleEndian, req.RelayTunnelID)
|
||||
msg, _ := newMessage(MsgP2P, MsgRelayHeartbeatAck, &req)
|
||||
@@ -313,26 +458,26 @@ func (t *P2PTunnel) readLoop() {
|
||||
req := RelayHeartbeat{}
|
||||
err := json.Unmarshal(body, &req)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "wrong RelayHeartbeat:%s", err)
|
||||
gLog.Printf(LvERROR, "wrong RelayHeartbeat:%s", err)
|
||||
continue
|
||||
}
|
||||
gLog.Printf(LevelDEBUG, "got MsgRelayHeartbeatAck to %d", req.AppID)
|
||||
gLog.Printf(LvDEBUG, "got MsgRelayHeartbeatAck to %d", req.AppID)
|
||||
t.pn.updateAppHeartbeat(req.AppID)
|
||||
case MsgOverlayConnectReq:
|
||||
req := OverlayConnectReq{}
|
||||
err := json.Unmarshal(body, &req)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "wrong MsgOverlayConnectReq:%s", err)
|
||||
gLog.Printf(LvERROR, "wrong MsgOverlayConnectReq:%s", err)
|
||||
continue
|
||||
}
|
||||
// app connect only accept token(not relay totp token), avoid someone using the share relay node's token
|
||||
if req.Token != t.pn.config.Token {
|
||||
gLog.Println(LevelERROR, "Access Denied:", req.Token)
|
||||
gLog.Println(LvERROR, "Access Denied:", req.Token)
|
||||
continue
|
||||
}
|
||||
|
||||
overlayID := req.ID
|
||||
gLog.Printf(LevelDEBUG, "App:%d overlayID:%d connect %+v", req.AppID, overlayID, req)
|
||||
gLog.Printf(LvDEBUG, "App:%d overlayID:%d connect %+v", req.AppID, overlayID, req)
|
||||
oConn := overlayConn{
|
||||
tunnel: t,
|
||||
id: overlayID,
|
||||
@@ -347,7 +492,7 @@ func (t *P2PTunnel) readLoop() {
|
||||
oConn.connTCP, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", req.DstIP, req.DstPort), time.Second*5)
|
||||
}
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, err)
|
||||
gLog.Println(LvERROR, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -365,11 +510,11 @@ func (t *P2PTunnel) readLoop() {
|
||||
req := OverlayDisconnectReq{}
|
||||
err := json.Unmarshal(body, &req)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "wrong OverlayDisconnectRequest:%s", err)
|
||||
gLog.Printf(LvERROR, "wrong OverlayDisconnectRequest:%s", err)
|
||||
continue
|
||||
}
|
||||
overlayID := req.ID
|
||||
gLog.Printf(LevelDEBUG, "%d disconnect overlay connection %d", t.id, overlayID)
|
||||
gLog.Printf(LvDEBUG, "%d disconnect overlay connection %d", t.id, overlayID)
|
||||
i, ok := t.overlayConns.Load(overlayID)
|
||||
if ok {
|
||||
oConn := i.(*overlayConn)
|
||||
@@ -380,32 +525,49 @@ func (t *P2PTunnel) readLoop() {
|
||||
}
|
||||
t.setRun(false)
|
||||
t.conn.Close()
|
||||
gLog.Printf(LevelDEBUG, "%d tunnel readloop end", t.id)
|
||||
gLog.Printf(LvDEBUG, "%d tunnel readloop end", t.id)
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) writeLoop() {
|
||||
func (t *P2PTunnel) heartbeatLoop() {
|
||||
tc := time.NewTicker(TunnelHeartbeatTime)
|
||||
defer tc.Stop()
|
||||
defer gLog.Printf(LevelDEBUG, "%d tunnel writeloop end", t.id)
|
||||
gLog.Printf(LvDEBUG, "%d tunnel heartbeatLoop start", t.id)
|
||||
defer gLog.Printf(LvDEBUG, "%d tunnel heartbeatLoop end", t.id)
|
||||
for t.isRuning() {
|
||||
select {
|
||||
case <-tc.C:
|
||||
// tunnel send
|
||||
err := t.conn.WriteBytes(MsgP2P, MsgTunnelHeartbeat, nil)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "%d write tunnel heartbeat error %s", t.id, err)
|
||||
gLog.Printf(LvERROR, "%d write tunnel heartbeat error %s", t.id, err)
|
||||
t.setRun(false)
|
||||
return
|
||||
}
|
||||
gLog.Printf(LevelDEBUG, "%d write tunnel heartbeat ok", t.id)
|
||||
gLog.Printf(LvDEBUG, "%d write tunnel heartbeat ok", t.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) listen() error {
|
||||
gLog.Printf(LevelDEBUG, "p2ptunnel wait for connecting")
|
||||
t.isServer = true
|
||||
return t.handshake()
|
||||
// notify client to connect
|
||||
rsp := PushConnectRsp{
|
||||
Error: 0,
|
||||
Detail: "connect ok",
|
||||
To: t.config.PeerNode,
|
||||
From: t.pn.config.Node,
|
||||
NatType: t.pn.config.natType,
|
||||
HasIPv4: t.pn.config.hasIPv4,
|
||||
IPv6: t.pn.config.IPv6,
|
||||
HasUPNPorNATPMP: t.pn.config.hasUPNPorNATPMP,
|
||||
FromIP: t.pn.config.publicIP,
|
||||
ConeNatPort: t.coneNatPort,
|
||||
ID: t.id,
|
||||
Version: OpenP2PVersion,
|
||||
}
|
||||
t.pn.push(t.config.PeerNode, MsgPushConnectRsp, rsp)
|
||||
gLog.Printf(LvDEBUG, "p2ptunnel wait for connecting")
|
||||
t.tunnelServer = true
|
||||
return t.start()
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) closeOverlayConns(appID uint64) {
|
||||
@@ -424,3 +586,24 @@ func (t *P2PTunnel) closeOverlayConns(appID uint64) {
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) isUnderlayServer() bool {
|
||||
if t.pn.config.natType == NATNone && t.config.peerNatType != NATNone {
|
||||
return true
|
||||
}
|
||||
if t.pn.config.natType != NATNone && t.config.peerNatType == NATNone {
|
||||
return false
|
||||
}
|
||||
// NAT or both has public IP
|
||||
return t.tunnelServer
|
||||
}
|
||||
|
||||
func (t *P2PTunnel) isSupportTCP() bool {
|
||||
if t.config.peerVersion == "" || compareVersion(t.config.peerVersion, LeastSupportTCPVersion) == LESS {
|
||||
return false
|
||||
}
|
||||
if t.pn.config.natType == NATNone || t.config.peerNatType == NATNone {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
78
protocol.go
78
protocol.go
@@ -10,8 +10,9 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const OpenP2PVersion = "1.4.2"
|
||||
const OpenP2PVersion = "1.5.6"
|
||||
const ProducnName string = "openp2p"
|
||||
const LeastSupportTCPVersion = "1.5.0"
|
||||
|
||||
type openP2PHeader struct {
|
||||
DataLen uint32
|
||||
@@ -69,6 +70,7 @@ const (
|
||||
MsgReport = 6
|
||||
)
|
||||
|
||||
// TODO: seperate node push and web push.
|
||||
const (
|
||||
MsgPushRsp = 0
|
||||
MsgPushConnectReq = 1
|
||||
@@ -78,11 +80,12 @@ const (
|
||||
MsgPushAddRelayTunnelRsp = 5
|
||||
MsgPushUpdate = 6
|
||||
MsgPushReportApps = 7
|
||||
MsgPushQuicConnect = 8
|
||||
MsgPushUnderlayConnect = 8
|
||||
MsgPushEditApp = 9
|
||||
MsgPushSwitchApp = 10
|
||||
MsgPushRestart = 11
|
||||
MsgPushEditNode = 12
|
||||
MsgPushAPPKey = 13
|
||||
)
|
||||
|
||||
// MsgP2P sub type message
|
||||
@@ -131,7 +134,7 @@ const (
|
||||
AESKeySize = 16
|
||||
MaxRetry = 10
|
||||
RetryInterval = time.Second * 30
|
||||
PublicIPEchoTimeout = time.Second * 3
|
||||
PublicIPEchoTimeout = time.Second * 1
|
||||
NatTestTimeout = time.Second * 10
|
||||
ClientAPITimeout = time.Second * 10
|
||||
MaxDirectTry = 5
|
||||
@@ -145,6 +148,13 @@ const (
|
||||
NATUnknown = 314
|
||||
)
|
||||
|
||||
// underlay protocol
|
||||
const (
|
||||
UderlayAuto = "auto"
|
||||
UderlayQUIC = "quic"
|
||||
UderlayTCP = "tcp"
|
||||
)
|
||||
|
||||
func newMessage(mainType uint16, subType uint16, packet interface{}) ([]byte, error) {
|
||||
data, err := json.Marshal(packet)
|
||||
if err != nil {
|
||||
@@ -170,23 +180,32 @@ func nodeNameToID(name string) uint64 {
|
||||
}
|
||||
|
||||
type PushConnectReq struct {
|
||||
From string `json:"from,omitempty"`
|
||||
FromToken uint64 `json:"fromToken,omitempty"` //my token
|
||||
Token uint64 `json:"token,omitempty"` // totp token
|
||||
ConeNatPort int `json:"coneNatPort,omitempty"`
|
||||
NatType int `json:"natType,omitempty"`
|
||||
FromIP string `json:"fromIP,omitempty"`
|
||||
ID uint64 `json:"id,omitempty"`
|
||||
From string `json:"from,omitempty"`
|
||||
FromToken uint64 `json:"fromToken,omitempty"` //my token
|
||||
Version string `json:"version,omitempty"`
|
||||
Token uint64 `json:"token,omitempty"` // totp token
|
||||
ConeNatPort int `json:"coneNatPort,omitempty"` // if isPublic, is public port
|
||||
NatType int `json:"natType,omitempty"`
|
||||
HasIPv4 int `json:"hasIPv4,omitempty"`
|
||||
IPv6 string `json:"IPv6,omitempty"`
|
||||
HasUPNPorNATPMP int `json:"hasUPNPorNATPMP,omitempty"`
|
||||
FromIP string `json:"fromIP,omitempty"`
|
||||
ID uint64 `json:"id,omitempty"`
|
||||
AppKey uint64 `json:"appKey,omitempty"` // for underlay tcp
|
||||
}
|
||||
type PushConnectRsp struct {
|
||||
Error int `json:"error,omitempty"`
|
||||
From string `json:"from,omitempty"`
|
||||
To string `json:"to,omitempty"`
|
||||
Detail string `json:"detail,omitempty"`
|
||||
NatType int `json:"natType,omitempty"`
|
||||
ConeNatPort int `json:"coneNatPort,omitempty"`
|
||||
FromIP string `json:"fromIP,omitempty"`
|
||||
ID uint64 `json:"id,omitempty"`
|
||||
Error int `json:"error,omitempty"`
|
||||
From string `json:"from,omitempty"`
|
||||
To string `json:"to,omitempty"`
|
||||
Detail string `json:"detail,omitempty"`
|
||||
NatType int `json:"natType,omitempty"`
|
||||
HasIPv4 int `json:"hasIPv4,omitempty"`
|
||||
IPv6 string `json:"IPv6,omitempty"`
|
||||
HasUPNPorNATPMP int `json:"hasUPNPorNATPMP,omitempty"`
|
||||
ConeNatPort int `json:"coneNatPort,omitempty"` //it's not only cone, but also upnp or nat-pmp hole
|
||||
FromIP string `json:"fromIP,omitempty"`
|
||||
ID uint64 `json:"id,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
type PushRsp struct {
|
||||
Error int `json:"error,omitempty"`
|
||||
@@ -246,8 +265,13 @@ type AddRelayTunnelReq struct {
|
||||
From string `json:"from,omitempty"`
|
||||
RelayName string `json:"relayName,omitempty"`
|
||||
RelayToken uint64 `json:"relayToken,omitempty"`
|
||||
AppID uint64 `json:"appID,omitempty"`
|
||||
AppKey uint64 `json:"appKey,omitempty"`
|
||||
AppID uint64 `json:"appID,omitempty"` // deprecated
|
||||
AppKey uint64 `json:"appKey,omitempty"` // deprecated
|
||||
}
|
||||
|
||||
type APPKeySync struct {
|
||||
AppID uint64 `json:"appID,omitempty"`
|
||||
AppKey uint64 `json:"appKey,omitempty"`
|
||||
}
|
||||
|
||||
type RelayHeartbeat struct {
|
||||
@@ -256,12 +280,14 @@ type RelayHeartbeat struct {
|
||||
}
|
||||
|
||||
type ReportBasic struct {
|
||||
OS string `json:"os,omitempty"`
|
||||
Mac string `json:"mac,omitempty"`
|
||||
LanIP string `json:"lanIP,omitempty"`
|
||||
IPv6 string `json:"IPv6,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
NetInfo NetInfo `json:"netInfo,omitempty"`
|
||||
OS string `json:"os,omitempty"`
|
||||
Mac string `json:"mac,omitempty"`
|
||||
LanIP string `json:"lanIP,omitempty"`
|
||||
HasIPv4 int `json:"hasIPv4,omitempty"`
|
||||
IPv6 string `json:"IPv6,omitempty"`
|
||||
HasUPNPorNATPMP int `json:"hasUPNPorNATPMP,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
NetInfo NetInfo `json:"netInfo,omitempty"`
|
||||
}
|
||||
|
||||
type ReportConnect struct {
|
||||
|
||||
4
udp.go
4
udp.go
@@ -23,7 +23,7 @@ func UDPRead(conn *net.UDPConn, timeout int) (ra net.Addr, head *openP2PHeader,
|
||||
deadline := time.Now().Add(time.Millisecond * time.Duration(timeout))
|
||||
err = conn.SetReadDeadline(deadline)
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "SetReadDeadline error")
|
||||
gLog.Println(LvERROR, "SetReadDeadline error")
|
||||
return nil, nil, nil, 0, err
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ func UDPRead(conn *net.UDPConn, timeout int) (ra net.Addr, head *openP2PHeader,
|
||||
head = &openP2PHeader{}
|
||||
err = binary.Read(bytes.NewReader(result[:openP2PHeaderSize]), binary.LittleEndian, head)
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "parse p2pheader error:", err)
|
||||
gLog.Println(LvERROR, "parse p2pheader error:", err)
|
||||
return nil, nil, nil, 0, err
|
||||
}
|
||||
return
|
||||
|
||||
16
underlay.go
Normal file
16
underlay.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type underlay interface {
|
||||
ReadBuffer() (*openP2PHeader, []byte, error)
|
||||
WriteBytes(uint16, uint16, []byte) error
|
||||
WriteBuffer([]byte) error
|
||||
WriteMessage(uint16, uint16, interface{}) error
|
||||
Close() error
|
||||
SetReadDeadline(t time.Time) error
|
||||
SetWriteDeadline(t time.Time) error
|
||||
Protocol() string
|
||||
}
|
||||
155
underlay_quic.go
Normal file
155
underlay_quic.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
)
|
||||
|
||||
//quic.DialContext do not support version 44,disable it
|
||||
var quicVersion []quic.VersionNumber
|
||||
|
||||
type underlayQUIC struct {
|
||||
listener quic.Listener
|
||||
writeMtx *sync.Mutex
|
||||
quic.Stream
|
||||
quic.Connection
|
||||
}
|
||||
|
||||
func (conn *underlayQUIC) Protocol() string {
|
||||
return "quic"
|
||||
}
|
||||
|
||||
func (conn *underlayQUIC) ReadBuffer() (*openP2PHeader, []byte, error) {
|
||||
headBuf := make([]byte, openP2PHeaderSize)
|
||||
_, err := io.ReadFull(conn, headBuf)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
head, err := decodeHeader(headBuf)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
dataBuf := make([]byte, head.DataLen)
|
||||
_, err = io.ReadFull(conn, dataBuf)
|
||||
return head, dataBuf, err
|
||||
}
|
||||
|
||||
func (conn *underlayQUIC) WriteBytes(mainType uint16, subType uint16, data []byte) error {
|
||||
writeBytes := append(encodeHeader(mainType, subType, uint32(len(data))), data...)
|
||||
conn.writeMtx.Lock()
|
||||
_, err := conn.Write(writeBytes)
|
||||
conn.writeMtx.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (conn *underlayQUIC) WriteBuffer(data []byte) error {
|
||||
conn.writeMtx.Lock()
|
||||
_, err := conn.Write(data)
|
||||
conn.writeMtx.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (conn *underlayQUIC) WriteMessage(mainType uint16, subType uint16, packet interface{}) error {
|
||||
// TODO: call newMessage
|
||||
data, err := json.Marshal(packet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeBytes := append(encodeHeader(mainType, subType, uint32(len(data))), data...)
|
||||
conn.writeMtx.Lock()
|
||||
_, err = conn.Write(writeBytes)
|
||||
conn.writeMtx.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (conn *underlayQUIC) Close() error {
|
||||
conn.Stream.CancelRead(1)
|
||||
conn.Connection.CloseWithError(0, "")
|
||||
return nil
|
||||
}
|
||||
func (conn *underlayQUIC) CloseListener() {
|
||||
if conn.listener != nil {
|
||||
conn.listener.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *underlayQUIC) Accept() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
||||
defer cancel()
|
||||
sess, err := conn.listener.Accept(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stream, err := sess.AcceptStream(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conn.Stream = stream
|
||||
conn.Connection = sess
|
||||
return nil
|
||||
}
|
||||
|
||||
func listenQuic(addr string, idleTimeout time.Duration) (*underlayQUIC, error) {
|
||||
gLog.Println(LvDEBUG, "quic listen on ", addr)
|
||||
listener, err := quic.ListenAddr(addr, generateTLSConfig(),
|
||||
&quic.Config{Versions: quicVersion, MaxIdleTimeout: idleTimeout, DisablePathMTUDiscovery: true})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("quic.ListenAddr error:%s", err)
|
||||
}
|
||||
return &underlayQUIC{listener: listener, writeMtx: &sync.Mutex{}}, nil
|
||||
}
|
||||
|
||||
func dialQuic(conn *net.UDPConn, remoteAddr *net.UDPAddr, idleTimeout time.Duration) (*underlayQUIC, error) {
|
||||
tlsConf := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
NextProtos: []string{"openp2pv1"},
|
||||
}
|
||||
Connection, err := quic.DialContext(context.Background(), conn, remoteAddr, conn.LocalAddr().String(), tlsConf,
|
||||
&quic.Config{Versions: quicVersion, MaxIdleTimeout: idleTimeout, DisablePathMTUDiscovery: true})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("quic.DialContext error:%s", err)
|
||||
}
|
||||
stream, err := Connection.OpenStreamSync(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("OpenStreamSync error:%s", err)
|
||||
}
|
||||
qConn := &underlayQUIC{nil, &sync.Mutex{}, stream, Connection}
|
||||
return qConn, nil
|
||||
}
|
||||
|
||||
// Setup a bare-bones TLS config for the server
|
||||
func generateTLSConfig() *tls.Config {
|
||||
key, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
template := x509.Certificate{SerialNumber: big.NewInt(1)}
|
||||
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
|
||||
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
|
||||
|
||||
tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &tls.Config{
|
||||
Certificates: []tls.Certificate{tlsCert},
|
||||
NextProtos: []string{"openp2pv1"},
|
||||
}
|
||||
}
|
||||
92
underlay_tcp.go
Normal file
92
underlay_tcp.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type underlayTCP struct {
|
||||
listener net.Listener
|
||||
writeMtx *sync.Mutex
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (conn *underlayTCP) Protocol() string {
|
||||
return "tcp"
|
||||
}
|
||||
|
||||
func (conn *underlayTCP) ReadBuffer() (*openP2PHeader, []byte, error) {
|
||||
headBuf := make([]byte, openP2PHeaderSize)
|
||||
_, err := io.ReadFull(conn, headBuf)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
head, err := decodeHeader(headBuf)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
dataBuf := make([]byte, head.DataLen)
|
||||
_, err = io.ReadFull(conn, dataBuf)
|
||||
return head, dataBuf, err
|
||||
}
|
||||
|
||||
func (conn *underlayTCP) WriteBytes(mainType uint16, subType uint16, data []byte) error {
|
||||
writeBytes := append(encodeHeader(mainType, subType, uint32(len(data))), data...)
|
||||
conn.writeMtx.Lock()
|
||||
_, err := conn.Write(writeBytes)
|
||||
conn.writeMtx.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (conn *underlayTCP) WriteBuffer(data []byte) error {
|
||||
conn.writeMtx.Lock()
|
||||
_, err := conn.Write(data)
|
||||
conn.writeMtx.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (conn *underlayTCP) WriteMessage(mainType uint16, subType uint16, packet interface{}) error {
|
||||
// TODO: call newMessage
|
||||
data, err := json.Marshal(packet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeBytes := append(encodeHeader(mainType, subType, uint32(len(data))), data...)
|
||||
conn.writeMtx.Lock()
|
||||
_, err = conn.Write(writeBytes)
|
||||
conn.writeMtx.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (conn *underlayTCP) Close() error {
|
||||
return conn.Conn.Close()
|
||||
}
|
||||
|
||||
func listenTCP(port int, idleTimeout time.Duration) (*underlayTCP, error) {
|
||||
addr, _ := net.ResolveTCPAddr("tcp", fmt.Sprintf("0.0.0.0:%d", port))
|
||||
l, err := net.ListenTCP("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer l.Close()
|
||||
l.SetDeadline(time.Now().Add(SymmetricHandshakeAckTimeout))
|
||||
c, err := l.Accept()
|
||||
defer l.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &underlayTCP{writeMtx: &sync.Mutex{}, Conn: c}, nil
|
||||
}
|
||||
|
||||
func dialTCP(host string, port int) (*underlayTCP, error) {
|
||||
c, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), SymmetricHandshakeAckTimeout)
|
||||
if err != nil {
|
||||
fmt.Printf("Dial %s:%d error:%s", host, port, err)
|
||||
return nil, err
|
||||
}
|
||||
return &underlayTCP{writeMtx: &sync.Mutex{}, Conn: c}, nil
|
||||
}
|
||||
92
underlay_tcp6.go
Normal file
92
underlay_tcp6.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type underlayTCP6 struct {
|
||||
listener net.Listener
|
||||
writeMtx *sync.Mutex
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (conn *underlayTCP6) Protocol() string {
|
||||
return "tcp6"
|
||||
}
|
||||
|
||||
func (conn *underlayTCP6) ReadBuffer() (*openP2PHeader, []byte, error) {
|
||||
headBuf := make([]byte, openP2PHeaderSize)
|
||||
_, err := io.ReadFull(conn, headBuf)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
head, err := decodeHeader(headBuf)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
dataBuf := make([]byte, head.DataLen)
|
||||
_, err = io.ReadFull(conn, dataBuf)
|
||||
return head, dataBuf, err
|
||||
}
|
||||
|
||||
func (conn *underlayTCP6) WriteBytes(mainType uint16, subType uint16, data []byte) error {
|
||||
writeBytes := append(encodeHeader(mainType, subType, uint32(len(data))), data...)
|
||||
conn.writeMtx.Lock()
|
||||
_, err := conn.Write(writeBytes)
|
||||
conn.writeMtx.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (conn *underlayTCP6) WriteBuffer(data []byte) error {
|
||||
conn.writeMtx.Lock()
|
||||
_, err := conn.Write(data)
|
||||
conn.writeMtx.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (conn *underlayTCP6) WriteMessage(mainType uint16, subType uint16, packet interface{}) error {
|
||||
// TODO: call newMessage
|
||||
data, err := json.Marshal(packet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeBytes := append(encodeHeader(mainType, subType, uint32(len(data))), data...)
|
||||
conn.writeMtx.Lock()
|
||||
_, err = conn.Write(writeBytes)
|
||||
conn.writeMtx.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (conn *underlayTCP6) Close() error {
|
||||
return conn.Conn.Close()
|
||||
}
|
||||
|
||||
func listenTCP6(port int, idleTimeout time.Duration) (*underlayTCP6, error) {
|
||||
addr, _ := net.ResolveTCPAddr("tcp6", fmt.Sprintf("0.0.0.0:%d", port))
|
||||
l, err := net.ListenTCP("tcp6", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer l.Close()
|
||||
l.SetDeadline(time.Now().Add(SymmetricHandshakeAckTimeout))
|
||||
c, err := l.Accept()
|
||||
defer l.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &underlayTCP6{writeMtx: &sync.Mutex{}, Conn: c}, nil
|
||||
}
|
||||
|
||||
func dialTCP6(host string, port int) (*underlayTCP6, error) {
|
||||
c, err := net.DialTimeout("tcp6", fmt.Sprintf("%s:%d", host, port), SymmetricHandshakeAckTimeout)
|
||||
if err != nil {
|
||||
fmt.Printf("Dial %s:%d error:%s", host, port, err)
|
||||
return nil, err
|
||||
}
|
||||
return &underlayTCP6{writeMtx: &sync.Mutex{}, Conn: c}, nil
|
||||
}
|
||||
34
update.go
34
update.go
@@ -17,8 +17,8 @@ import (
|
||||
)
|
||||
|
||||
func update() {
|
||||
gLog.Println(LevelINFO, "update start")
|
||||
defer gLog.Println(LevelINFO, "update end")
|
||||
gLog.Println(LvINFO, "update start")
|
||||
defer gLog.Println(LvINFO, "update end")
|
||||
c := http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
@@ -29,43 +29,43 @@ func update() {
|
||||
goarch := runtime.GOARCH
|
||||
rsp, err := c.Get(fmt.Sprintf("https://openp2p.cn:27183/api/v1/update?fromver=%s&os=%s&arch=%s", OpenP2PVersion, goos, goarch))
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "update:query update list failed:", err)
|
||||
gLog.Println(LvERROR, "update:query update list failed:", err)
|
||||
return
|
||||
}
|
||||
defer rsp.Body.Close()
|
||||
if rsp.StatusCode != http.StatusOK {
|
||||
gLog.Println(LevelERROR, "get update info error:", rsp.Status)
|
||||
gLog.Println(LvERROR, "get update info error:", rsp.Status)
|
||||
return
|
||||
}
|
||||
rspBuf, err := ioutil.ReadAll(rsp.Body)
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "update:read update list failed:", err)
|
||||
gLog.Println(LvERROR, "update:read update list failed:", err)
|
||||
return
|
||||
}
|
||||
updateInfo := UpdateInfo{}
|
||||
err = json.Unmarshal(rspBuf, &updateInfo)
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, rspBuf, " update info decode error:", err)
|
||||
gLog.Println(LvERROR, rspBuf, " update info decode error:", err)
|
||||
return
|
||||
}
|
||||
if updateInfo.Error != 0 {
|
||||
gLog.Println(LevelERROR, "update error:", updateInfo.Error, updateInfo.ErrorDetail)
|
||||
gLog.Println(LvERROR, "update error:", updateInfo.Error, updateInfo.ErrorDetail)
|
||||
return
|
||||
}
|
||||
err = updateFile(updateInfo.Url, "", "openp2p")
|
||||
if err != nil {
|
||||
gLog.Println(LevelERROR, "update: download failed:", err)
|
||||
gLog.Println(LvERROR, "update: download failed:", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// todo rollback on error
|
||||
func updateFile(url string, checksum string, dst string) error {
|
||||
gLog.Println(LevelINFO, "download ", url)
|
||||
gLog.Println(LvINFO, "download ", url)
|
||||
tmpFile := filepath.Dir(os.Args[0]) + "/openp2p.tmp"
|
||||
output, err := os.OpenFile(tmpFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0776)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "OpenFile %s error:%s", tmpFile, err)
|
||||
gLog.Printf(LvERROR, "OpenFile %s error:%s", tmpFile, err)
|
||||
return err
|
||||
}
|
||||
tr := &http.Transport{
|
||||
@@ -74,31 +74,31 @@ func updateFile(url string, checksum string, dst string) error {
|
||||
client := &http.Client{Transport: tr}
|
||||
response, err := client.Get(url)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "download url %s error:%s", url, err)
|
||||
gLog.Printf(LvERROR, "download url %s error:%s", url, err)
|
||||
output.Close()
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
n, err := io.Copy(output, response.Body)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "io.Copy error:%s", err)
|
||||
gLog.Printf(LvERROR, "io.Copy error:%s", err)
|
||||
output.Close()
|
||||
return err
|
||||
}
|
||||
output.Sync()
|
||||
output.Close()
|
||||
gLog.Println(LevelINFO, "download ", url, " ok")
|
||||
gLog.Printf(LevelINFO, "size: %d bytes", n)
|
||||
gLog.Println(LvINFO, "download ", url, " ok")
|
||||
gLog.Printf(LvINFO, "size: %d bytes", n)
|
||||
|
||||
err = os.Rename(os.Args[0], os.Args[0]+"0")
|
||||
if err != nil && os.IsExist(err) {
|
||||
gLog.Printf(LevelINFO, " rename %s error:%s", os.Args[0], err)
|
||||
gLog.Printf(LvINFO, " rename %s error:%s", os.Args[0], err)
|
||||
}
|
||||
// extract
|
||||
gLog.Println(LevelINFO, "extract files")
|
||||
gLog.Println(LvINFO, "extract files")
|
||||
err = extract(filepath.Dir(os.Args[0]), tmpFile)
|
||||
if err != nil {
|
||||
gLog.Printf(LevelERROR, "extract error:%s. revert rename", err)
|
||||
gLog.Printf(LvERROR, "extract error:%s. revert rename", err)
|
||||
os.Rename(os.Args[0]+"0", os.Args[0])
|
||||
return err
|
||||
}
|
||||
|
||||
404
upnp.go
Normal file
404
upnp.go
Normal file
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
Taken from taipei-torrent
|
||||
|
||||
Just enough UPnP to be able to forward ports
|
||||
*/
|
||||
package main
|
||||
|
||||
// BUG(jae): TODO: use syscalls to get actual ourIP. http://pastebin.com/9exZG4rh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type upnpNAT struct {
|
||||
serviceURL string
|
||||
ourIP string
|
||||
urnDomain string
|
||||
}
|
||||
|
||||
// protocol is either "udp" or "tcp"
|
||||
type NAT interface {
|
||||
GetExternalAddress() (addr net.IP, err error)
|
||||
AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error)
|
||||
DeletePortMapping(protocol string, externalPort, internalPort int) (err error)
|
||||
}
|
||||
|
||||
func Discover() (nat NAT, err error) {
|
||||
ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
conn, err := net.ListenPacket("udp4", ":0")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
socket := conn.(*net.UDPConn)
|
||||
defer socket.Close() // nolint: errcheck
|
||||
|
||||
if err := socket.SetDeadline(time.Now().Add(3 * time.Second)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
st := "InternetGatewayDevice:1"
|
||||
|
||||
buf := bytes.NewBufferString(
|
||||
"M-SEARCH * HTTP/1.1\r\n" +
|
||||
"HOST: 239.255.255.250:1900\r\n" +
|
||||
"ST: ssdp:all\r\n" +
|
||||
"MAN: \"ssdp:discover\"\r\n" +
|
||||
"MX: 2\r\n\r\n")
|
||||
message := buf.Bytes()
|
||||
answerBytes := make([]byte, 1024)
|
||||
for i := 0; i < 3; i++ {
|
||||
_, err = socket.WriteToUDP(message, ssdp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var n int
|
||||
_, _, err = socket.ReadFromUDP(answerBytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
n, _, err = socket.ReadFromUDP(answerBytes)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
answer := string(answerBytes[0:n])
|
||||
if !strings.Contains(answer, st) {
|
||||
continue
|
||||
}
|
||||
// HTTP header field names are case-insensitive.
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
||||
locString := "\r\nlocation:"
|
||||
answer = strings.ToLower(answer)
|
||||
locIndex := strings.Index(answer, locString)
|
||||
if locIndex < 0 {
|
||||
continue
|
||||
}
|
||||
loc := answer[locIndex+len(locString):]
|
||||
endIndex := strings.Index(loc, "\r\n")
|
||||
if endIndex < 0 {
|
||||
continue
|
||||
}
|
||||
locURL := strings.TrimSpace(loc[0:endIndex])
|
||||
var serviceURL, urnDomain string
|
||||
serviceURL, urnDomain, err = getServiceURL(locURL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var ourIP net.IP
|
||||
ourIP, err = localIPv4()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP.String(), urnDomain: urnDomain}
|
||||
return
|
||||
}
|
||||
}
|
||||
err = errors.New("UPnP port discovery failed")
|
||||
return
|
||||
}
|
||||
|
||||
type Envelope struct {
|
||||
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
|
||||
Soap *SoapBody
|
||||
}
|
||||
type SoapBody struct {
|
||||
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
|
||||
ExternalIP *ExternalIPAddressResponse
|
||||
}
|
||||
|
||||
type ExternalIPAddressResponse struct {
|
||||
XMLName xml.Name `xml:"GetExternalIPAddressResponse"`
|
||||
IPAddress string `xml:"NewExternalIPAddress"`
|
||||
}
|
||||
|
||||
type ExternalIPAddress struct {
|
||||
XMLName xml.Name `xml:"NewExternalIPAddress"`
|
||||
IP string
|
||||
}
|
||||
|
||||
type UPNPService struct {
|
||||
ServiceType string `xml:"serviceType"`
|
||||
ControlURL string `xml:"controlURL"`
|
||||
}
|
||||
|
||||
type DeviceList struct {
|
||||
Device []Device `xml:"device"`
|
||||
}
|
||||
|
||||
type ServiceList struct {
|
||||
Service []UPNPService `xml:"service"`
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
XMLName xml.Name `xml:"device"`
|
||||
DeviceType string `xml:"deviceType"`
|
||||
DeviceList DeviceList `xml:"deviceList"`
|
||||
ServiceList ServiceList `xml:"serviceList"`
|
||||
}
|
||||
|
||||
type Root struct {
|
||||
Device Device
|
||||
}
|
||||
|
||||
func getChildDevice(d *Device, deviceType string) *Device {
|
||||
dl := d.DeviceList.Device
|
||||
for i := 0; i < len(dl); i++ {
|
||||
if strings.Contains(dl[i].DeviceType, deviceType) {
|
||||
return &dl[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getChildService(d *Device, serviceType string) *UPNPService {
|
||||
sl := d.ServiceList.Service
|
||||
for i := 0; i < len(sl); i++ {
|
||||
if strings.Contains(sl[i].ServiceType, serviceType) {
|
||||
return &sl[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func localIPv4() (net.IP, error) {
|
||||
tt, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, t := range tt {
|
||||
aa, err := t.Addrs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, a := range aa {
|
||||
ipnet, ok := a.(*net.IPNet)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
v4 := ipnet.IP.To4()
|
||||
if v4 == nil || v4[0] == 127 { // loopback address
|
||||
continue
|
||||
}
|
||||
return v4, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("cannot find local IP address")
|
||||
}
|
||||
|
||||
func getServiceURL(rootURL string) (url, urnDomain string, err error) {
|
||||
r, err := http.Get(rootURL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer r.Body.Close() // nolint: errcheck
|
||||
|
||||
if r.StatusCode >= 400 {
|
||||
err = errors.New(fmt.Sprint(r.StatusCode))
|
||||
return
|
||||
}
|
||||
var root Root
|
||||
err = xml.NewDecoder(r.Body).Decode(&root)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
a := &root.Device
|
||||
if !strings.Contains(a.DeviceType, "InternetGatewayDevice:1") {
|
||||
err = errors.New("No InternetGatewayDevice")
|
||||
return
|
||||
}
|
||||
|
||||
b := getChildDevice(a, "WANDevice:1")
|
||||
if b == nil {
|
||||
err = errors.New("No WANDevice")
|
||||
return
|
||||
}
|
||||
c := getChildDevice(b, "WANConnectionDevice:1")
|
||||
if c == nil {
|
||||
err = errors.New("No WANConnectionDevice")
|
||||
return
|
||||
}
|
||||
d := getChildService(c, "WANIPConnection:1")
|
||||
if d == nil {
|
||||
// Some routers don't follow the UPnP spec, and put WanIPConnection under WanDevice,
|
||||
// instead of under WanConnectionDevice
|
||||
d = getChildService(b, "WANIPConnection:1")
|
||||
|
||||
if d == nil {
|
||||
d = getChildService(c, "WANPPPConnection:1")
|
||||
if d == nil {
|
||||
err = errors.New("No WANIPConnection or WANPPPConnection")
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// Extract the domain name, which isn't always 'schemas-upnp-org'
|
||||
urnDomain = strings.Split(d.ServiceType, ":")[1]
|
||||
url = combineURL(rootURL, d.ControlURL)
|
||||
return
|
||||
}
|
||||
|
||||
func combineURL(rootURL, subURL string) string {
|
||||
protocolEnd := "://"
|
||||
protoEndIndex := strings.Index(rootURL, protocolEnd)
|
||||
a := rootURL[protoEndIndex+len(protocolEnd):]
|
||||
rootIndex := strings.Index(a, "/")
|
||||
return rootURL[0:protoEndIndex+len(protocolEnd)+rootIndex] + subURL
|
||||
}
|
||||
|
||||
func soapRequest(url, function, message, domain string) (r *http.Response, err error) {
|
||||
fullMessage := "<?xml version=\"1.0\" ?>" +
|
||||
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" +
|
||||
"<s:Body>" + message + "</s:Body></s:Envelope>"
|
||||
|
||||
req, err := http.NewRequest("POST", url, strings.NewReader(fullMessage))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "text/xml ; charset=\"utf-8\"")
|
||||
req.Header.Set("User-Agent", "Darwin/10.0.0, UPnP/1.0, MiniUPnPc/1.3")
|
||||
//req.Header.Set("Transfer-Encoding", "chunked")
|
||||
req.Header.Set("SOAPAction", "\"urn:"+domain+":service:WANIPConnection:1#"+function+"\"")
|
||||
req.Header.Set("Connection", "Close")
|
||||
req.Header.Set("Cache-Control", "no-cache")
|
||||
req.Header.Set("Pragma", "no-cache")
|
||||
|
||||
// log.Stderr("soapRequest ", req)
|
||||
|
||||
r, err = http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
/*if r.Body != nil {
|
||||
defer r.Body.Close()
|
||||
}*/
|
||||
|
||||
if r.StatusCode >= 400 {
|
||||
// log.Stderr(function, r.StatusCode)
|
||||
err = errors.New("Error " + strconv.Itoa(r.StatusCode) + " for " + function)
|
||||
r = nil
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type statusInfo struct {
|
||||
externalIpAddress string
|
||||
}
|
||||
|
||||
func (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {
|
||||
|
||||
message := "<u:GetExternalIPAddress xmlns:u=\"urn:" + n.urnDomain + ":service:WANIPConnection:1\">\r\n" +
|
||||
"</u:GetExternalIPAddress>"
|
||||
|
||||
var response *http.Response
|
||||
response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)
|
||||
if response != nil {
|
||||
defer response.Body.Close() // nolint: errcheck
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var envelope Envelope
|
||||
data, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
reader := bytes.NewReader(data)
|
||||
err = xml.NewDecoder(reader).Decode(&envelope)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
info = statusInfo{envelope.Soap.ExternalIP.IPAddress}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetExternalAddress returns an external IP. If GetExternalIPAddress action
|
||||
// fails or IP returned is invalid, GetExternalAddress returns an error.
|
||||
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
|
||||
info, err := n.getExternalIPAddress()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
addr = net.ParseIP(info.externalIpAddress)
|
||||
if addr == nil {
|
||||
err = fmt.Errorf("Failed to parse IP: %v", info.externalIpAddress)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
|
||||
// A single concatenation would break ARM compilation.
|
||||
message := "<u:AddPortMapping xmlns:u=\"urn:" + n.urnDomain + ":service:WANIPConnection:1\">\r\n" +
|
||||
"<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort)
|
||||
message += "</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>"
|
||||
message += "<NewInternalPort>" + strconv.Itoa(internalPort) + "</NewInternalPort>" +
|
||||
"<NewInternalClient>" + n.ourIP + "</NewInternalClient>" +
|
||||
"<NewEnabled>1</NewEnabled><NewPortMappingDescription>"
|
||||
message += description +
|
||||
"</NewPortMappingDescription><NewLeaseDuration>" + strconv.Itoa(timeout) +
|
||||
"</NewLeaseDuration></u:AddPortMapping>"
|
||||
|
||||
var response *http.Response
|
||||
response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain)
|
||||
if response != nil {
|
||||
defer response.Body.Close() // nolint: errcheck
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: check response to see if the port was forwarded
|
||||
// log.Println(message, response)
|
||||
// JAE:
|
||||
// body, err := ioutil.ReadAll(response.Body)
|
||||
// fmt.Println(string(body), err)
|
||||
mappedExternalPort = externalPort
|
||||
_ = response
|
||||
return
|
||||
}
|
||||
|
||||
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
|
||||
|
||||
message := "<u:DeletePortMapping xmlns:u=\"urn:" + n.urnDomain + ":service:WANIPConnection:1\">\r\n" +
|
||||
"<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort) +
|
||||
"</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>" +
|
||||
"</u:DeletePortMapping>"
|
||||
|
||||
var response *http.Response
|
||||
response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain)
|
||||
if response != nil {
|
||||
defer response.Body.Close() // nolint: errcheck
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: check response to see if the port was deleted
|
||||
// log.Println(message, response)
|
||||
_ = response
|
||||
return
|
||||
}
|
||||
32
util_darwin.go
Normal file
32
util_darwin.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultInstallPath = "/usr/local/openp2p"
|
||||
defaultBinName = "openp2p"
|
||||
)
|
||||
|
||||
func getOsName() (osName string) {
|
||||
output := execOutput("sw_vers", "-productVersion")
|
||||
osName = "Mac OS X " + strings.TrimSpace(output)
|
||||
return
|
||||
}
|
||||
|
||||
func setRLimit() error {
|
||||
var limit syscall.Rlimit
|
||||
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
|
||||
return err
|
||||
}
|
||||
limit.Cur = 10240
|
||||
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setFirewall() {
|
||||
}
|
||||
72
util_linux.go
Normal file
72
util_linux.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultInstallPath = "/usr/local/openp2p"
|
||||
defaultBinName = "openp2p"
|
||||
)
|
||||
|
||||
func getOsName() (osName string) {
|
||||
var sysnamePath string
|
||||
sysnamePath = "/etc/redhat-release"
|
||||
_, err := os.Stat(sysnamePath)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
str := "PRETTY_NAME="
|
||||
f, err := os.Open("/etc/os-release")
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
str = "DISTRIB_ID="
|
||||
f, err = os.Open("/etc/openwrt_release")
|
||||
}
|
||||
if err == nil {
|
||||
buf := bufio.NewReader(f)
|
||||
for {
|
||||
line, err := buf.ReadString('\n')
|
||||
if err == nil {
|
||||
line = strings.TrimSpace(line)
|
||||
pos := strings.Count(line, str)
|
||||
if pos > 0 {
|
||||
len1 := len([]rune(str)) + 1
|
||||
rs := []rune(line)
|
||||
osName = string(rs[len1 : (len(rs))-1])
|
||||
break
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buff, err := ioutil.ReadFile(sysnamePath)
|
||||
if err == nil {
|
||||
osName = string(bytes.TrimSpace(buff))
|
||||
}
|
||||
}
|
||||
if osName == "" {
|
||||
osName = "Linux"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setRLimit() error {
|
||||
var limit syscall.Rlimit
|
||||
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
|
||||
return err
|
||||
}
|
||||
limit.Max = 1024 * 1024
|
||||
limit.Cur = limit.Max
|
||||
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setFirewall() {
|
||||
}
|
||||
53
util_windows.go
Normal file
53
util_windows.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultInstallPath = "C:\\Program Files\\OpenP2P"
|
||||
defaultBinName = "openp2p.exe"
|
||||
)
|
||||
|
||||
func getOsName() (osName string) {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE|registry.WOW64_64KEY)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer k.Close()
|
||||
pn, _, err := k.GetStringValue("ProductName")
|
||||
if err == nil {
|
||||
osName = pn
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setRLimit() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func setFirewall() {
|
||||
fullPath, err := filepath.Abs(os.Args[0])
|
||||
if err != nil {
|
||||
gLog.Println(LvERROR, "add firewall error:", err)
|
||||
return
|
||||
}
|
||||
isXP := false
|
||||
osName := getOsName()
|
||||
if strings.Contains(osName, "XP") || strings.Contains(osName, "2003") {
|
||||
isXP = true
|
||||
}
|
||||
if isXP {
|
||||
exec.Command("cmd.exe", `/c`, fmt.Sprintf(`netsh firewall del allowedprogram "%s"`, fullPath)).Run()
|
||||
exec.Command("cmd.exe", `/c`, fmt.Sprintf(`netsh firewall add allowedprogram "%s" "%s" ENABLE`, ProducnName, fullPath)).Run()
|
||||
} else { // win7 or later
|
||||
exec.Command("cmd.exe", `/c`, fmt.Sprintf(`netsh advfirewall firewall del rule name="%s"`, ProducnName)).Run()
|
||||
exec.Command("cmd.exe", `/c`, fmt.Sprintf(`netsh advfirewall firewall add rule name="%s" dir=in action=allow program="%s" enable=yes`, ProducnName, fullPath)).Run()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user