Compare commits

...

2 Commits

Author SHA1 Message Date
TenderIronh
b2d35c6f97 1.5.5 2022-04-27 23:26:23 +08:00
TenderIronh
037f3cc34e doc 2022-04-07 23:22:46 +08:00
28 changed files with 1819 additions and 617 deletions

View File

@@ -95,6 +95,7 @@ Windows默认会阻止没有花钱买它家证书签名过的程序选择“
服务端有个调度模型根据带宽、ping值、稳定性、服务时长尽可能地使共享节点均匀地提供服务。连接共享节点使用TOTP密码hmac-sha256算法校验它是一次性密码和我们平时使用的手机验证码或银行密码器一样的原理。 服务端有个调度模型根据带宽、ping值、稳定性、服务时长尽可能地使共享节点均匀地提供服务。连接共享节点使用TOTP密码hmac-sha256算法校验它是一次性密码和我们平时使用的手机验证码或银行密码器一样的原理。
## 编译 ## 编译
go version go1.18.1+
cd到代码根目录执行 cd到代码根目录执行
``` ```
export GOPROXY=https://goproxy.io,direct export GOPROXY=https://goproxy.io,direct
@@ -105,15 +106,15 @@ go build
## TODO ## TODO
近期计划: 近期计划:
1. 支持IPv6 1. 支持IPv6
2. 支持随系统自动启动,安装成系统服务 2. 支持随系统自动启动,安装成系统服务(100%)
3. 提供一些免费服务器给特别差的网络,如广电网络 3. 提供一些免费服务器给特别差的网络,如广电网络(100%)
4. 建立网站用户可以在网站管理所有P2PApp和设备。查看设备在线状态升级增删查改重启P2PApp等 4. 建立网站用户可以在网站管理所有P2PApp和设备。查看设备在线状态升级增删查改重启P2PApp等(100%)
5. 建立公众号用户可在微信公众号管理所有P2PApp和设备 5. 建立公众号用户可在微信公众号管理所有P2PApp和设备
6. 客户端提供WebUI 6. 客户端提供WebUI
7. 支持自有服务器高并发连接 7. 支持自有服务器高并发连接
8. 共享节点调度模型优化,对不同的运营商优化 8. 共享节点调度模型优化,对不同的运营商优化
9. 方便二次开发提供API和lib 9. 方便二次开发提供API和lib
10. 应用层支持UDP协议实现很简单但UDP应用较少暂不急 10. 应用层支持UDP协议实现很简单但UDP应用较少暂不急(100%)
11. 底层通信支持KCP协议目前仅支持QuicKCP专门对延时优化被游戏加速器广泛使用可以牺牲一定的带宽降低延时 11. 底层通信支持KCP协议目前仅支持QuicKCP专门对延时优化被游戏加速器广泛使用可以牺牲一定的带宽降低延时
12. 支持Android系统让旧手机焕发青春变成移动网关 12. 支持Android系统让旧手机焕发青春变成移动网关
13. 支持Windows网上邻居共享文件 13. 支持Windows网上邻居共享文件

View File

@@ -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. 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 ## Build
go version go1.18.1+
cd root directory of the socure code and execute cd root directory of the socure code and execute
``` ```
export GOPROXY=https://goproxy.io,direct export GOPROXY=https://goproxy.io,direct
@@ -113,17 +114,17 @@ go build
## TODO ## TODO
Short-Term: Short-Term:
1. Support IPv6. 1. Support IPv6.
2. Support auto run when system boot, setup system service. 2. Support auto run when system boot, setup system service.(100%)
3. Provide free servers to some low-performance network. 3. Provide free servers to some low-performance network.(100%)
4. Build website, users can manage all P2PApp and devices via it. View devices' online status, upgrade, restart or CURD P2PApp . 4. Build website, users can manage all P2PApp and devices via it. View devices' online status, upgrade, restart or CURD P2PApp .(100%)
5. Provide wechat official account, user can manage P2PApp nodes and deivce as same as website. 5. Provide wechat official account, user can manage P2PApp nodes and deivce as same as website.
6. Provide WebUI on client side. 6. Provide WebUI on client side.
7. Support high concurrency on server side. 7. Support high concurrency on server side.
8. Optimize our share scheduling model for different network operators. 8. Optimize our share scheduling model for different network operators.
9. Provide REST APIs and libary for secondary development. 9. Provide REST APIs and libary for secondary development.
10. Support UDP at application layer, it is easy to implement but not urgent due to only a few applicaitons using UDP protocol. 10. Support UDP at application layer, it is easy to implement but not urgent due to only a few applicaitons using UDP protocol.(100%)
11. Support KCP protocol underlay, currently support Quic only. KCP focus on delay optimization,which has been widely used as game accelerator,it can sacrifice part of bandwidth to reduce timelag. 11. Support KCP protocol underlay, currently support Quic only. KCP focus on delay optimization,which has been widely used as game accelerator,it can sacrifice part of bandwidth to reduce timelag.
12. Support Android platform, let the phones to be mobile gateway . 12. Support Android platform, let the phones to be mobile gateway.
13. Support SMB Windows neighborhood. 13. Support SMB Windows neighborhood.
14. Direct connection on intranet, for testing. 14. Direct connection on intranet, for testing.

View File

@@ -12,6 +12,8 @@ import (
"net/http" "net/http"
"os" "os"
"os/exec" "os/exec"
"strconv"
"strings"
"time" "time"
) )
@@ -77,6 +79,7 @@ func pkcs7UnPadding(origData []byte, dataLen int) ([]byte, error) {
return origData[:(dataLen - unPadLen)], nil return origData[:(dataLen - unPadLen)], nil
} }
// AES-CBC
func encryptBytes(key []byte, out, in []byte, plainLen int) ([]byte, error) { func encryptBytes(key []byte, out, in []byte, plainLen int) ([]byte, error) {
if len(key) == 0 { if len(key) == 0 {
return in[:plainLen], nil return in[:plainLen], nil
@@ -122,20 +125,20 @@ func netInfo() *NetInfo {
client := &http.Client{Transport: tr, Timeout: time.Second * 10} client := &http.Client{Transport: tr, Timeout: time.Second * 10}
r, err := client.Get("https://ifconfig.co/json") r, err := client.Get("https://ifconfig.co/json")
if err != nil { if err != nil {
gLog.Println(LevelINFO, "netInfo error:", err) gLog.Println(LvINFO, "netInfo error:", err)
continue continue
} }
defer r.Body.Close() defer r.Body.Close()
buf := make([]byte, 1024*64) buf := make([]byte, 1024*64)
n, err := r.Body.Read(buf) n, err := r.Body.Read(buf)
if err != nil { if err != nil {
gLog.Println(LevelINFO, "netInfo error:", err) gLog.Println(LvINFO, "netInfo error:", err)
continue continue
} }
rsp := NetInfo{} rsp := NetInfo{}
err = json.Unmarshal(buf[:n], &rsp) err = json.Unmarshal(buf[:n], &rsp)
if err != nil { if err != nil {
gLog.Printf(LevelERROR, "wrong NetInfo:%s", err) gLog.Printf(LvERROR, "wrong NetInfo:%s", err)
continue continue
} }
return &rsp return &rsp
@@ -158,3 +161,33 @@ func defaultNodeName() string {
} }
return name 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
}

View File

@@ -43,3 +43,29 @@ func TestAESCBC(t *testing.T) {
func TestNetInfo(t *testing.T) { func TestNetInfo(t *testing.T) {
log.Println(netInfo()) 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)
}

View File

@@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"flag" "flag"
"io/ioutil" "io/ioutil"
"os"
"sync" "sync"
"time" "time"
) )
@@ -23,8 +24,12 @@ type AppConfig struct {
PeerUser string PeerUser string
Enabled int // default:1 Enabled int // default:1
// runtime info // runtime info
peerVersion string
peerToken uint64 peerToken uint64
peerNatType int peerNatType int
hasIPv4 int
IPv6 string
hasUPNPorNATPMP int
peerIP string peerIP string
peerConeNatPort int peerConeNatPort int
retryNum int retryNum int
@@ -61,7 +66,7 @@ func (c *Config) add(app AppConfig, override bool) {
c.mtx.Lock() c.mtx.Lock()
defer c.mtx.Unlock() defer c.mtx.Unlock()
if app.SrcPort == 0 || app.DstPort == 0 { if app.SrcPort == 0 || app.DstPort == 0 {
gLog.Println(LevelERROR, "invalid app ", app) gLog.Println(LvERROR, "invalid app ", app)
return return
} }
if override { if override {
@@ -95,7 +100,7 @@ func (c *Config) save() {
data, _ := json.MarshalIndent(c, "", " ") data, _ := json.MarshalIndent(c, "", " ")
err := ioutil.WriteFile("config.json", data, 0644) err := ioutil.WriteFile("config.json", data, 0644)
if err != nil { 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) err = json.Unmarshal(data, &c)
if err != nil { if err != nil {
gLog.Println(LevelERROR, "parse config.json error:", err) gLog.Println(LvERROR, "parse config.json error:", err)
} }
return err return err
} }
@@ -139,16 +144,19 @@ func (c *Config) setShareBandwidth(bw int) {
type NetworkConfig struct { type NetworkConfig struct {
// local info // local info
Token uint64 Token uint64
Node string Node string
User string User string
localIP string localIP string
ipv6 string ipv6 string
mac string mac string
os string os string
publicIP string publicIP string
natType int natType int
ShareBandwidth int hasIPv4 int
IPv6 string
hasUPNPorNATPMP int
ShareBandwidth int
// server info // server info
ServerHost string ServerHost string
ServerPort int ServerPort int
@@ -156,22 +164,28 @@ type NetworkConfig struct {
UDPPort2 int UDPPort2 int
} }
func parseParams() { func parseParams(subCommand string) {
serverHost := flag.String("serverhost", "api.openp2p.cn", "server host ") 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 // serverHost := flag.String("serverhost", "127.0.0.1", "server host ") // for debug
node := flag.String("node", "", "node name. 8-31 characters") token := fset.Uint64("token", 0, "token")
token := flag.Uint64("token", 0, "token") node := fset.String("node", "", "node name. 8-31 characters. if not set, it will be hostname")
peerNode := flag.String("peernode", "", "peer node name that you want to connect") peerNode := fset.String("peernode", "", "peer node name that you want to connect")
dstIP := flag.String("dstip", "127.0.0.1", "destination ip ") dstIP := fset.String("dstip", "127.0.0.1", "destination ip ")
dstPort := flag.Int("dstport", 0, "destination port ") dstPort := fset.Int("dstport", 0, "destination port ")
srcPort := flag.Int("srcport", 0, "source port ") srcPort := fset.Int("srcport", 0, "source port ")
protocol := flag.String("protocol", "tcp", "tcp or udp") protocol := fset.String("protocol", "tcp", "tcp or udp")
appName := flag.String("appname", "", "app name") appName := fset.String("appname", "", "app name")
shareBandwidth := flag.Int("sharebandwidth", 10, "N mbps share bandwidth limit, private network no limit") shareBandwidth := fset.Int("sharebandwidth", 10, "N mbps share bandwidth limit, private network no limit")
daemonMode := flag.Bool("d", false, "daemonMode") daemonMode := fset.Bool("d", false, "daemonMode")
notVerbose := flag.Bool("nv", false, "not log console") notVerbose := fset.Bool("nv", false, "not log console")
logLevel := flag.Int("loglevel", 1, "0:debug 1:info 2:warn 3:error") newconfig := fset.Bool("newconfig", false, "not load existing config.json")
flag.Parse() 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 := AppConfig{Enabled: 1}
config.PeerNode = *peerNode config.PeerNode = *peerNode
@@ -180,14 +194,17 @@ func parseParams() {
config.SrcPort = *srcPort config.SrcPort = *srcPort
config.Protocol = *protocol config.Protocol = *protocol
config.AppName = *appName config.AppName = *appName
gConf.load() if !*newconfig {
gConf.load() // load old config. otherwise will clear all apps
}
gConf.LogLevel = *logLevel
if config.SrcPort != 0 { if config.SrcPort != 0 {
gConf.add(config, true) gConf.add(config, true)
} }
// gConf.mtx.Lock() // when calling this func it's single-thread no lock // gConf.mtx.Lock() // when calling this func it's single-thread no lock
gConf.daemonMode = *daemonMode gConf.daemonMode = *daemonMode
// spec paramters in commandline will always be used // spec paramters in commandline will always be used
flag.Visit(func(f *flag.Flag) { fset.Visit(func(f *flag.Flag) {
if f.Name == "sharebandwidth" { if f.Name == "sharebandwidth" {
gConf.Network.ShareBandwidth = *shareBandwidth gConf.Network.ShareBandwidth = *shareBandwidth
} }
@@ -205,16 +222,20 @@ func parseParams() {
if gConf.Network.ServerHost == "" { if gConf.Network.ServerHost == "" {
gConf.Network.ServerHost = *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 { if *token != 0 {
gConf.Network.Token = *token 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 { if gConf.LogLevel == IntValueNotSet {
gConf.LogLevel = *logLevel gConf.LogLevel = *logLevel
} }

188
daemon.go
View File

@@ -1,13 +1,9 @@
package main package main
import ( import (
"flag"
"fmt" "fmt"
"io"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"strings"
"time" "time"
"github.com/kardianos/service" "github.com/kardianos/service"
@@ -19,34 +15,34 @@ type daemon struct {
} }
func (d *daemon) Start(s service.Service) error { func (d *daemon) Start(s service.Service) error {
gLog.Println(LevelINFO, "daemon start") gLog.Println(LvINFO, "daemon start")
return nil return nil
} }
func (d *daemon) Stop(s service.Service) error { func (d *daemon) Stop(s service.Service) error {
gLog.Println(LevelINFO, "service stop") gLog.Println(LvINFO, "service stop")
d.running = false d.running = false
if d.proc != nil { if d.proc != nil {
gLog.Println(LevelINFO, "stop worker") gLog.Println(LvINFO, "stop worker")
d.proc.Kill() d.proc.Kill()
} }
if service.Interactive() { if service.Interactive() {
gLog.Println(LevelINFO, "stop daemon") gLog.Println(LvINFO, "stop daemon")
os.Exit(0) os.Exit(0)
} }
return nil return nil
} }
func (d *daemon) run() { func (d *daemon) run() {
gLog.Println(LevelINFO, "daemon run start") gLog.Println(LvINFO, "daemon run start")
defer gLog.Println(LevelINFO, "daemon run end") defer gLog.Println(LvINFO, "daemon run end")
d.running = true d.running = true
binPath, _ := os.Executable() binPath, _ := os.Executable()
mydir, err := os.Getwd() mydir, err := os.Getwd()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
gLog.Println(LevelINFO, mydir) gLog.Println(LvINFO, mydir)
conf := &service.Config{ conf := &service.Config{
Name: ProducnName, Name: ProducnName,
DisplayName: ProducnName, DisplayName: ProducnName,
@@ -71,14 +67,14 @@ func (d *daemon) run() {
dumpFile := filepath.Join("log", "dump.log") dumpFile := filepath.Join("log", "dump.log")
f, err := os.Create(filepath.Join(tmpDump)) f, err := os.Create(filepath.Join(tmpDump))
if err != nil { if err != nil {
gLog.Printf(LevelERROR, "start worker error:%s", err) gLog.Printf(LvERROR, "start worker error:%s", err)
return 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}} execSpec := &os.ProcAttr{Env: append(os.Environ(), "GOTRACEBACK=crash"), Files: []*os.File{os.Stdin, os.Stdout, f}}
p, err := os.StartProcess(binPath, args, execSpec) p, err := os.StartProcess(binPath, args, execSpec)
if err != nil { if err != nil {
gLog.Printf(LevelERROR, "start worker error:%s", err) gLog.Printf(LvERROR, "start worker error:%s", err)
return return
} }
d.proc = p d.proc = p
@@ -87,12 +83,12 @@ func (d *daemon) run() {
time.Sleep(time.Second) time.Sleep(time.Second)
err = os.Rename(tmpDump, dumpFile) err = os.Rename(tmpDump, dumpFile)
if err != nil { if err != nil {
gLog.Printf(LevelERROR, "rename dump error:%s", err) gLog.Printf(LvERROR, "rename dump error:%s", err)
} }
if !d.running { if !d.running {
return return
} }
gLog.Printf(LevelERROR, "worker stop, restart it after 10s") gLog.Printf(LvERROR, "worker stop, restart it after 10s")
time.Sleep(time.Second * 10) time.Sleep(time.Second * 10)
} }
} }
@@ -117,163 +113,3 @@ func (d *daemon) Control(ctrlComm string, exeAbsPath string, args []string) erro
return nil 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
View File

@@ -1,10 +1,27 @@
module openp2p module openp2p
go 1.16 go 1.18
require ( require (
github.com/gorilla/websocket v1.4.2 github.com/gorilla/websocket v1.4.2
github.com/kardianos/service v1.2.0 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 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
)

View File

@@ -17,37 +17,38 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
if err != nil { if err != nil {
return err 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 { switch subType {
case MsgPushConnectReq: case MsgPushConnectReq:
req := PushConnectReq{} req := PushConnectReq{}
err := json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req) err := json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req)
if err != nil { if err != nil {
gLog.Printf(LevelERROR, "wrong MsgPushConnectReq:%s", err) gLog.Printf(LvERROR, "wrong MsgPushConnectReq:%s", err)
return err return err
} }
gLog.Printf(LevelINFO, "%s is connecting...", req.From) gLog.Printf(LvINFO, "%s is connecting...", req.From)
gLog.Println(LevelDEBUG, "push connect response to ", req.From) gLog.Println(LvDEBUG, "push connect response to ", req.From)
// verify totp token or token // 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 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()) || VerifyTOTP(req.Token, pn.config.Token, time.Now().Unix()) ||
(req.FromToken == pn.config.Token) { (req.FromToken == pn.config.Token) {
gLog.Printf(LevelINFO, "Access Granted\n") gLog.Printf(LvINFO, "Access Granted\n")
config := AppConfig{} config := AppConfig{}
config.peerNatType = req.NatType config.peerNatType = req.NatType
config.peerConeNatPort = req.ConeNatPort config.peerConeNatPort = req.ConeNatPort
config.peerIP = req.FromIP config.peerIP = req.FromIP
config.PeerNode = req.From config.PeerNode = req.From
config.peerVersion = req.Version
// share relay node will limit bandwidth // share relay node will limit bandwidth
if req.FromToken != pn.config.Token { 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 config.shareBandwidth = pn.config.ShareBandwidth
} }
// go pn.AddTunnel(config, req.ID) // go pn.AddTunnel(config, req.ID)
go pn.addDirectTunnel(config, req.ID) go pn.addDirectTunnel(config, req.ID)
break break
} }
gLog.Println(LevelERROR, "Access Denied:", req.From) gLog.Println(LvERROR, "Access Denied:", req.From)
rsp := PushConnectRsp{ rsp := PushConnectRsp{
Error: 1, Error: 1,
Detail: fmt.Sprintf("connect to %s error: Access Denied", pn.config.Node), 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{} rsp := PushRsp{}
err := json.Unmarshal(msg[openP2PHeaderSize:], &rsp) err := json.Unmarshal(msg[openP2PHeaderSize:], &rsp)
if err != nil { if err != nil {
gLog.Printf(LevelERROR, "wrong pushRsp:%s", err) gLog.Printf(LvERROR, "wrong pushRsp:%s", err)
return err return err
} }
if rsp.Error == 0 { if rsp.Error == 0 {
gLog.Printf(LevelDEBUG, "push ok, detail:%s", rsp.Detail) gLog.Printf(LvDEBUG, "push ok, detail:%s", rsp.Detail)
} else { } 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: case MsgPushAddRelayTunnelReq:
req := AddRelayTunnelReq{} req := AddRelayTunnelReq{}
err := json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req) err := json.Unmarshal(msg[openP2PHeaderSize+PushHeaderSize:], &req)
if err != nil { if err != nil {
gLog.Printf(LevelERROR, "wrong RelayNodeRsp:%s", err) gLog.Printf(LvERROR, "wrong RelayNodeRsp:%s", err)
return err return err
} }
config := AppConfig{} config := AppConfig{}
@@ -83,12 +84,19 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
// notify peer relay ready // notify peer relay ready
msg := TunnelMsg{ID: t.id} msg := TunnelMsg{ID: t.id}
pn.push(r.From, MsgPushAddRelayTunnelRsp, msg) pn.push(r.From, MsgPushAddRelayTunnelRsp, msg)
SaveKey(req.AppID, req.AppKey)
} }
}(req) }(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: case MsgPushUpdate:
gLog.Println(LevelINFO, "MsgPushUpdate") gLog.Println(LvINFO, "MsgPushUpdate")
update() // download new version first, then exec ./openp2p update update() // download new version first, then exec ./openp2p update
targetPath := filepath.Join(defaultInstallPath, defaultBinName) targetPath := filepath.Join(defaultInstallPath, defaultBinName)
args := []string{"update"} args := []string{"update"}
@@ -104,11 +112,11 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
} }
return err return err
case MsgPushRestart: case MsgPushRestart:
gLog.Println(LevelINFO, "MsgPushRestart") gLog.Println(LvINFO, "MsgPushRestart")
os.Exit(0) os.Exit(0)
return err return err
case MsgPushReportApps: case MsgPushReportApps:
gLog.Println(LevelINFO, "MsgPushReportApps") gLog.Println(LvINFO, "MsgPushReportApps")
req := ReportApps{} req := ReportApps{}
gConf.mtx.Lock() gConf.mtx.Lock()
defer gConf.mtx.Unlock() defer gConf.mtx.Unlock()
@@ -147,11 +155,11 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
} }
pn.write(MsgReport, MsgReportApps, &req) pn.write(MsgReport, MsgReportApps, &req)
case MsgPushEditApp: case MsgPushEditApp:
gLog.Println(LevelINFO, "MsgPushEditApp") gLog.Println(LvINFO, "MsgPushEditApp")
newApp := AppInfo{} newApp := AppInfo{}
err := json.Unmarshal(msg[openP2PHeaderSize:], &newApp) err := json.Unmarshal(msg[openP2PHeaderSize:], &newApp)
if err != nil { 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 return err
} }
oldConf := AppConfig{Enabled: 1} oldConf := AppConfig{Enabled: 1}
@@ -175,11 +183,11 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
// pn.AddApp(config) // pn.AddApp(config)
// TODO: report result // TODO: report result
case MsgPushEditNode: case MsgPushEditNode:
gLog.Println(LevelINFO, "MsgPushEditNode") gLog.Println(LvINFO, "MsgPushEditNode")
req := EditNode{} req := EditNode{}
err := json.Unmarshal(msg[openP2PHeaderSize:], &req) err := json.Unmarshal(msg[openP2PHeaderSize:], &req)
if err != nil { 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 return err
} }
gConf.setNode(req.NewName) gConf.setNode(req.NewName)
@@ -188,15 +196,15 @@ func handlePush(pn *P2PNetwork, subType uint16, msg []byte) error {
// TODO: hot reload // TODO: hot reload
os.Exit(0) os.Exit(0)
case MsgPushSwitchApp: case MsgPushSwitchApp:
gLog.Println(LevelINFO, "MsgPushSwitchApp") gLog.Println(LvINFO, "MsgPushSwitchApp")
app := AppInfo{} app := AppInfo{}
err := json.Unmarshal(msg[openP2PHeaderSize:], &app) err := json.Unmarshal(msg[openP2PHeaderSize:], &app)
if err != nil { 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 return err
} }
config := AppConfig{Enabled: app.Enabled, SrcPort: app.SrcPort, Protocol: app.Protocol} 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) gConf.switchApp(config, app.Enabled)
if app.Enabled == 0 { if app.Enabled == 0 {
// disable APP // disable APP

View File

@@ -11,8 +11,8 @@ import (
) )
func handshakeC2C(t *P2PTunnel) (err error) { 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) 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(LevelDEBUG, "handshakeC2C ok") defer gLog.Printf(LvDEBUG, "handshakeC2C ok")
conn, err := net.ListenUDP("udp", t.la) conn, err := net.ListenUDP("udp", t.la)
if err != nil { if err != nil {
return err return err
@@ -20,40 +20,40 @@ func handshakeC2C(t *P2PTunnel) (err error) {
defer conn.Close() defer conn.Close()
_, err = UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id}) _, err = UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id})
if err != nil { if err != nil {
gLog.Println(LevelDEBUG, "handshakeC2C write MsgPunchHandshake error:", err) gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshake error:", err)
return err return err
} }
ra, head, _, _, err := UDPRead(conn, 5000) ra, head, _, _, err := UDPRead(conn, 5000)
if err != nil { if err != nil {
time.Sleep(time.Millisecond * 200) 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) ra, head, _, _, err = UDPRead(conn, 5000)
if err != nil { if err != nil {
gLog.Println(LevelDEBUG, "handshakeC2C read MsgPunchHandshake error:", err) gLog.Println(LvDEBUG, "handshakeC2C read MsgPunchHandshake error:", err)
return err return err
} }
} }
t.ra, _ = net.ResolveUDPAddr("udp", ra.String()) t.ra, _ = net.ResolveUDPAddr("udp", ra.String())
// cone server side // cone server side
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake { 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}) UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
_, head, _, _, err = UDPRead(conn, 5000) _, head, _, _, err = UDPRead(conn, 5000)
if err != nil { if err != nil {
gLog.Println(LevelDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err) gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err)
return err return err
} }
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck { 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 return nil
} }
} }
// cone client side will only read handshake ack // cone client side will only read handshake ack
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck { 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}) _, err = UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
if err != nil { if err != nil {
gLog.Println(LevelDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err) gLog.Println(LvDEBUG, "handshakeC2C write MsgPunchHandshakeAck error", err)
} }
return err return err
} }
@@ -61,8 +61,8 @@ func handshakeC2C(t *P2PTunnel) (err error) {
} }
func handshakeC2S(t *P2PTunnel) error { func handshakeC2S(t *P2PTunnel) error {
gLog.Printf(LevelDEBUG, "handshakeC2S start") gLog.Printf(LvDEBUG, "handshakeC2S start")
defer gLog.Printf(LevelDEBUG, "handshakeC2S end") defer gLog.Printf(LvDEBUG, "handshakeC2S end")
// even if read timeout, continue handshake // even if read timeout, continue handshake
t.pn.read(t.config.PeerNode, MsgPush, MsgPushHandshakeStart, SymmetricHandshakeAckTimeout) t.pn.read(t.config.PeerNode, MsgPush, MsgPushHandshakeStart, SymmetricHandshakeAckTimeout)
r := rand.New(rand.NewSource(time.Now().UnixNano())) r := rand.New(rand.NewSource(time.Now().UnixNano()))
@@ -73,7 +73,7 @@ func handshakeC2S(t *P2PTunnel) error {
} }
defer conn.Close() defer conn.Close()
go func() error { 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++ { for i := 0; i < SymmetricHandshakeNum; i++ {
// TODO: auto calc cost time // TODO: auto calc cost time
time.Sleep(SymmetricHandshakeInterval) time.Sleep(SymmetricHandshakeInterval)
@@ -83,35 +83,35 @@ func handshakeC2S(t *P2PTunnel) error {
} }
_, err = UDPWrite(conn, dst, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id}) _, err = UDPWrite(conn, dst, MsgP2P, MsgPunchHandshake, P2PHandshakeReq{ID: t.id})
if err != nil { if err != nil {
gLog.Println(LevelDEBUG, "handshakeC2S write MsgPunchHandshake error:", err) gLog.Println(LvDEBUG, "handshakeC2S write MsgPunchHandshake error:", err)
return err return err
} }
} }
gLog.Println(LevelDEBUG, "send symmetric handshake end") gLog.Println(LvDEBUG, "send symmetric handshake end")
return nil return nil
}() }()
deadline := time.Now().Add(SymmetricHandshakeAckTimeout) deadline := time.Now().Add(SymmetricHandshakeAckTimeout)
err = conn.SetReadDeadline(deadline) err = conn.SetReadDeadline(deadline)
if err != nil { if err != nil {
gLog.Println(LevelERROR, "SymmetricHandshakeAckTimeout SetReadDeadline error") gLog.Println(LvERROR, "SymmetricHandshakeAckTimeout SetReadDeadline error")
return err return err
} }
// read response of the punching hole ok port // read response of the punching hole ok port
result := make([]byte, 1024) result := make([]byte, 1024)
_, dst, err := conn.ReadFrom(result) _, dst, err := conn.ReadFrom(result)
if err != nil { if err != nil {
gLog.Println(LevelERROR, "handshakeC2S wait timeout") gLog.Println(LvERROR, "handshakeC2S wait timeout")
return err return err
} }
head := &openP2PHeader{} head := &openP2PHeader{}
err = binary.Read(bytes.NewReader(result[:openP2PHeaderSize]), binary.LittleEndian, head) err = binary.Read(bytes.NewReader(result[:openP2PHeaderSize]), binary.LittleEndian, head)
if err != nil { if err != nil {
gLog.Println(LevelERROR, "parse p2pheader error:", err) gLog.Println(LvERROR, "parse p2pheader error:", err)
return err return err
} }
t.ra, _ = net.ResolveUDPAddr("udp", dst.String()) t.ra, _ = net.ResolveUDPAddr("udp", dst.String())
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck { 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}) _, err = UDPWrite(conn, dst, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
return err return err
} }
@@ -119,11 +119,11 @@ func handshakeC2S(t *P2PTunnel) error {
} }
func handshakeS2C(t *P2PTunnel) error { func handshakeS2C(t *P2PTunnel) error {
gLog.Printf(LevelDEBUG, "handshakeS2C start") gLog.Printf(LvDEBUG, "handshakeS2C start")
defer gLog.Printf(LevelDEBUG, "handshakeS2C end") defer gLog.Printf(LvDEBUG, "handshakeS2C end")
gotCh := make(chan *net.UDPAddr, 5) gotCh := make(chan *net.UDPAddr, 5)
// sequencely udp send handshake, do not parallel send // 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 gotIt := false
gotMtx := sync.Mutex{} gotMtx := sync.Mutex{}
for i := 0; i < SymmetricHandshakeNum; i++ { for i := 0; i < SymmetricHandshakeNum; i++ {
@@ -132,7 +132,7 @@ func handshakeS2C(t *P2PTunnel) error {
go func(t *P2PTunnel) error { go func(t *P2PTunnel) error {
conn, err := net.ListenUDP("udp", nil) conn, err := net.ListenUDP("udp", nil)
if err != nil { if err != nil {
gLog.Printf(LevelDEBUG, "listen error") gLog.Printf(LvDEBUG, "listen error")
return err return err
} }
defer conn.Close() defer conn.Close()
@@ -150,15 +150,15 @@ func handshakeS2C(t *P2PTunnel) error {
gotIt = true gotIt = true
t.la, _ = net.ResolveUDPAddr("udp", conn.LocalAddr().String()) t.la, _ = net.ResolveUDPAddr("udp", conn.LocalAddr().String())
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshake { 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}) UDPWrite(conn, t.ra, MsgP2P, MsgPunchHandshakeAck, P2PHandshakeReq{ID: t.id})
_, head, _, _, err = UDPRead(conn, 5000) _, head, _, _, err = UDPRead(conn, 5000)
if err != nil { if err != nil {
gLog.Println(LevelDEBUG, "handshakeS2C handshake error") gLog.Println(LvDEBUG, "handshakeS2C handshake error")
return err return err
} }
if head.MainType == MsgP2P && head.SubType == MsgPunchHandshakeAck { 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 gotCh <- t.la
return nil return nil
} }
@@ -166,15 +166,15 @@ func handshakeS2C(t *P2PTunnel) error {
return nil return nil
}(t) }(t)
} }
gLog.Printf(LevelDEBUG, "send symmetric handshake end") gLog.Printf(LvDEBUG, "send symmetric handshake end")
gLog.Println(LevelDEBUG, "handshakeS2C ready, notify peer connect") gLog.Println(LvDEBUG, "handshakeS2C ready, notify peer connect")
t.pn.push(t.config.PeerNode, MsgPushHandshakeStart, TunnelMsg{ID: t.id}) t.pn.push(t.config.PeerNode, MsgPushHandshakeStart, TunnelMsg{ID: t.id})
select { select {
case <-time.After(SymmetricHandshakeAckTimeout): case <-time.After(SymmetricHandshakeAckTimeout):
return fmt.Errorf("wait handshake failed") return fmt.Errorf("wait handshake failed")
case la := <-gotCh: case la := <-gotCh:
gLog.Println(LevelDEBUG, "symmetric handshake ok", la) gLog.Println(LvDEBUG, "symmetric handshake ok", la)
} }
return nil return nil
} }

127
install.go Normal file
View 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
View File

@@ -8,17 +8,15 @@ import (
"time" "time"
) )
// LogLevel ...
type LogLevel int type LogLevel int
var gLog *V8log var gLog *logger
// LevelDEBUG ...
const ( const (
LevelDEBUG LogLevel = iota LvDEBUG LogLevel = iota
LevelINFO LvINFO
LevelWARN LvWARN
LevelERROR LvERROR
) )
var ( var (
@@ -30,10 +28,10 @@ func init() {
logFileNames = make(map[LogLevel]string) logFileNames = make(map[LogLevel]string)
loglevel = make(map[LogLevel]string) loglevel = make(map[LogLevel]string)
logFileNames[0] = ".log" logFileNames[0] = ".log"
loglevel[LevelDEBUG] = "DEBUG" loglevel[LvDEBUG] = "DEBUG"
loglevel[LevelINFO] = "INFO" loglevel[LvINFO] = "INFO"
loglevel[LevelWARN] = "WARN" loglevel[LvWARN] = "WARN"
loglevel[LevelERROR] = "ERROR" loglevel[LvERROR] = "ERROR"
} }
@@ -43,25 +41,21 @@ const (
LogFileAndConsole LogFileAndConsole
) )
// V8log ... type logger struct {
type V8log struct {
loggers map[LogLevel]*log.Logger loggers map[LogLevel]*log.Logger
files map[LogLevel]*os.File files map[LogLevel]*os.File
level LogLevel level LogLevel
stopSig chan bool
logDir string logDir string
mtx *sync.Mutex mtx *sync.Mutex
stoped bool
lineEnding string lineEnding string
pid int pid int
maxLogSize int64 maxLogSize int64
mode int mode int
} }
// InitLogger ... func NewLogger(path string, filePrefix string, level LogLevel, maxLogSize int64, mode int) *logger {
func InitLogger(path string, filePrefix string, level LogLevel, maxLogSize int64, mode int) *V8log { loggers := make(map[LogLevel]*log.Logger)
logger := make(map[LogLevel]*log.Logger) logfiles := make(map[LogLevel]*os.File)
openedfile := make(map[LogLevel]*os.File)
var ( var (
logdir string logdir string
) )
@@ -71,15 +65,15 @@ func InitLogger(path string, filePrefix string, level LogLevel, maxLogSize int64
logdir = path + "/log/" logdir = path + "/log/"
} }
os.MkdirAll(logdir, 0777) os.MkdirAll(logdir, 0777)
for l := range logFileNames { for lv := range logFileNames {
logFilePath := logdir + filePrefix + logFileNames[l] logFilePath := logdir + filePrefix + logFileNames[lv]
f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
os.Chmod(logFilePath, 0666) os.Chmod(logFilePath, 0666)
openedfile[l] = f logfiles[lv] = f
logger[l] = log.New(f, "", log.LstdFlags) loggers[lv] = log.New(f, "", log.LstdFlags)
} }
var le string var le string
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
@@ -87,97 +81,84 @@ func InitLogger(path string, filePrefix string, level LogLevel, maxLogSize int64
} else { } else {
le = "\n" 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() go pLog.checkFile()
return pLog return pLog
} }
func (vl *V8log) setLevel(level LogLevel) { func (l *logger) setLevel(level LogLevel) {
vl.mtx.Lock() l.mtx.Lock()
defer vl.mtx.Unlock() defer l.mtx.Unlock()
vl.level = level l.level = level
} }
func (vl *V8log) setMode(mode int) { func (l *logger) setMode(mode int) {
vl.mtx.Lock() l.mtx.Lock()
defer vl.mtx.Unlock() defer l.mtx.Unlock()
vl.mode = mode l.mode = mode
} }
func (vl *V8log) checkFile() { func (l *logger) checkFile() {
if vl.maxLogSize <= 0 { if l.maxLogSize <= 0 {
return return
} }
ticker := time.NewTicker(time.Minute) ticker := time.NewTicker(time.Minute)
for { for {
select { select {
case <-ticker.C: case <-ticker.C:
vl.mtx.Lock() l.mtx.Lock()
for l, logFile := range vl.files { for lv, logFile := range l.files {
f, e := logFile.Stat() f, e := logFile.Stat()
if e != nil { if e != nil {
continue continue
} }
if f.Size() <= vl.maxLogSize { if f.Size() <= l.maxLogSize {
continue continue
} }
logFile.Close() logFile.Close()
fname := f.Name() fname := f.Name()
backupPath := vl.logDir + fname + ".0" backupPath := l.logDir + fname + ".0"
os.Remove(backupPath) os.Remove(backupPath)
os.Rename(vl.logDir+fname, backupPath) os.Rename(l.logDir+fname, backupPath)
newFile, e := os.OpenFile(vl.logDir+fname, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) newFile, e := os.OpenFile(l.logDir+fname, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if e == nil { if e == nil {
vl.loggers[l].SetOutput(newFile) l.loggers[lv].SetOutput(newFile)
vl.files[l] = newFile l.files[lv] = newFile
} }
} }
vl.mtx.Unlock() l.mtx.Unlock()
case <-vl.stopSig:
}
if vl.stoped {
break
} }
} }
} }
// Printf Warning: report error log depends on this Print format. func (l *logger) Printf(level LogLevel, format string, params ...interface{}) {
func (vl *V8log) Printf(level LogLevel, format string, params ...interface{}) { l.mtx.Lock()
vl.mtx.Lock() defer l.mtx.Unlock()
defer vl.mtx.Unlock() if level < l.level {
if vl.stoped {
return return
} }
if level < vl.level { pidAndLevel := []interface{}{l.pid, loglevel[level]}
return
}
pidAndLevel := []interface{}{vl.pid, loglevel[level]}
params = append(pidAndLevel, params...) params = append(pidAndLevel, params...)
if vl.mode == LogFile || vl.mode == LogFileAndConsole { if l.mode == LogFile || l.mode == LogFileAndConsole {
vl.loggers[0].Printf("%d %s "+format+vl.lineEnding, params...) l.loggers[0].Printf("%d %s "+format+l.lineEnding, params...)
} }
if vl.mode == LogConsole || vl.mode == LogFileAndConsole { if l.mode == LogConsole || l.mode == LogFileAndConsole {
log.Printf("%d %s "+format+vl.lineEnding, params...) log.Printf("%d %s "+format+l.lineEnding, params...)
} }
} }
// Println ... func (l *logger) Println(level LogLevel, params ...interface{}) {
func (vl *V8log) Println(level LogLevel, params ...interface{}) { l.mtx.Lock()
vl.mtx.Lock() defer l.mtx.Unlock()
defer vl.mtx.Unlock() if level < l.level {
if vl.stoped {
return return
} }
if level < vl.level { pidAndLevel := []interface{}{l.pid, " ", loglevel[level], " "}
return
}
pidAndLevel := []interface{}{vl.pid, " ", loglevel[level], " "}
params = append(pidAndLevel, params...) params = append(pidAndLevel, params...)
params = append(params, vl.lineEnding) params = append(params, l.lineEnding)
if vl.mode == LogFile || vl.mode == LogFileAndConsole { if l.mode == LogFile || l.mode == LogFileAndConsole {
vl.loggers[0].Print(params...) l.loggers[0].Print(params...)
} }
if vl.mode == LogConsole || vl.mode == LogFileAndConsole { if l.mode == LogConsole || l.mode == LogFileAndConsole {
log.Print(params...) log.Print(params...)
} }
} }

83
nat.go
View File

@@ -3,47 +3,69 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"log"
"math/rand" "math/rand"
"net" "net"
"time" "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)) conn, err := net.ListenPacket("udp", fmt.Sprintf(":%d", localPort))
if err != nil { if err != nil {
return "", 0, 0, err return "", 0, 0, 0, err
} }
defer conn.Close() defer conn.Close()
dst, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", serverHost, serverPort)) dst, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", serverHost, serverPort))
if err != nil { if err != nil {
return "", 0, 0, err return "", 0, 0, 0, err
} }
// The connection can write data to the desired address. // The connection can write data to the desired address.
msg, err := newMessage(MsgNATDetect, 0, &NatDetectReq{SrcPort: localPort, EchoPort: echoPort}) msg, err := newMessage(MsgNATDetect, 0, &NatDetectReq{SrcPort: localPort, EchoPort: echoPort})
_, err = conn.WriteTo(msg, dst) _, err = conn.WriteTo(msg, dst)
if err != nil { if err != nil {
return "", 0, 0, err return "", 0, 0, 0, err
} }
deadline := time.Now().Add(NatTestTimeout) deadline := time.Now().Add(NatTestTimeout)
err = conn.SetReadDeadline(deadline) err = conn.SetReadDeadline(deadline)
if err != nil { if err != nil {
return "", 0, 0, err return "", 0, 0, 0, err
} }
buffer := make([]byte, 1024) buffer := make([]byte, 1024)
nRead, _, err := conn.ReadFrom(buffer) nRead, _, err := conn.ReadFrom(buffer)
if err != nil { if err != nil {
gLog.Println(LevelERROR, "NAT detect error:", err) gLog.Println(LvERROR, "NAT detect error:", err)
return "", 0, 0, err return "", 0, 0, 0, err
} }
natRsp := NatDetectRsp{} natRsp := NatDetectRsp{}
err = json.Unmarshal(buffer[openP2PHeaderSize:nRead], &natRsp) err = json.Unmarshal(buffer[openP2PHeaderSize:nRead], &natRsp)
hasPublicIP = 0
hasUPNPorNATPMP = 0
// testing for public ip // testing for public ip
if echoPort != 0 { if echoPort != 0 {
for { for i := 0; i < 2; i++ {
gLog.Printf(LevelDEBUG, "public ip test start %s:%d", natRsp.IP, echoPort) 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) conn, err := net.ListenUDP("udp", nil)
if err != nil { if err != nil {
break break
@@ -60,56 +82,59 @@ func natTest(serverHost string, serverPort int, localPort int, echoPort int) (pu
conn.SetReadDeadline(time.Now().Add(PublicIPEchoTimeout)) conn.SetReadDeadline(time.Now().Add(PublicIPEchoTimeout))
_, _, err = conn.ReadFromUDP(buf) _, _, err = conn.ReadFromUDP(buf)
if err == nil { if err == nil {
gLog.Println(LevelDEBUG, "public ip:YES") if i == 1 {
natRsp.IsPublicIP = 1 gLog.Println(LvDEBUG, "UPNP or NAT-PMP:YES")
} else { hasUPNPorNATPMP = 1
gLog.Println(LevelDEBUG, "public ip:NO") } 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. // the random local port may be used by other.
localPort := int(rand.Uint32()%10000 + 50000) localPort := int(rand.Uint32()%10000 + 50000)
echoPort := int(rand.Uint32()%10000 + 50000) echoPort := int(rand.Uint32()%10000 + 50000)
go echo(echoPort) go echo(echoPort)
ip1, isPublicIP, port1, err := natTest(host, udp1, localPort, echoPort) ip1, hasPublicIP, hasUPNPorNATPMP, port1, err := natTest(host, udp1, localPort, echoPort)
gLog.Printf(LevelDEBUG, "local port:%d nat port:%d", localPort, port1) gLog.Printf(LvDEBUG, "local port:%d nat port:%d", localPort, port1)
if err != nil { if err != nil {
return "", 0, err return "", 0, hasUPNPorNATPMP, err
} }
if isPublicIP == 1 { if hasPublicIP == 1 || hasUPNPorNATPMP == 1 {
return ip1, NATNone, nil return ip1, NATNone, hasUPNPorNATPMP, nil
} }
ip2, _, port2, err := natTest(host, udp2, localPort, 0) // 2rd nat test not need testing publicip 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) gLog.Printf(LvDEBUG, "local port:%d nat port:%d", localPort, port2)
if err != nil { if err != nil {
return "", 0, err return "", 0, hasUPNPorNATPMP, err
} }
if ip1 != ip2 { 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 natType := NATSymmetric
if port1 == port2 { if port1 == port2 {
natType = NATCone natType = NATCone
} }
//TODO: NATNone //TODO: NATNone
return ip1, natType, nil return ip1, natType, hasUPNPorNATPMP, nil
} }
func echo(echoPort int) { func echo(echoPort int) {
conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: echoPort}) conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: echoPort})
if err != nil { if err != nil {
gLog.Println(LevelERROR, "echo server listen error:", err) gLog.Println(LvERROR, "echo server listen error:", err)
return return
} }
buf := make([]byte, 1600) buf := make([]byte, 1600)
defer conn.Close() defer conn.Close()
// wait 5s for echo testing // 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) n, addr, err := conn.ReadFromUDP(buf)
if err != nil { if err != nil {
return return

View File

@@ -12,7 +12,7 @@ func main() {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
binDir := filepath.Dir(os.Args[0]) binDir := filepath.Dir(os.Args[0])
os.Chdir(binDir) // for system service 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 // TODO: install sub command, deamon process
if len(os.Args) > 1 { if len(os.Args) > 1 {
switch os.Args[1] { switch os.Args[1] {
@@ -20,14 +20,14 @@ func main() {
fmt.Println(OpenP2PVersion) fmt.Println(OpenP2PVersion)
return return
case "update": 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) targetPath := filepath.Join(defaultInstallPath, defaultBinName)
d := daemon{} d := daemon{}
err := d.Control("restart", targetPath, nil) err := d.Control("restart", targetPath, nil)
if err != nil { if err != nil {
gLog.Println(LevelERROR, "restart service error:", err) gLog.Println(LvERROR, "restart service error:", err)
} else { } else {
gLog.Println(LevelINFO, "restart service ok.") gLog.Println(LvINFO, "restart service ok.")
} }
return return
case "install": case "install":
@@ -40,9 +40,9 @@ func main() {
} else { } else {
installByFilename() installByFilename()
} }
parseParams() parseParams("")
gLog.Println(LevelINFO, "openp2p start. version: ", OpenP2PVersion) gLog.Println(LvINFO, "openp2p start. version: ", OpenP2PVersion)
gLog.Println(LevelINFO, "Contact: QQ Group: 16947733, Email: openp2p.cn@gmail.com") gLog.Println(LvINFO, "Contact: QQ Group: 16947733, Email: openp2p.cn@gmail.com")
if gConf.daemonMode { if gConf.daemonMode {
d := daemon{} d := daemon{}
@@ -50,14 +50,14 @@ func main() {
return return
} }
gLog.Println(LevelINFO, &gConf) gLog.Println(LvINFO, &gConf)
setFirewall() setFirewall()
network := P2PNetworkInstance(&gConf.Network) network := P2PNetworkInstance(&gConf.Network)
if ok := network.Connect(30000); !ok { if ok := network.Connect(30000); !ok {
gLog.Println(LevelERROR, "P2PNetwork login error") gLog.Println(LvERROR, "P2PNetwork login error")
return return
} }
gLog.Println(LevelINFO, "waiting for connection...") gLog.Println(LvINFO, "waiting for connection...")
forever := make(chan bool) forever := make(chan bool)
<-forever <-forever
} }

View File

@@ -40,8 +40,8 @@ type overlayConn struct {
} }
func (oConn *overlayConn) run() { func (oConn *overlayConn) run() {
gLog.Printf(LevelDEBUG, "%d overlayConn run start", oConn.id) gLog.Printf(LvDEBUG, "%d overlayConn run start", oConn.id)
defer gLog.Printf(LevelDEBUG, "%d overlayConn run end", oConn.id) defer gLog.Printf(LvDEBUG, "%d overlayConn run end", oConn.id)
oConn.running = true oConn.running = true
oConn.lastReadUDPTs = time.Now() oConn.lastReadUDPTs = time.Now()
buffer := make([]byte, ReadBuffLen+PaddingSize) buffer := make([]byte, ReadBuffLen+PaddingSize)
@@ -58,7 +58,7 @@ func (oConn *overlayConn) run() {
continue continue
} }
// overlay tcp connection normal close, debug log // 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 break
} }
payload := buff[:dataLen] payload := buff[:dataLen]
@@ -68,13 +68,13 @@ func (oConn *overlayConn) run() {
writeBytes := append(tunnelHead.Bytes(), payload...) writeBytes := append(tunnelHead.Bytes(), payload...)
if oConn.rtid == 0 { if oConn.rtid == 0 {
oConn.tunnel.conn.WriteBytes(MsgP2P, MsgOverlayData, writeBytes) 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 { } else {
// write raley data // write raley data
all := append(relayHead.Bytes(), encodeHeader(MsgP2P, MsgOverlayData, uint32(len(writeBytes)))...) all := append(relayHead.Bytes(), encodeHeader(MsgP2P, MsgOverlayData, uint32(len(writeBytes)))...)
all = append(all, writeBytes...) all = append(all, writeBytes...)
oConn.tunnel.conn.WriteBytes(MsgP2P, MsgRelayData, all) 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 { if oConn.connTCP != nil {

View File

@@ -17,7 +17,7 @@ type p2pApp struct {
listener net.Listener listener net.Listener
listenerUDP *net.UDPConn listenerUDP *net.UDPConn
tunnel *P2PTunnel tunnel *P2PTunnel
rtid uint64 rtid uint64 // relay tunnelID
relayNode string relayNode string
relayMode string relayMode string
hbTime time.Time hbTime time.Time
@@ -48,19 +48,19 @@ func (app *p2pApp) updateHeartbeat() {
} }
func (app *p2pApp) listenTCP() error { func (app *p2pApp) listenTCP() error {
gLog.Printf(LevelDEBUG, "tcp accept on port %d start", app.config.SrcPort) gLog.Printf(LvDEBUG, "tcp accept on port %d start", app.config.SrcPort)
defer gLog.Printf(LevelDEBUG, "tcp accept on port %d end", app.config.SrcPort) defer gLog.Printf(LvDEBUG, "tcp accept on port %d end", app.config.SrcPort)
var err error var err error
app.listener, err = net.Listen("tcp4", fmt.Sprintf("0.0.0.0:%d", app.config.SrcPort)) app.listener, err = net.Listen("tcp4", fmt.Sprintf("0.0.0.0:%d", app.config.SrcPort))
if err != nil { if err != nil {
gLog.Printf(LevelERROR, "listen error:%s", err) gLog.Printf(LvERROR, "listen error:%s", err)
return err return err
} }
for app.running { for app.running {
conn, err := app.listener.Accept() conn, err := app.listener.Accept()
if err != nil { if err != nil {
if app.running { 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 break
} }
@@ -81,7 +81,7 @@ func (app *p2pApp) listenTCP() error {
oConn.appKeyBytes = encryptKey oConn.appKeyBytes = encryptKey
} }
app.tunnel.overlayConns.Store(oConn.id, &oConn) 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 // tell peer connect
req := OverlayConnectReq{ID: oConn.id, req := OverlayConnectReq{ID: oConn.id,
Token: app.tunnel.pn.config.Token, Token: app.tunnel.pn.config.Token,
@@ -106,12 +106,12 @@ func (app *p2pApp) listenTCP() error {
} }
func (app *p2pApp) listenUDP() error { func (app *p2pApp) listenUDP() error {
gLog.Printf(LevelDEBUG, "udp accept on port %d start", app.config.SrcPort) gLog.Printf(LvDEBUG, "udp accept on port %d start", app.config.SrcPort)
defer gLog.Printf(LevelDEBUG, "udp accept on port %d end", app.config.SrcPort) defer gLog.Printf(LvDEBUG, "udp accept on port %d end", app.config.SrcPort)
var err error var err error
app.listenerUDP, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: app.config.SrcPort}) app.listenerUDP, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: app.config.SrcPort})
if err != nil { if err != nil {
gLog.Printf(LevelERROR, "listen error:%s", err) gLog.Printf(LvERROR, "listen error:%s", err)
return err return err
} }
buffer := make([]byte, 64*1024) buffer := make([]byte, 64*1024)
@@ -123,7 +123,7 @@ func (app *p2pApp) listenUDP() error {
if ne, ok := err.(net.Error); ok && ne.Timeout() { if ne, ok := err.(net.Error); ok && ne.Timeout() {
continue continue
} else { } else {
gLog.Printf(LevelERROR, "udp read failed:%s", err) gLog.Printf(LvERROR, "udp read failed:%s", err)
break break
} }
} else { } else {
@@ -161,7 +161,7 @@ func (app *p2pApp) listenUDP() error {
oConn.appKeyBytes = encryptKey oConn.appKeyBytes = encryptKey
} }
app.tunnel.overlayConns.Store(oConn.id, &oConn) 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 // tell peer connect
req := OverlayConnectReq{ID: oConn.id, req := OverlayConnectReq{ID: oConn.id,
Token: app.tunnel.pn.config.Token, Token: app.tunnel.pn.config.Token,
@@ -196,8 +196,8 @@ func (app *p2pApp) listenUDP() error {
} }
func (app *p2pApp) listen() error { func (app *p2pApp) listen() error {
gLog.Printf(LevelINFO, "LISTEN ON PORT %s:%d START", app.config.Protocol, app.config.SrcPort) gLog.Printf(LvINFO, "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) defer gLog.Printf(LvINFO, "LISTEN ON PORT %s:%d END", app.config.Protocol, app.config.SrcPort)
app.wg.Add(1) app.wg.Add(1)
defer app.wg.Done() defer app.wg.Done()
app.running = true app.running = true
@@ -233,8 +233,8 @@ func (app *p2pApp) close() {
func (app *p2pApp) relayHeartbeatLoop() { func (app *p2pApp) relayHeartbeatLoop() {
app.wg.Add(1) app.wg.Add(1)
defer app.wg.Done() defer app.wg.Done()
gLog.Printf(LevelDEBUG, "relayHeartbeat to %d start", app.rtid) gLog.Printf(LvDEBUG, "relayHeartbeat to %d start", app.rtid)
defer gLog.Printf(LevelDEBUG, "relayHeartbeat to %d end", app.rtid) defer gLog.Printf(LvDEBUG, "relayHeartbeat to %d end", app.rtid)
relayHead := new(bytes.Buffer) relayHead := new(bytes.Buffer)
binary.Write(relayHead, binary.LittleEndian, app.rtid) binary.Write(relayHead, binary.LittleEndian, app.rtid)
req := RelayHeartbeat{RelayTunnelID: app.tunnel.id, req := RelayHeartbeat{RelayTunnelID: app.tunnel.id,

View File

@@ -9,7 +9,6 @@ import (
"fmt" "fmt"
"math" "math"
"math/rand" "math/rand"
"net"
"net/url" "net/url"
"strings" "strings"
"sync" "sync"
@@ -76,7 +75,7 @@ func (pn *P2PNetwork) run() {
time.Sleep(NetworkHeartbeatTime) time.Sleep(NetworkHeartbeatTime)
err := pn.init() err := pn.init()
if err != nil { 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) pn.DeleteApp(*config)
} }
if config.retryNum > 0 { 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 if time.Now().Add(-time.Minute * 15).After(config.retryTime) { // normal lasts 15min
config.retryNum = 0 config.retryNum = 0
} }
@@ -145,7 +144,7 @@ func (pn *P2PNetwork) runAll() {
} }
} }
func (pn *P2PNetwork) autorunApp() { func (pn *P2PNetwork) autorunApp() {
gLog.Println(LevelINFO, "autorunApp start") gLog.Println(LvINFO, "autorunApp start")
for pn.running { for pn.running {
time.Sleep(time.Second) time.Sleep(time.Second)
if !pn.online { if !pn.online {
@@ -154,12 +153,12 @@ func (pn *P2PNetwork) autorunApp() {
pn.runAll() pn.runAll()
time.Sleep(time.Second * 10) 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) { func (pn *P2PNetwork) addRelayTunnel(config AppConfig) (*P2PTunnel, uint64, string, error) {
gLog.Printf(LevelINFO, "addRelayTunnel to %s start", config.PeerNode) gLog.Printf(LvINFO, "addRelayTunnel to %s start", config.PeerNode)
defer gLog.Printf(LevelINFO, "addRelayTunnel to %s end", config.PeerNode) defer gLog.Printf(LvINFO, "addRelayTunnel to %s end", config.PeerNode)
pn.write(MsgRelay, MsgRelayNodeReq, &RelayNodeReq{config.PeerNode}) pn.write(MsgRelay, MsgRelayNodeReq, &RelayNodeReq{config.PeerNode})
head, body := pn.read("", MsgRelay, MsgRelayNodeRsp, time.Second*10) head, body := pn.read("", MsgRelay, MsgRelayNodeRsp, time.Second*10)
if head == nil { if head == nil {
@@ -168,20 +167,20 @@ func (pn *P2PNetwork) addRelayTunnel(config AppConfig, appid uint64, appkey uint
rsp := RelayNodeRsp{} rsp := RelayNodeRsp{}
err := json.Unmarshal(body, &rsp) err := json.Unmarshal(body, &rsp)
if err != nil { 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 nil, 0, "", errors.New("unmarshal MsgRelayNodeRsp error")
} }
if rsp.RelayName == "" || rsp.RelayToken == 0 { if rsp.RelayName == "" || rsp.RelayToken == 0 {
gLog.Printf(LevelERROR, "MsgRelayNodeReq error") gLog.Printf(LvERROR, "MsgRelayNodeReq error")
return nil, 0, "", errors.New("MsgRelayNodeReq error") return nil, 0, "", errors.New("MsgRelayNodeReq error")
} }
gLog.Printf(LevelINFO, "got relay node:%s", rsp.RelayName) gLog.Printf(LvINFO, "got relay node:%s", rsp.RelayName)
relayConfig := config relayConfig := config
relayConfig.PeerNode = rsp.RelayName relayConfig.PeerNode = rsp.RelayName
relayConfig.peerToken = rsp.RelayToken relayConfig.peerToken = rsp.RelayToken
t, err := pn.addDirectTunnel(relayConfig, 0) t, err := pn.addDirectTunnel(relayConfig, 0)
if err != nil { if err != nil {
gLog.Println(LevelERROR, "direct connect error:", err) gLog.Println(LvERROR, "direct connect error:", err)
return nil, 0, "", err return nil, 0, "", err
} }
// notify peer addRelayTunnel // notify peer addRelayTunnel
@@ -189,22 +188,20 @@ func (pn *P2PNetwork) addRelayTunnel(config AppConfig, appid uint64, appkey uint
From: pn.config.Node, From: pn.config.Node,
RelayName: rsp.RelayName, RelayName: rsp.RelayName,
RelayToken: rsp.RelayToken, 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) pn.push(config.PeerNode, MsgPushAddRelayTunnelReq, &req)
// wait relay ready // wait relay ready
head, body = pn.read(config.PeerNode, MsgPush, MsgPushAddRelayTunnelRsp, PeerAddRelayTimeount) // TODO: const value head, body = pn.read(config.PeerNode, MsgPush, MsgPushAddRelayTunnelRsp, PeerAddRelayTimeount) // TODO: const value
if head == nil { if head == nil {
gLog.Printf(LevelERROR, "read MsgPushAddRelayTunnelRsp error") gLog.Printf(LvERROR, "read MsgPushAddRelayTunnelRsp error")
return nil, 0, "", errors.New("read MsgPushAddRelayTunnelRsp error") return nil, 0, "", errors.New("read MsgPushAddRelayTunnelRsp error")
} }
rspID := TunnelMsg{} rspID := TunnelMsg{}
err = json.Unmarshal(body, &rspID) err = json.Unmarshal(body, &rspID)
if err != nil { 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 nil, 0, "", errors.New("unmarshal MsgRelayNodeRsp error")
} }
return t, rspID.ID, rsp.Mode, err 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 // use *AppConfig to save status
func (pn *P2PNetwork) AddApp(config AppConfig) error { 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) gLog.Printf(LvINFO, "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) defer gLog.Printf(LvINFO, "addApp %s to %s:%s:%d end", config.AppName, config.PeerNode, config.DstHost, config.DstPort)
if !pn.online { if !pn.online {
return errors.New("P2PNetwork offline") return errors.New("P2PNetwork offline")
} }
@@ -240,9 +237,8 @@ func (pn *P2PNetwork) AddApp(config AppConfig) error {
peerIP = t.config.peerIP peerIP = t.config.peerIP
} }
if err != nil && err == ErrorHandshake { if err != nil && err == ErrorHandshake {
gLog.Println(LevelERROR, "direct connect failed, try to relay") gLog.Println(LvERROR, "direct connect failed, try to relay")
appKey = rand.Uint64() t, rtid, relayMode, err = pn.addRelayTunnel(config)
t, rtid, relayMode, err = pn.addRelayTunnel(config, appID, appKey)
if t != nil { if t != nil {
relayNode = t.config.PeerNode relayNode = t.config.PeerNode
} }
@@ -269,6 +265,16 @@ func (pn *P2PNetwork) AddApp(config AppConfig) error {
if err != nil { if err != nil {
return err 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{ app := p2pApp{
id: appID, id: appID,
key: appKey, key: appKey,
@@ -286,21 +292,21 @@ func (pn *P2PNetwork) AddApp(config AppConfig) error {
} }
func (pn *P2PNetwork) DeleteApp(config AppConfig) { func (pn *P2PNetwork) DeleteApp(config AppConfig) {
gLog.Printf(LevelINFO, "DeleteApp %s%d start", config.Protocol, config.SrcPort) gLog.Printf(LvINFO, "DeleteApp %s%d start", config.Protocol, config.SrcPort)
defer gLog.Printf(LevelINFO, "DeleteApp %s%d end", config.Protocol, config.SrcPort) defer gLog.Printf(LvINFO, "DeleteApp %s%d end", config.Protocol, config.SrcPort)
// close the apps of this config // close the apps of this config
i, ok := pn.apps.Load(fmt.Sprintf("%s%d", config.Protocol, config.SrcPort)) i, ok := pn.apps.Load(fmt.Sprintf("%s%d", config.Protocol, config.SrcPort))
if ok { if ok {
app := i.(*p2pApp) 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() app.close()
pn.apps.Delete(fmt.Sprintf("%s%d", config.Protocol, config.SrcPort)) pn.apps.Delete(fmt.Sprintf("%s%d", config.Protocol, config.SrcPort))
} }
} }
func (pn *P2PNetwork) addDirectTunnel(config AppConfig, tid uint64) (*P2PTunnel, error) { 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) gLog.Printf(LvDEBUG, "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) defer gLog.Printf(LvDEBUG, "addDirectTunnel %s%d to %s:%s:%d end", config.Protocol, config.SrcPort, config.PeerNode, config.DstHost, config.DstPort)
isClient := false isClient := false
// client side tid=0, assign random uint64 // client side tid=0, assign random uint64
if tid == 0 { if tid == 0 {
@@ -320,11 +326,11 @@ func (pn *P2PNetwork) addDirectTunnel(config AppConfig, tid uint64) (*P2PTunnel,
} }
// client side checking // client side checking
gLog.Println(LevelINFO, "tunnel already exist ", config.PeerNode) gLog.Println(LvINFO, "tunnel already exist ", config.PeerNode)
isActive := t.checkActive() isActive := t.checkActive()
// inactive, close it // inactive, close it
if !isActive { 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() t.close()
} else { } else {
// active // active
@@ -346,47 +352,40 @@ func (pn *P2PNetwork) addDirectTunnel(config AppConfig, tid uint64) (*P2PTunnel,
t.init() t.init()
if isClient { if isClient {
if err := t.connect(); err != nil { if err := t.connect(); err != nil {
gLog.Println(LevelERROR, "p2pTunnel connect error:", err) gLog.Println(LvERROR, "p2pTunnel connect error:", err)
return t, err return t, err
} }
} else { } 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 { if err := t.listen(); err != nil {
gLog.Println(LevelERROR, "p2pTunnel listen error:", err) gLog.Println(LvERROR, "p2pTunnel listen error:", err)
return t, err return t, err
} }
} }
} }
// store it when success // store it when success
gLog.Printf(LevelDEBUG, "store tunnel %d", tid) gLog.Printf(LvDEBUG, "store tunnel %d", tid)
pn.allTunnels.Store(tid, t) pn.allTunnels.Store(tid, t)
return t, nil return t, nil
} }
func (pn *P2PNetwork) init() error { func (pn *P2PNetwork) init() error {
gLog.Println(LevelINFO, "init start") gLog.Println(LvINFO, "init start")
var err error var err error
for { for {
// detect nat type // detect nat type
pn.config.publicIP, pn.config.natType, err = getNATType(pn.config.ServerHost, pn.config.UDPPort1, pn.config.UDPPort2) pn.config.publicIP, pn.config.natType, pn.config.hasUPNPorNATPMP, err = getNATType(pn.config.ServerHost, pn.config.UDPPort1, pn.config.UDPPort2)
// TODO rm test s2s // for testcase
if strings.Contains(pn.config.Node, "openp2pS2STest") { if strings.Contains(pn.config.Node, "openp2pS2STest") {
pn.config.natType = NATSymmetric pn.config.natType = NATSymmetric
} }
if strings.Contains(pn.config.Node, "openp2pC2CTest") {
pn.config.natType = NATCone
}
if err != nil { if err != nil {
gLog.Println(LevelDEBUG, "detect NAT type error:", err) gLog.Println(LvDEBUG, "detect NAT type error:", err)
break 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) gatewayURL := fmt.Sprintf("%s:%d", pn.config.ServerHost, pn.config.ServerPort)
forwardPath := "/openp2p/v1/login" 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 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() pn.config.os = getOsName()
req := ReportBasic{ req := ReportBasic{
Mac: pn.config.mac, Mac: pn.config.mac,
LanIP: pn.config.localIP, LanIP: pn.config.localIP,
OS: pn.config.os, OS: pn.config.os,
Version: OpenP2PVersion, HasIPv4: pn.config.hasIPv4,
HasUPNPorNATPMP: pn.config.hasUPNPorNATPMP,
Version: OpenP2PVersion,
} }
rsp := netInfo() rsp := netInfo()
gLog.Println(LevelDEBUG, "netinfo:", rsp) gLog.Println(LvDEBUG, "netinfo:", rsp)
if rsp != nil && rsp.Country != "" { if rsp != nil && rsp.Country != "" {
if len(rsp.IP) == net.IPv6len { if IsIPv6(rsp.IP.String()) {
pn.config.ipv6 = rsp.IP.String() pn.config.ipv6 = rsp.IP.String()
req.IPv6 = rsp.IP.String() req.IPv6 = rsp.IP.String()
} }
req.NetInfo = *rsp req.NetInfo = *rsp
} }
pn.write(MsgReport, MsgReportBasic, &req) pn.write(MsgReport, MsgReportBasic, &req)
gLog.Println(LevelDEBUG, "P2PNetwork init ok") gLog.Println(LvDEBUG, "P2PNetwork init ok")
break break
} }
if err != nil { if err != nil {
// init failed, retry // init failed, retry
pn.restartCh <- true pn.restartCh <- true
gLog.Println(LevelERROR, "P2PNetwork init error:", err) gLog.Println(LvERROR, "P2PNetwork init error:", err)
} }
return err return err
} }
@@ -449,7 +450,7 @@ func (pn *P2PNetwork) handleMessage(t int, msg []byte) {
head := openP2PHeader{} head := openP2PHeader{}
err := binary.Read(bytes.NewReader(msg[:openP2PHeaderSize]), binary.LittleEndian, &head) err := binary.Read(bytes.NewReader(msg[:openP2PHeaderSize]), binary.LittleEndian, &head)
if err != nil { if err != nil {
gLog.Println(LevelERROR, "handleMessage error:", err) gLog.Println(LvERROR, "handleMessage error:", err)
return return
} }
switch head.MainType { switch head.MainType {
@@ -458,11 +459,11 @@ func (pn *P2PNetwork) handleMessage(t int, msg []byte) {
rsp := LoginRsp{} rsp := LoginRsp{}
err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp) err = json.Unmarshal(msg[openP2PHeaderSize:], &rsp)
if err != nil { if err != nil {
gLog.Printf(LevelERROR, "wrong login response:%s", err) gLog.Printf(LvERROR, "wrong login response:%s", err)
return return
} }
if rsp.Error != 0 { 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 pn.running = false
} else { } else {
pn.serverTs = rsp.Ts pn.serverTs = rsp.Ts
@@ -472,10 +473,10 @@ func (pn *P2PNetwork) handleMessage(t int, msg []byte) {
gConf.setUser(rsp.User) gConf.setUser(rsp.User)
gConf.save() gConf.save()
pn.localTs = time.Now().Unix() 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: case MsgHeartbeat:
gLog.Printf(LevelDEBUG, "P2PNetwork heartbeat ok") gLog.Printf(LvDEBUG, "P2PNetwork heartbeat ok")
case MsgPush: case MsgPush:
handlePush(pn, head.SubType, msg) handlePush(pn, head.SubType, msg)
default: default:
@@ -488,21 +489,21 @@ func (pn *P2PNetwork) handleMessage(t int, msg []byte) {
} }
func (pn *P2PNetwork) readLoop() { func (pn *P2PNetwork) readLoop() {
gLog.Printf(LevelDEBUG, "P2PNetwork readLoop start") gLog.Printf(LvDEBUG, "P2PNetwork readLoop start")
pn.wg.Add(1) pn.wg.Add(1)
defer pn.wg.Done() defer pn.wg.Done()
for pn.running { for pn.running {
pn.conn.SetReadDeadline(time.Now().Add(NetworkHeartbeatTime + 10*time.Second)) pn.conn.SetReadDeadline(time.Now().Add(NetworkHeartbeatTime + 10*time.Second))
t, msg, err := pn.conn.ReadMessage() t, msg, err := pn.conn.ReadMessage()
if err != nil { if err != nil {
gLog.Printf(LevelERROR, "P2PNetwork read error:%s", err) gLog.Printf(LvERROR, "P2PNetwork read error:%s", err)
pn.conn.Close() pn.conn.Close()
pn.restartCh <- true pn.restartCh <- true
break break
} }
pn.handleMessage(t, msg) 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 { 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() pn.writeMtx.Lock()
defer pn.writeMtx.Unlock() defer pn.writeMtx.Unlock()
if err = pn.conn.WriteMessage(websocket.BinaryMessage, msg); err != nil { 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() pn.conn.Close()
} }
return err return err
} }
func (pn *P2PNetwork) relay(to uint64, body []byte) error { 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) i, ok := pn.allTunnels.Load(to)
if !ok { if !ok {
return nil 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 { 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 { if !pn.online {
return errors.New("client offline") return errors.New("client offline")
} }
@@ -559,7 +560,7 @@ func (pn *P2PNetwork) push(to string, subType uint16, packet interface{}) error
pn.writeMtx.Lock() pn.writeMtx.Lock()
defer pn.writeMtx.Unlock() defer pn.writeMtx.Unlock()
if err = pn.conn.WriteMessage(websocket.BinaryMessage, pushMsg); err != nil { 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() pn.conn.Close()
} }
return err return err
@@ -578,13 +579,13 @@ func (pn *P2PNetwork) read(node string, mainType uint16, subType uint16, timeout
for { for {
select { select {
case <-time.After(timeout): 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 return
case msg := <-ch: case msg := <-ch:
head = &openP2PHeader{} head = &openP2PHeader{}
err := binary.Read(bytes.NewReader(msg[:openP2PHeaderSize]), binary.LittleEndian, head) err := binary.Read(bytes.NewReader(msg[:openP2PHeaderSize]), binary.LittleEndian, head)
if err != nil { if err != nil {
gLog.Println(LevelERROR, "read msg error:", err) gLog.Println(LvERROR, "read msg error:", err)
break break
} }
if head.MainType != mainType || head.SubType != subType { if head.MainType != mainType || head.SubType != subType {

View File

@@ -14,7 +14,7 @@ import (
type P2PTunnel struct { type P2PTunnel struct {
pn *P2PNetwork pn *P2PNetwork
conn p2pConn conn underlay
hbTime time.Time hbTime time.Time
hbMtx sync.Mutex hbMtx sync.Mutex
hbTimeRelay time.Time hbTimeRelay time.Time
@@ -25,7 +25,7 @@ type P2PTunnel struct {
id uint64 id uint64
running bool running bool
runMtx sync.Mutex runMtx sync.Mutex
isServer bool // 0:server 1:client tunnelServer bool // different from underlayServer
coneLocalPort int coneLocalPort int
coneNatPort int coneNatPort int
} }
@@ -39,29 +39,47 @@ func (t *P2PTunnel) init() {
localPort := int(rand.Uint32()%10000 + 50000) localPort := int(rand.Uint32()%10000 + 50000)
if t.pn.config.natType == NATCone { if t.pn.config.natType == NATCone {
// prepare one random cone hole // 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.coneLocalPort = localPort
t.coneNatPort = port1 t.coneNatPort = port1
t.la = &net.UDPAddr{IP: net.ParseIP(t.pn.config.localIP), Port: t.coneLocalPort} t.la = &net.UDPAddr{IP: net.ParseIP(t.pn.config.localIP), Port: t.coneLocalPort}
} else { } else {
t.coneLocalPort = localPort 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} 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 { func (t *P2PTunnel) connect() error {
gLog.Printf(LevelDEBUG, "start p2pTunnel to %s ", t.config.PeerNode) gLog.Printf(LvDEBUG, "start p2pTunnel to %s ", t.config.PeerNode)
t.isServer = false t.tunnelServer = false
appKey := uint64(0)
req := PushConnectReq{ req := PushConnectReq{
Token: t.config.peerToken, Token: t.config.peerToken,
From: t.pn.config.Node, From: t.pn.config.Node,
FromToken: t.pn.config.Token, FromToken: t.pn.config.Token,
FromIP: t.pn.config.publicIP, FromIP: t.pn.config.publicIP,
ConeNatPort: t.coneNatPort, ConeNatPort: t.coneNatPort,
NatType: t.pn.config.natType, NatType: t.pn.config.natType,
ID: t.id} 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) t.pn.push(t.config.PeerNode, MsgPushConnectReq, req)
head, body := t.pn.read(t.config.PeerNode, MsgPush, MsgPushConnectRsp, time.Second*10) head, body := t.pn.read(t.config.PeerNode, MsgPush, MsgPushConnectRsp, time.Second*10)
if head == nil { if head == nil {
@@ -70,19 +88,23 @@ func (t *P2PTunnel) connect() error {
rsp := PushConnectRsp{} rsp := PushConnectRsp{}
err := json.Unmarshal(body, &rsp) err := json.Unmarshal(body, &rsp)
if err != nil { if err != nil {
gLog.Printf(LevelERROR, "wrong MsgPushConnectRsp:%s", err) gLog.Printf(LvERROR, "wrong MsgPushConnectRsp:%s", err)
return err return err
} }
// gLog.Println(LevelINFO, rsp) // gLog.Println(LevelINFO, rsp)
if rsp.Error != 0 { if rsp.Error != 0 {
return errors.New(rsp.Detail) 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.peerConeNatPort = rsp.ConeNatPort
t.config.peerIP = rsp.FromIP t.config.peerIP = rsp.FromIP
err = t.handshake() err = t.start()
if err != nil { if err != nil {
gLog.Println(LevelERROR, "handshake error:", err) gLog.Println(LvERROR, "handshake error:", err)
err = ErrorHandshake err = ErrorHandshake
} }
return err return err
@@ -135,6 +157,20 @@ func (t *P2PTunnel) close() {
t.pn.allTunnels.Delete(t.id) 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 { func (t *P2PTunnel) handshake() error {
if t.config.peerConeNatPort > 0 { if t.config.peerConeNatPort > 0 {
var err error var err error
@@ -143,7 +179,7 @@ func (t *P2PTunnel) handshake() error {
return err return err
} }
} }
gLog.Println(LevelDEBUG, "handshake to ", t.config.PeerNode) gLog.Println(LvDEBUG, "handshake to ", t.config.PeerNode)
var err error var err error
// TODO: handle NATNone, nodes with public ip has no punching // 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) { 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") return errors.New("unknown error")
} }
if err != nil { if err != nil {
gLog.Println(LevelERROR, "punch handshake error:", err) gLog.Println(LvERROR, "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)
return err return err
} }
gLog.Printf(LvDEBUG, "handshake to %s ok", t.config.PeerNode)
return nil return nil
} }
func (t *P2PTunnel) run() error { func (t *P2PTunnel) connectUnderlay() (err error) {
if t.isServer { if !t.isSupportTCP() {
qConn, e := listenQuic(t.la.String(), TunnelIdleTimeout) t.conn, err = t.connectUnderlayQuic()
if e != nil { if err != nil {
gLog.Println(LevelINFO, "listen quic error:", e, ", retry...") return err
time.Sleep(time.Millisecond * 10)
qConn, e = listenQuic(t.la.String(), TunnelIdleTimeout)
if e != nil {
return fmt.Errorf("listen quic error:%s", e)
}
} }
t.pn.push(t.config.PeerNode, MsgPushQuicConnect, nil) } else {
e = qConn.Accept() // TODO: udp or tcp first?
if e != nil { // 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() qConn.CloseListener()
return fmt.Errorf("accept quic error:%s", e) return nil, fmt.Errorf("accept quic error:%s", err)
} }
_, buff, err := qConn.ReadMessage() _, buff, err := qConn.ReadBuffer()
if e != nil { if err != nil {
qConn.listener.Close() 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 { if buff != nil {
gLog.Println(LevelDEBUG, string(buff)) gLog.Println(LvDEBUG, string(buff))
} }
qConn.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, []byte("OpenP2P,hello2")) qConn.WriteBytes(MsgP2P, MsgTunnelHandshakeAck, []byte("OpenP2P,hello2"))
gLog.Println(LevelDEBUG, "quic connection ok") gLog.Println(LvDEBUG, "quic connection ok")
t.conn = qConn return qConn, nil
t.setRun(true)
go t.readLoop()
go t.writeLoop()
return nil
} }
//else //else
@@ -211,44 +267,133 @@ func (t *P2PTunnel) run() error {
time.Sleep(time.Millisecond * 10) time.Sleep(time.Millisecond * 10)
conn, e = net.ListenUDP("udp", t.la) conn, e = net.ListenUDP("udp", t.la)
if e != nil { 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) t.pn.read(t.config.PeerNode, MsgPush, MsgPushUnderlayConnect, time.Second*5)
gLog.Println(LevelDEBUG, "quic dial to ", t.ra.String()) gLog.Println(LvDEBUG, "quic dial to ", t.ra.String())
qConn, e := dialQuic(conn, t.ra, TunnelIdleTimeout) qConn, e = dialQuic(conn, t.ra, TunnelIdleTimeout)
if e != nil { 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() handshakeBegin := time.Now()
qConn.WriteBytes(MsgP2P, MsgTunnelHandshake, []byte("OpenP2P,hello")) qConn.WriteBytes(MsgP2P, MsgTunnelHandshake, []byte("OpenP2P,hello"))
_, buff, err := qConn.ReadMessage() _, buff, err := qConn.ReadBuffer()
if e != nil { if e != nil {
qConn.listener.Close() qConn.listener.Close()
return fmt.Errorf("read MsgTunnelHandshake error:%s", err) return nil, fmt.Errorf("read MsgTunnelHandshake error:%s", err)
} }
if buff != nil { if buff != nil {
gLog.Println(LevelDEBUG, string(buff)) gLog.Println(LvDEBUG, string(buff))
} }
gLog.Println(LevelINFO, "rtt=", time.Since(handshakeBegin)) gLog.Println(LvINFO, "rtt=", time.Since(handshakeBegin))
gLog.Println(LevelDEBUG, "quic connection ok") gLog.Println(LvDEBUG, "quic connection ok")
t.conn = qConn return qConn, nil
t.setRun(true) }
go t.readLoop()
go t.writeLoop() // websocket
return nil 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() { func (t *P2PTunnel) readLoop() {
decryptData := make([]byte, ReadBuffLen+PaddingSize) // 16 bytes for padding 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() { for t.isRuning() {
t.conn.SetReadDeadline(time.Now().Add(TunnelIdleTimeout)) t.conn.SetReadDeadline(time.Now().Add(TunnelIdleTimeout))
head, body, err := t.conn.ReadMessage() head, body, err := t.conn.ReadBuffer()
if err != nil { if err != nil {
if t.isRuning() { 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 break
} }
@@ -258,22 +403,22 @@ func (t *P2PTunnel) readLoop() {
switch head.SubType { switch head.SubType {
case MsgTunnelHeartbeat: case MsgTunnelHeartbeat:
t.conn.WriteBytes(MsgP2P, MsgTunnelHeartbeatAck, nil) 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: case MsgTunnelHeartbeatAck:
t.hbMtx.Lock() t.hbMtx.Lock()
t.hbTime = time.Now() t.hbTime = time.Now()
t.hbMtx.Unlock() 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: case MsgOverlayData:
if len(body) < overlayHeaderSize { if len(body) < overlayHeaderSize {
continue continue
} }
overlayID := binary.LittleEndian.Uint64(body[:8]) 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) s, ok := t.overlayConns.Load(overlayID)
if !ok { if !ok {
// debug level, when overlay connection closed, always has some packet not found tunnel // 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 continue
} }
overlayConn, ok := s.(*overlayConn) overlayConn, ok := s.(*overlayConn)
@@ -287,10 +432,10 @@ func (t *P2PTunnel) readLoop() {
} }
_, err = overlayConn.Write(payload) _, err = overlayConn.Write(payload)
if err != nil { if err != nil {
gLog.Println(LevelERROR, "overlay write error:", err) gLog.Println(LvERROR, "overlay write error:", err)
} }
case MsgRelayData: 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 { if len(body) < 8 {
continue continue
} }
@@ -300,10 +445,10 @@ func (t *P2PTunnel) readLoop() {
req := RelayHeartbeat{} req := RelayHeartbeat{}
err := json.Unmarshal(body, &req) err := json.Unmarshal(body, &req)
if err != nil { if err != nil {
gLog.Printf(LevelERROR, "wrong RelayHeartbeat:%s", err) gLog.Printf(LvERROR, "wrong RelayHeartbeat:%s", err)
continue 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) relayHead := new(bytes.Buffer)
binary.Write(relayHead, binary.LittleEndian, req.RelayTunnelID) binary.Write(relayHead, binary.LittleEndian, req.RelayTunnelID)
msg, _ := newMessage(MsgP2P, MsgRelayHeartbeatAck, &req) msg, _ := newMessage(MsgP2P, MsgRelayHeartbeatAck, &req)
@@ -313,26 +458,26 @@ func (t *P2PTunnel) readLoop() {
req := RelayHeartbeat{} req := RelayHeartbeat{}
err := json.Unmarshal(body, &req) err := json.Unmarshal(body, &req)
if err != nil { if err != nil {
gLog.Printf(LevelERROR, "wrong RelayHeartbeat:%s", err) gLog.Printf(LvERROR, "wrong RelayHeartbeat:%s", err)
continue continue
} }
gLog.Printf(LevelDEBUG, "got MsgRelayHeartbeatAck to %d", req.AppID) gLog.Printf(LvDEBUG, "got MsgRelayHeartbeatAck to %d", req.AppID)
t.pn.updateAppHeartbeat(req.AppID) t.pn.updateAppHeartbeat(req.AppID)
case MsgOverlayConnectReq: case MsgOverlayConnectReq:
req := OverlayConnectReq{} req := OverlayConnectReq{}
err := json.Unmarshal(body, &req) err := json.Unmarshal(body, &req)
if err != nil { if err != nil {
gLog.Printf(LevelERROR, "wrong MsgOverlayConnectReq:%s", err) gLog.Printf(LvERROR, "wrong MsgOverlayConnectReq:%s", err)
continue continue
} }
// app connect only accept token(not relay totp token), avoid someone using the share relay node's token // 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 { if req.Token != t.pn.config.Token {
gLog.Println(LevelERROR, "Access Denied:", req.Token) gLog.Println(LvERROR, "Access Denied:", req.Token)
continue continue
} }
overlayID := req.ID 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{ oConn := overlayConn{
tunnel: t, tunnel: t,
id: overlayID, 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) oConn.connTCP, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", req.DstIP, req.DstPort), time.Second*5)
} }
if err != nil { if err != nil {
gLog.Println(LevelERROR, err) gLog.Println(LvERROR, err)
continue continue
} }
@@ -365,11 +510,11 @@ func (t *P2PTunnel) readLoop() {
req := OverlayDisconnectReq{} req := OverlayDisconnectReq{}
err := json.Unmarshal(body, &req) err := json.Unmarshal(body, &req)
if err != nil { if err != nil {
gLog.Printf(LevelERROR, "wrong OverlayDisconnectRequest:%s", err) gLog.Printf(LvERROR, "wrong OverlayDisconnectRequest:%s", err)
continue continue
} }
overlayID := req.ID 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) i, ok := t.overlayConns.Load(overlayID)
if ok { if ok {
oConn := i.(*overlayConn) oConn := i.(*overlayConn)
@@ -380,32 +525,49 @@ func (t *P2PTunnel) readLoop() {
} }
t.setRun(false) t.setRun(false)
t.conn.Close() 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) tc := time.NewTicker(TunnelHeartbeatTime)
defer tc.Stop() 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() { for t.isRuning() {
select { select {
case <-tc.C: case <-tc.C:
// tunnel send // tunnel send
err := t.conn.WriteBytes(MsgP2P, MsgTunnelHeartbeat, nil) err := t.conn.WriteBytes(MsgP2P, MsgTunnelHeartbeat, nil)
if err != 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) t.setRun(false)
return 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 { func (t *P2PTunnel) listen() error {
gLog.Printf(LevelDEBUG, "p2ptunnel wait for connecting") // notify client to connect
t.isServer = true rsp := PushConnectRsp{
return t.handshake() 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) { func (t *P2PTunnel) closeOverlayConns(appID uint64) {
@@ -424,3 +586,24 @@ func (t *P2PTunnel) closeOverlayConns(appID uint64) {
return true 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
}

View File

@@ -10,8 +10,9 @@ import (
"time" "time"
) )
const OpenP2PVersion = "1.4.2" const OpenP2PVersion = "1.5.6"
const ProducnName string = "openp2p" const ProducnName string = "openp2p"
const LeastSupportTCPVersion = "1.5.0"
type openP2PHeader struct { type openP2PHeader struct {
DataLen uint32 DataLen uint32
@@ -69,6 +70,7 @@ const (
MsgReport = 6 MsgReport = 6
) )
// TODO: seperate node push and web push.
const ( const (
MsgPushRsp = 0 MsgPushRsp = 0
MsgPushConnectReq = 1 MsgPushConnectReq = 1
@@ -78,11 +80,12 @@ const (
MsgPushAddRelayTunnelRsp = 5 MsgPushAddRelayTunnelRsp = 5
MsgPushUpdate = 6 MsgPushUpdate = 6
MsgPushReportApps = 7 MsgPushReportApps = 7
MsgPushQuicConnect = 8 MsgPushUnderlayConnect = 8
MsgPushEditApp = 9 MsgPushEditApp = 9
MsgPushSwitchApp = 10 MsgPushSwitchApp = 10
MsgPushRestart = 11 MsgPushRestart = 11
MsgPushEditNode = 12 MsgPushEditNode = 12
MsgPushAPPKey = 13
) )
// MsgP2P sub type message // MsgP2P sub type message
@@ -131,7 +134,7 @@ const (
AESKeySize = 16 AESKeySize = 16
MaxRetry = 10 MaxRetry = 10
RetryInterval = time.Second * 30 RetryInterval = time.Second * 30
PublicIPEchoTimeout = time.Second * 3 PublicIPEchoTimeout = time.Second * 1
NatTestTimeout = time.Second * 10 NatTestTimeout = time.Second * 10
ClientAPITimeout = time.Second * 10 ClientAPITimeout = time.Second * 10
MaxDirectTry = 5 MaxDirectTry = 5
@@ -145,6 +148,13 @@ const (
NATUnknown = 314 NATUnknown = 314
) )
// underlay protocol
const (
UderlayAuto = "auto"
UderlayQUIC = "quic"
UderlayTCP = "tcp"
)
func newMessage(mainType uint16, subType uint16, packet interface{}) ([]byte, error) { func newMessage(mainType uint16, subType uint16, packet interface{}) ([]byte, error) {
data, err := json.Marshal(packet) data, err := json.Marshal(packet)
if err != nil { if err != nil {
@@ -170,23 +180,32 @@ func nodeNameToID(name string) uint64 {
} }
type PushConnectReq struct { type PushConnectReq struct {
From string `json:"from,omitempty"` From string `json:"from,omitempty"`
FromToken uint64 `json:"fromToken,omitempty"` //my token FromToken uint64 `json:"fromToken,omitempty"` //my token
Token uint64 `json:"token,omitempty"` // totp token Version string `json:"version,omitempty"`
ConeNatPort int `json:"coneNatPort,omitempty"` Token uint64 `json:"token,omitempty"` // totp token
NatType int `json:"natType,omitempty"` ConeNatPort int `json:"coneNatPort,omitempty"` // if isPublic, is public port
FromIP string `json:"fromIP,omitempty"` NatType int `json:"natType,omitempty"`
ID uint64 `json:"id,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 { type PushConnectRsp struct {
Error int `json:"error,omitempty"` Error int `json:"error,omitempty"`
From string `json:"from,omitempty"` From string `json:"from,omitempty"`
To string `json:"to,omitempty"` To string `json:"to,omitempty"`
Detail string `json:"detail,omitempty"` Detail string `json:"detail,omitempty"`
NatType int `json:"natType,omitempty"` NatType int `json:"natType,omitempty"`
ConeNatPort int `json:"coneNatPort,omitempty"` HasIPv4 int `json:"hasIPv4,omitempty"`
FromIP string `json:"fromIP,omitempty"` IPv6 string `json:"IPv6,omitempty"`
ID uint64 `json:"id,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 { type PushRsp struct {
Error int `json:"error,omitempty"` Error int `json:"error,omitempty"`
@@ -246,8 +265,13 @@ type AddRelayTunnelReq struct {
From string `json:"from,omitempty"` From string `json:"from,omitempty"`
RelayName string `json:"relayName,omitempty"` RelayName string `json:"relayName,omitempty"`
RelayToken uint64 `json:"relayToken,omitempty"` RelayToken uint64 `json:"relayToken,omitempty"`
AppID uint64 `json:"appID,omitempty"` AppID uint64 `json:"appID,omitempty"` // deprecated
AppKey uint64 `json:"appKey,omitempty"` AppKey uint64 `json:"appKey,omitempty"` // deprecated
}
type APPKeySync struct {
AppID uint64 `json:"appID,omitempty"`
AppKey uint64 `json:"appKey,omitempty"`
} }
type RelayHeartbeat struct { type RelayHeartbeat struct {
@@ -256,12 +280,14 @@ type RelayHeartbeat struct {
} }
type ReportBasic struct { type ReportBasic struct {
OS string `json:"os,omitempty"` OS string `json:"os,omitempty"`
Mac string `json:"mac,omitempty"` Mac string `json:"mac,omitempty"`
LanIP string `json:"lanIP,omitempty"` LanIP string `json:"lanIP,omitempty"`
IPv6 string `json:"IPv6,omitempty"` HasIPv4 int `json:"hasIPv4,omitempty"`
Version string `json:"version,omitempty"` IPv6 string `json:"IPv6,omitempty"`
NetInfo NetInfo `json:"netInfo,omitempty"` HasUPNPorNATPMP int `json:"hasUPNPorNATPMP,omitempty"`
Version string `json:"version,omitempty"`
NetInfo NetInfo `json:"netInfo,omitempty"`
} }
type ReportConnect struct { type ReportConnect struct {

4
udp.go
View File

@@ -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)) deadline := time.Now().Add(time.Millisecond * time.Duration(timeout))
err = conn.SetReadDeadline(deadline) err = conn.SetReadDeadline(deadline)
if err != nil { if err != nil {
gLog.Println(LevelERROR, "SetReadDeadline error") gLog.Println(LvERROR, "SetReadDeadline error")
return nil, nil, nil, 0, err return nil, nil, nil, 0, err
} }
} }
@@ -37,7 +37,7 @@ func UDPRead(conn *net.UDPConn, timeout int) (ra net.Addr, head *openP2PHeader,
head = &openP2PHeader{} head = &openP2PHeader{}
err = binary.Read(bytes.NewReader(result[:openP2PHeaderSize]), binary.LittleEndian, head) err = binary.Read(bytes.NewReader(result[:openP2PHeaderSize]), binary.LittleEndian, head)
if err != nil { if err != nil {
gLog.Println(LevelERROR, "parse p2pheader error:", err) gLog.Println(LvERROR, "parse p2pheader error:", err)
return nil, nil, nil, 0, err return nil, nil, nil, 0, err
} }
return return

16
underlay.go Normal file
View 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
View 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
View 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
View 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
}

View File

@@ -17,8 +17,8 @@ import (
) )
func update() { func update() {
gLog.Println(LevelINFO, "update start") gLog.Println(LvINFO, "update start")
defer gLog.Println(LevelINFO, "update end") defer gLog.Println(LvINFO, "update end")
c := http.Client{ c := http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
@@ -29,43 +29,43 @@ func update() {
goarch := runtime.GOARCH 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)) 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 { if err != nil {
gLog.Println(LevelERROR, "update:query update list failed:", err) gLog.Println(LvERROR, "update:query update list failed:", err)
return return
} }
defer rsp.Body.Close() defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK { if rsp.StatusCode != http.StatusOK {
gLog.Println(LevelERROR, "get update info error:", rsp.Status) gLog.Println(LvERROR, "get update info error:", rsp.Status)
return return
} }
rspBuf, err := ioutil.ReadAll(rsp.Body) rspBuf, err := ioutil.ReadAll(rsp.Body)
if err != nil { if err != nil {
gLog.Println(LevelERROR, "update:read update list failed:", err) gLog.Println(LvERROR, "update:read update list failed:", err)
return return
} }
updateInfo := UpdateInfo{} updateInfo := UpdateInfo{}
err = json.Unmarshal(rspBuf, &updateInfo) err = json.Unmarshal(rspBuf, &updateInfo)
if err != nil { if err != nil {
gLog.Println(LevelERROR, rspBuf, " update info decode error:", err) gLog.Println(LvERROR, rspBuf, " update info decode error:", err)
return return
} }
if updateInfo.Error != 0 { if updateInfo.Error != 0 {
gLog.Println(LevelERROR, "update error:", updateInfo.Error, updateInfo.ErrorDetail) gLog.Println(LvERROR, "update error:", updateInfo.Error, updateInfo.ErrorDetail)
return return
} }
err = updateFile(updateInfo.Url, "", "openp2p") err = updateFile(updateInfo.Url, "", "openp2p")
if err != nil { if err != nil {
gLog.Println(LevelERROR, "update: download failed:", err) gLog.Println(LvERROR, "update: download failed:", err)
return return
} }
} }
// todo rollback on error // todo rollback on error
func updateFile(url string, checksum string, dst string) 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" tmpFile := filepath.Dir(os.Args[0]) + "/openp2p.tmp"
output, err := os.OpenFile(tmpFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0776) output, err := os.OpenFile(tmpFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0776)
if err != nil { if err != nil {
gLog.Printf(LevelERROR, "OpenFile %s error:%s", tmpFile, err) gLog.Printf(LvERROR, "OpenFile %s error:%s", tmpFile, err)
return err return err
} }
tr := &http.Transport{ tr := &http.Transport{
@@ -74,31 +74,31 @@ func updateFile(url string, checksum string, dst string) error {
client := &http.Client{Transport: tr} client := &http.Client{Transport: tr}
response, err := client.Get(url) response, err := client.Get(url)
if err != nil { 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() output.Close()
return err return err
} }
defer response.Body.Close() defer response.Body.Close()
n, err := io.Copy(output, response.Body) n, err := io.Copy(output, response.Body)
if err != nil { if err != nil {
gLog.Printf(LevelERROR, "io.Copy error:%s", err) gLog.Printf(LvERROR, "io.Copy error:%s", err)
output.Close() output.Close()
return err return err
} }
output.Sync() output.Sync()
output.Close() output.Close()
gLog.Println(LevelINFO, "download ", url, " ok") gLog.Println(LvINFO, "download ", url, " ok")
gLog.Printf(LevelINFO, "size: %d bytes", n) gLog.Printf(LvINFO, "size: %d bytes", n)
err = os.Rename(os.Args[0], os.Args[0]+"0") err = os.Rename(os.Args[0], os.Args[0]+"0")
if err != nil && os.IsExist(err) { 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 // extract
gLog.Println(LevelINFO, "extract files") gLog.Println(LvINFO, "extract files")
err = extract(filepath.Dir(os.Args[0]), tmpFile) err = extract(filepath.Dir(os.Args[0]), tmpFile)
if err != nil { 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]) os.Rename(os.Args[0]+"0", os.Args[0])
return err return err
} }

404
upnp.go Normal file
View 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
View 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
View 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
View 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()
}
}