Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
763a379053 | ||
|
|
72c2175daf | ||
|
|
1749ae8d12 | ||
|
|
4c645d30aa | ||
|
|
3583ea3270 | ||
|
|
a8dcc3871d | ||
|
|
b9f350bb8d | ||
|
|
000ea7dcb0 |
@@ -12,10 +12,13 @@ rakshasa是一个使用Go语言编写的强大多级代理工具,专为实现*
|
|||||||
- 控制节点,全功能节点
|
- 控制节点,全功能节点
|
||||||
|
|
||||||
## 项目结构示例和截图
|
## 项目结构示例和截图
|
||||||
|
|
||||||
[点击查看更多介绍](./readme/rakshasa项目设计.md)
|
[点击查看更多介绍](./readme/rakshasa项目设计.md)
|
||||||
|
|
||||||
[win10+Proxifier实现内网穿透](./readme/rakshasa内网穿透--win10+Proxifier使用案例.md)
|
[win10+Proxifier实现内网穿透](./readme/rakshasa内网穿透--win10+Proxifier使用案例.md)
|
||||||
|
|
||||||
|
[rakshasa主被控设计说明](./readme/rakshasa主被控设计说明.md)
|
||||||
|
|
||||||
## 版本迭代
|
## 版本迭代
|
||||||
- **v0.1.0** 2023-03-28
|
- **v0.1.0** 2023-03-28
|
||||||
- 首次发布
|
- 首次发布
|
||||||
@@ -28,7 +31,10 @@ rakshasa是一个使用Go语言编写的强大多级代理工具,专为实现*
|
|||||||
- 增加http_proxy重连逻辑,节点掉线后重连http代理能够正常重连使用
|
- 增加http_proxy重连逻辑,节点掉线后重连http代理能够正常重连使用
|
||||||
- 优化节点重连逻辑
|
- 优化节点重连逻辑
|
||||||
- 增加uuid选项,默认uuid使用网卡mac作为随机数种子,进行生成
|
- 增加uuid选项,默认uuid使用网卡mac作为随机数种子,进行生成
|
||||||
|
- **v0.2.3** 2023-04-22
|
||||||
|
- 优化了连接逻辑,尝试解决连接失败导致nil的bug
|
||||||
|
- 修改一个map为sync.Map,减少代码上lock的使用量
|
||||||
|
- 增加了 主控端、被控端说明的md文档
|
||||||
## 编译与使用
|
## 编译与使用
|
||||||
|
|
||||||
生成新的证书,编译所有版本节点
|
生成新的证书,编译所有版本节点
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+18
-18
@@ -18,8 +18,8 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Debug bool = false
|
var Debug bool = true
|
||||||
var NoPing bool = false
|
var NoPing bool = true
|
||||||
var DebugLock bool = false
|
var DebugLock bool = false
|
||||||
var DebugLockMap sync.Map
|
var DebugLockMap sync.Map
|
||||||
|
|
||||||
@@ -201,7 +201,7 @@ var msgId uint32
|
|||||||
|
|
||||||
func (m *Msg) Marshal() []byte {
|
func (m *Msg) Marshal() []byte {
|
||||||
l := UUID_LEN*2 + 4 + 1 + 5 + len(m.CmdData)
|
l := UUID_LEN*2 + 4 + 1 + 5 + len(m.CmdData)
|
||||||
data := make([]byte, l+2)
|
data := make([]byte, l)
|
||||||
data1 := make([]byte, l+2)
|
data1 := make([]byte, l+2)
|
||||||
data1[0] = byte(l)
|
data1[0] = byte(l)
|
||||||
data1[1] = byte(l >> 8)
|
data1[1] = byte(l >> 8)
|
||||||
@@ -209,24 +209,24 @@ func (m *Msg) Marshal() []byte {
|
|||||||
ut, _ := uuid.Parse(m.To)
|
ut, _ := uuid.Parse(m.To)
|
||||||
bf, _ := uf.MarshalBinary()
|
bf, _ := uf.MarshalBinary()
|
||||||
bt, _ := ut.MarshalBinary()
|
bt, _ := ut.MarshalBinary()
|
||||||
copy(data[2:], bf)
|
copy(data, bf)
|
||||||
copy(data[2+UUID_LEN:], bt)
|
copy(data[+UUID_LEN:], bt)
|
||||||
b := 2 + 2*UUID_LEN
|
|
||||||
if m.MsgId == 0 { //id不为0
|
if m.MsgId == 0 { //id不为0
|
||||||
m.MsgId = atomic.AddUint32(&msgId, 1)
|
m.MsgId = atomic.AddUint32(&msgId, 1)
|
||||||
}
|
}
|
||||||
data[b] = byte(m.MsgId)
|
data[2*UUID_LEN] = byte(m.MsgId)
|
||||||
data[b+1] = byte(m.MsgId >> 8)
|
data[2*UUID_LEN+1] = byte(m.MsgId >> 8)
|
||||||
data[b+2] = byte(m.MsgId >> 16)
|
data[2*UUID_LEN+2] = byte(m.MsgId >> 16)
|
||||||
data[b+3] = byte(m.MsgId >> 24)
|
data[2*UUID_LEN+3] = byte(m.MsgId >> 24)
|
||||||
data[b+4] = m.Ttl
|
data[2*UUID_LEN+4] = m.Ttl
|
||||||
data[b+5] = m.CmdOpteion
|
data[2*UUID_LEN+5] = m.CmdOpteion
|
||||||
data[b+6] = byte(m.CmdId)
|
data[2*UUID_LEN+6] = byte(m.CmdId)
|
||||||
data[b+7] = byte(m.CmdId >> 8)
|
data[2*UUID_LEN+7] = byte(m.CmdId >> 8)
|
||||||
data[b+8] = byte(m.CmdId >> 16)
|
data[2*UUID_LEN+8] = byte(m.CmdId >> 16)
|
||||||
data[b+9] = byte(m.CmdId >> 24)
|
data[2*UUID_LEN+9] = byte(m.CmdId >> 24)
|
||||||
copy(data[2+2*UUID_LEN+4+1+5:], m.CmdData)
|
copy(data[2*UUID_LEN+4+1+5:], m.CmdData)
|
||||||
aes.AesCtrEncrypt(data1[2:], data[2:])
|
aes.AesCtrEncrypt(data1[2:], data)
|
||||||
return data1
|
return data1
|
||||||
}
|
}
|
||||||
func UnmarshalMsg(data []byte) (msg *Msg) {
|
func UnmarshalMsg(data []byte) (msg *Msg) {
|
||||||
|
|||||||
+86
-103
@@ -11,6 +11,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"rakshasa_lite/aes"
|
"rakshasa_lite/aes"
|
||||||
"rakshasa_lite/common"
|
"rakshasa_lite/common"
|
||||||
|
"runtime/debug"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -33,13 +34,13 @@ type Conn struct {
|
|||||||
node *node
|
node *node
|
||||||
nodeaddr string
|
nodeaddr string
|
||||||
//key string
|
//key string
|
||||||
remoteAddr string
|
remoteAddr string
|
||||||
inChan chan func()
|
inChan chan func()
|
||||||
OutChan chan []byte
|
OutChan chan []byte
|
||||||
close chan string
|
close chan string
|
||||||
isClient bool
|
isClient bool
|
||||||
nodeConn *tls.Conn
|
nodeConn *tls.Conn
|
||||||
regResult chan RegMsg
|
regResult chan RegMsg
|
||||||
}
|
}
|
||||||
|
|
||||||
type serverListen struct {
|
type serverListen struct {
|
||||||
@@ -484,7 +485,6 @@ func (c *Conn) handlerNodeRead() {
|
|||||||
_, err = io.ReadFull(c.nodeConn, buf)
|
_, err = io.ReadFull(c.nodeConn, buf)
|
||||||
b := aes.AesCtrDecrypt(buf)
|
b := aes.AesCtrDecrypt(buf)
|
||||||
msg := common.UnmarshalMsg(b)
|
msg := common.UnmarshalMsg(b)
|
||||||
|
|
||||||
if msg.To == common.NoneUUID.String() && c.node == nil {
|
if msg.To == common.NoneUUID.String() && c.node == nil {
|
||||||
c.inChan <- func() {
|
c.inChan <- func() {
|
||||||
newNode := &node{
|
newNode := &node{
|
||||||
@@ -493,81 +493,75 @@ func (c *Conn) handlerNodeRead() {
|
|||||||
newNode.do(msg)
|
newNode.do(msg)
|
||||||
}
|
}
|
||||||
} else if msg.To == currentNode.uuid {
|
} else if msg.To == currentNode.uuid {
|
||||||
|
v, ok := nodeMap.Load(msg.From)
|
||||||
func() {
|
if ok && v.(*node).port != 0 {
|
||||||
l := clientLock.RLock()
|
c.inChan <- func() {
|
||||||
v, ok := nodeMap[msg.From]
|
v.(*node).do(msg)
|
||||||
l.RUnlock()
|
}
|
||||||
if ok && v.port != 0 {
|
} else {
|
||||||
c.inChan <- func() {
|
if !ok {
|
||||||
v.do(msg)
|
newNode := &node{
|
||||||
|
uuid: msg.From,
|
||||||
|
conn: c,
|
||||||
|
waitMsg: []*common.Msg{msg},
|
||||||
}
|
}
|
||||||
} else {
|
result := make(chan interface{}, 1)
|
||||||
l := clientLock.Lock()
|
id := newNode.storeQuery(result)
|
||||||
v, ok := nodeMap[msg.From]
|
nodeMap.Store(msg.From, newNode)
|
||||||
if !ok {
|
newNode.Write(common.CMD_GET_CURRENT_NODE, id, []byte{1}) //获取丢失节点的信息
|
||||||
newNode := &node{
|
go func() {
|
||||||
uuid: msg.From,
|
defer func() {
|
||||||
conn: c,
|
if err := recover(); err != nil {
|
||||||
waitMsg: []*common.Msg{msg},
|
fmt.Println(err)
|
||||||
}
|
debug.PrintStack()
|
||||||
result := make(chan interface{}, 1)
|
}
|
||||||
id := newNode.storeQuery(result)
|
newNode.deleteQuery(id)
|
||||||
|
}()
|
||||||
nodeMap[msg.From] = newNode
|
select {
|
||||||
l.Unlock()
|
case res := <-result:
|
||||||
newNode.Write(common.CMD_GET_CURRENT_NODE, id, []byte{1}) //获取丢失节点的信息
|
if res == nil {
|
||||||
go func() {
|
for _, m := range newNode.waitMsg {
|
||||||
defer newNode.deleteQuery(id)
|
c.inChan <- func() {
|
||||||
select {
|
newNode.do(m)
|
||||||
case res := <-result:
|
|
||||||
if res == nil {
|
|
||||||
|
|
||||||
for _, m := range newNode.waitMsg {
|
|
||||||
c.inChan <- func() {
|
|
||||||
newNode.do(m)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case <-time.After(common.CMD_TIMEOUT):
|
|
||||||
newNode.Close("超时")
|
|
||||||
}
|
}
|
||||||
}()
|
case <-time.After(common.CMD_TIMEOUT):
|
||||||
|
newNode.Close("超时")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
if msg.CmdOpteion == common.CMD_GET_CURRENT_NODE_RESULT {
|
||||||
if msg.CmdOpteion == common.CMD_GET_CURRENT_NODE_RESULT {
|
n := v.(*node)
|
||||||
|
var res chan interface{}
|
||||||
var res chan interface{}
|
if _v, ok := n.loadQuery(msg.CmdId); !ok {
|
||||||
if _v, ok := v.loadQuery(msg.CmdId); !ok {
|
return
|
||||||
return
|
|
||||||
} else {
|
|
||||||
res = _v
|
|
||||||
}
|
|
||||||
|
|
||||||
var nmsg nodeInfo
|
|
||||||
err = json.Unmarshal(msg.CmdData, &nmsg)
|
|
||||||
if err != nil {
|
|
||||||
res <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
v.hostName = cert.RSADecrypterStr(nmsg.HostName)
|
|
||||||
v.uuid = cert.RSADecrypterStr(nmsg.UUID)
|
|
||||||
if v.port, err = strconv.Atoi(cert.RSADecrypterStr(nmsg.Port)); err != nil {
|
|
||||||
v.port = -1
|
|
||||||
}
|
|
||||||
v.mainIp = cert.RSADecrypterStr(nmsg.MainIp)
|
|
||||||
v.goos = cert.RSADecrypterStr(nmsg.Goos)
|
|
||||||
res <- nil
|
|
||||||
} else {
|
} else {
|
||||||
v.waitMsg = append(v.waitMsg, msg)
|
res = _v
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Unlock()
|
var nmsg nodeInfo
|
||||||
|
err = json.Unmarshal(msg.CmdData, &nmsg)
|
||||||
|
if err != nil {
|
||||||
|
res <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n.hostName = cert.RSADecrypterStr(nmsg.HostName)
|
||||||
|
n.uuid = cert.RSADecrypterStr(nmsg.UUID)
|
||||||
|
if n.port, err = strconv.Atoi(cert.RSADecrypterStr(nmsg.Port)); err != nil {
|
||||||
|
n.port = -1
|
||||||
|
}
|
||||||
|
n.mainIp = cert.RSADecrypterStr(nmsg.MainIp)
|
||||||
|
n.goos = cert.RSADecrypterStr(nmsg.Goos)
|
||||||
|
res <- nil
|
||||||
|
} else {
|
||||||
|
v.(*node).waitMsg = append(v.(*node).waitMsg, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -605,7 +599,6 @@ func (c *Conn) handlerNodeRead() {
|
|||||||
func (c *Conn) handle() {
|
func (c *Conn) handle() {
|
||||||
c.OutChan = make(chan []byte, 64)
|
c.OutChan = make(chan []byte, 64)
|
||||||
c.inChan = make(chan func())
|
c.inChan = make(chan func())
|
||||||
|
|
||||||
c.close = make(chan string, 999)
|
c.close = make(chan string, 999)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@@ -631,34 +624,27 @@ func (c *Conn) handle() {
|
|||||||
c.node.ping(0)
|
c.node.ping(0)
|
||||||
c.node.nextPingTime = time.Now().Unix() + 5
|
c.node.nextPingTime = time.Now().Unix() + 5
|
||||||
}
|
}
|
||||||
func() { //返回false则退出handle
|
connMap.Delete(c.remoteAddr)
|
||||||
connMap.Delete(c.remoteAddr)
|
|
||||||
l := clientLock.Lock()
|
|
||||||
defer func() {
|
|
||||||
l.Unlock()
|
|
||||||
}()
|
|
||||||
|
|
||||||
if atomic.CompareAndSwapInt32(&c.closeTag, 0, 1) {
|
if atomic.CompareAndSwapInt32(&c.closeTag, 0, 1) {
|
||||||
|
|
||||||
if c.nodeConn != nil {
|
|
||||||
c.nodeConn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.node != nil {
|
|
||||||
c.node.Close(reason)
|
|
||||||
//移除上游连接
|
|
||||||
for i := len(upLevelNode) - 1; i >= 0; i-- {
|
|
||||||
n := upLevelNode[i]
|
|
||||||
if n.uuid == c.node.uuid {
|
|
||||||
upLevelNode = append(upLevelNode[:i], upLevelNode[i+1:]...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if c.nodeConn != nil {
|
||||||
|
c.nodeConn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
if c.node != nil {
|
||||||
}()
|
c.node.Close(reason)
|
||||||
|
//移除上游连接
|
||||||
|
for i := len(upLevelNode) - 1; i >= 0; i-- {
|
||||||
|
n := upLevelNode[i]
|
||||||
|
if n.uuid == c.node.uuid {
|
||||||
|
upLevelNode = append(upLevelNode[:i], upLevelNode[i+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -682,6 +668,7 @@ func (c *Conn) reg() error {
|
|||||||
CmdOpteion: common.CMD_REG,
|
CmdOpteion: common.CMD_REG,
|
||||||
CmdData: regb,
|
CmdData: regb,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = c.tlsWrite(msg.Marshal()); err != nil {
|
if err = c.tlsWrite(msg.Marshal()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -689,12 +676,8 @@ func (c *Conn) reg() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (c *Conn) WriteToUUID(msg *common.Msg) {
|
func (c *Conn) WriteToUUID(msg *common.Msg) {
|
||||||
|
if n, ok := nodeMap.Load(msg.To); ok {
|
||||||
l := clientLock.RLock()
|
n.(*node).WriteMsg(msg)
|
||||||
defer l.RUnlock()
|
|
||||||
|
|
||||||
if n, ok := nodeMap[msg.To]; ok {
|
|
||||||
n.WriteMsg(msg)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+181
-170
@@ -7,13 +7,16 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/creack/pty"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"rakshasa_lite/common"
|
"rakshasa_lite/common"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"runtime/debug"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -21,11 +24,29 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
var (
|
||||||
|
shellMapLock sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
type StartCmdParam struct {
|
||||||
|
Param string
|
||||||
|
Size *pty.Winsize
|
||||||
|
}
|
||||||
|
type remoteCmd struct {
|
||||||
|
cmdStatus int32
|
||||||
|
cmd *exec.Cmd
|
||||||
|
id uint32
|
||||||
|
stdin io.WriteCloser
|
||||||
|
inChan chan []byte
|
||||||
|
translate func(in []byte) ([]byte, error)
|
||||||
|
ping, pong int64
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
currentNode = &node{}
|
currentNode = &node{}
|
||||||
clientLock = &lock{}
|
clientLock1 = &lock{}
|
||||||
nodeMap = make(map[string]*node)
|
nodeMap sync.Map
|
||||||
upLevelNode []*node //上游节点
|
upLevelNode []*node //上游节点
|
||||||
upNodeWrite = make(chan []byte, 999)
|
upNodeWrite = make(chan []byte, 999)
|
||||||
extNodeIp []string
|
extNodeIp []string
|
||||||
@@ -64,14 +85,14 @@ func InitCurrentNode() {
|
|||||||
addr: currentNode.addr,
|
addr: currentNode.addr,
|
||||||
}
|
}
|
||||||
currentNode.mirrorNode.mirrorNode = currentNode
|
currentNode.mirrorNode.mirrorNode = currentNode
|
||||||
nodeMap[currentNode.uuid] = currentNode
|
nodeMap.Store(currentNode.uuid, currentNode)
|
||||||
//fmt.Println("当前节点UUID", currentNode.uuid)
|
//fmt.Println("当前节点UUID", currentNode.uuid)
|
||||||
go func() {
|
go func() {
|
||||||
for b := range upNodeWrite {
|
for b := range upNodeWrite {
|
||||||
for {
|
for {
|
||||||
ok := func() bool {
|
ok := func() bool {
|
||||||
|
|
||||||
l := clientLock.Lock()
|
l := clientLock1.Lock()
|
||||||
defer l.Unlock()
|
defer l.Unlock()
|
||||||
|
|
||||||
if len(upLevelNode) == 0 {
|
if len(upLevelNode) == 0 {
|
||||||
@@ -91,6 +112,40 @@ func InitCurrentNode() {
|
|||||||
nodeTickPing()
|
nodeTickPing()
|
||||||
time.AfterFunc(time.Second*10, checkUpLevelNode)
|
time.AfterFunc(time.Second*10, checkUpLevelNode)
|
||||||
}
|
}
|
||||||
|
func getNode(arg string) (n *node, err error) {
|
||||||
|
id, err := strconv.Atoi(arg)
|
||||||
|
if err == nil {
|
||||||
|
nodeMap.Range(func(key, value interface{}) bool {
|
||||||
|
_n := value.(*node)
|
||||||
|
if _n.id == id {
|
||||||
|
n = _n
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
nodeMap.Range(func(key, value interface{}) bool {
|
||||||
|
node := value.(*node)
|
||||||
|
if fmt.Sprintf("%s:%d", node.mainIp, node.port) == arg {
|
||||||
|
n = node
|
||||||
|
return false
|
||||||
|
} else if fmt.Sprintf("%s:%d", node.addr, node.port) == arg {
|
||||||
|
n = node
|
||||||
|
return false
|
||||||
|
} else if node.uuid == arg {
|
||||||
|
n = node
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if n != nil {
|
||||||
|
return n, nil
|
||||||
|
} else {
|
||||||
|
return connectNew(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
func checkUpLevelNode() {
|
func checkUpLevelNode() {
|
||||||
|
|
||||||
if len(currentConfig.DstNode) > 0 && len(upLevelNode) == 0 {
|
if len(currentConfig.DstNode) > 0 && len(upLevelNode) == 0 {
|
||||||
@@ -108,29 +163,18 @@ func checkUpLevelNode() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func() {
|
nodeMap.Range(func(key, value interface{}) bool {
|
||||||
|
n := value.(*node)
|
||||||
l := clientLock.RLock()
|
if n.uuid != currentNode.uuid {
|
||||||
defer l.RUnlock()
|
if len(n.mainIp) == 0 {
|
||||||
|
getNode(fmt.Sprintf("%s:%d", n.addr, n.port))
|
||||||
for _, n := range nodeMap {
|
}
|
||||||
if n.uuid != currentNode.uuid {
|
if len(upLevelNode) > 0 {
|
||||||
func() {
|
return false
|
||||||
|
|
||||||
l.RUnlock()
|
|
||||||
defer clientLock.RLock(l)
|
|
||||||
|
|
||||||
if len(n.mainIp) == 0 {
|
|
||||||
getNode(fmt.Sprintf("%s:%d", n.addr, n.port))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
if len(upLevelNode) > 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
return true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,12 +182,9 @@ func checkUpLevelNode() {
|
|||||||
time.AfterFunc(time.Second*5, checkUpLevelNode)
|
time.AfterFunc(time.Second*5, checkUpLevelNode)
|
||||||
}
|
}
|
||||||
func nodeTickPing() {
|
func nodeTickPing() {
|
||||||
|
|
||||||
l := clientLock.RLock()
|
|
||||||
defer l.RUnlock()
|
|
||||||
|
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
for _, n := range nodeMap {
|
nodeMap.Range(func(key, value interface{}) bool {
|
||||||
|
n := value.(*node)
|
||||||
if n.uuid != currentNode.uuid {
|
if n.uuid != currentNode.uuid {
|
||||||
if n.mainIp != "" {
|
if n.mainIp != "" {
|
||||||
addr1 := fmt.Sprintf("%s:%d", n.mainIp, n.port)
|
addr1 := fmt.Sprintf("%s:%d", n.mainIp, n.port)
|
||||||
@@ -167,8 +208,8 @@ func nodeTickPing() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
})
|
||||||
time.AfterFunc(time.Second*1, nodeTickPing)
|
time.AfterFunc(time.Second*1, nodeTickPing)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,29 +319,28 @@ func connectNew(addr string) (n *node, e error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
n.mainIp = cert.RSADecrypterStr(regmsg.MainIp)
|
n.mainIp = cert.RSADecrypterStr(regmsg.MainIp)
|
||||||
if n.port, err = strconv.Atoi(cert.RSADecrypterStr(regmsg.Port)); err != nil {
|
if n.port, err = strconv.Atoi(cert.RSADecrypterStr(regmsg.Port)); n.port==0 {
|
||||||
n.port = -1
|
n.port = -1
|
||||||
}
|
}
|
||||||
if v, ok := nodeMap[regmsg.UUID]; ok {
|
|
||||||
if v.conn.node != nil && v.conn.node.uuid == regmsg.UUID && v.conn.closeTag == 0 {
|
if v, ok := nodeMap.Load(regmsg.UUID); ok {
|
||||||
|
if v.(*node).conn.node != nil && v.(*node).conn.node.uuid == regmsg.UUID && v.(*node).conn.closeTag == 0 {
|
||||||
n.uuid = "" //清空uuid避免正常的node被删
|
n.uuid = "" //清空uuid避免正常的node被删
|
||||||
n.conn.Close("重复注册") //当前的连接关掉
|
n.conn.Close("重复注册") //当前的连接关掉
|
||||||
n.conn = v.conn
|
v.(*node).mainIp = cert.RSADecrypterStr(regmsg.MainIp)
|
||||||
v.mainIp = cert.RSADecrypterStr(regmsg.MainIp)
|
if v.(*node).port, err = strconv.Atoi(cert.RSADecrypterStr(regmsg.Port)); v.(*node).port==0 {
|
||||||
if v.port, err = strconv.Atoi(cert.RSADecrypterStr(regmsg.Port)); err != nil {
|
v.(*node).port = -1
|
||||||
v.port = -1
|
|
||||||
}
|
}
|
||||||
n = v
|
n = v.(*node)
|
||||||
} else {
|
} else {
|
||||||
n.conn.node = n
|
n.conn.node = n
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
n.conn.node = n
|
n.conn.node = n
|
||||||
}
|
}
|
||||||
|
nodeMap.Store(n.uuid, n)
|
||||||
nodeMap[n.uuid] = n
|
|
||||||
n.reConnectAddrs = []string{addr}
|
n.reConnectAddrs = []string{addr}
|
||||||
|
n.Write(common.CMD_GET_NODE, 0, nil)
|
||||||
return n, nil
|
return n, nil
|
||||||
case <-time.After(time.Second * 10):
|
case <-time.After(time.Second * 10):
|
||||||
return nil, errors.New("time out")
|
return nil, errors.New("time out")
|
||||||
@@ -340,7 +380,6 @@ func (n *node) WriteMsg(msg *common.Msg) {
|
|||||||
func (n *node) do(msg *common.Msg) {
|
func (n *node) do(msg *common.Msg) {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
//fmt.Println(common.CmdToName[msg.CmdOpteion])
|
|
||||||
switch msg.CmdOpteion {
|
switch msg.CmdOpteion {
|
||||||
case common.CMD_CONNECT_BYIDADDR:
|
case common.CMD_CONNECT_BYIDADDR:
|
||||||
msg.CmdData = cert.RSADecrypterByPubByte(msg.CmdData)
|
msg.CmdData = cert.RSADecrypterByPubByte(msg.CmdData)
|
||||||
@@ -436,57 +475,50 @@ func (n *node) do(msg *common.Msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case common.CMD_REG:
|
case common.CMD_REG:
|
||||||
func() {
|
var regmsg RegMsg
|
||||||
l := clientLock.Lock()
|
err = json.Unmarshal(msg.CmdData, ®msg)
|
||||||
defer l.Unlock()
|
if err != nil {
|
||||||
|
regmsg.Err = err.Error()
|
||||||
var regmsg RegMsg
|
b, _ := json.Marshal(regmsg)
|
||||||
err = json.Unmarshal(msg.CmdData, ®msg)
|
|
||||||
if err != nil {
|
|
||||||
regmsg.Err = err.Error()
|
|
||||||
b, _ := json.Marshal(regmsg)
|
|
||||||
n.Write(common.CMD_REG_RESULT, 0, b)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
uuid := regmsg.UUID
|
|
||||||
if uuid == currentNode.uuid {
|
|
||||||
regmsg.Err = "不能连接自己"
|
|
||||||
b, _ := json.Marshal(regmsg)
|
|
||||||
n.Write(common.CMD_REG_RESULT, 0, b)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
n.hostName = cert.RSADecrypterStr(regmsg.Hostname)
|
|
||||||
n.mainIp = cert.RSADecrypterStr(regmsg.MainIp)
|
|
||||||
if n.port, err = strconv.Atoi(cert.RSADecrypterStr(regmsg.Port)); err != nil {
|
|
||||||
n.port = -1
|
|
||||||
}
|
|
||||||
n.goos = cert.RSADecrypterStr(regmsg.Goos)
|
|
||||||
n.addr = n.conn.nodeConn.RemoteAddr().String()
|
|
||||||
if i := strings.Index(n.addr, ":"); i > -1 {
|
|
||||||
n.addr = n.addr[:i]
|
|
||||||
}
|
|
||||||
resultMsg := regmsg
|
|
||||||
resultMsg.UUID = currentNode.uuid
|
|
||||||
resultMsg.Hostname = cert.RSAEncrypterStr(currentNode.hostName)
|
|
||||||
resultMsg.MainIp = cert.RSAEncrypterStr(currentNode.mainIp)
|
|
||||||
resultMsg.Port = cert.RSAEncrypterStr(strconv.Itoa(currentNode.port))
|
|
||||||
resultMsg.Goos = cert.RSAEncrypterStr(currentNode.goos)
|
|
||||||
|
|
||||||
b, _ := json.Marshal(resultMsg)
|
|
||||||
//返回成功结果
|
|
||||||
n.Write(common.CMD_REG_RESULT, 0, b)
|
n.Write(common.CMD_REG_RESULT, 0, b)
|
||||||
//储存节点
|
return
|
||||||
n.uuid = uuid
|
}
|
||||||
if v, ok := nodeMap[uuid]; !ok || v.conn.closeTag > 0 {
|
uuid := regmsg.UUID
|
||||||
n.conn.node = n
|
if uuid == currentNode.uuid {
|
||||||
nodeMap[regmsg.UUID] = n
|
regmsg.Err = "请求的UUID相同,无法连接自己,请将节点设置为不同的UUID"
|
||||||
|
b, _ := json.Marshal(regmsg)
|
||||||
|
n.Write(common.CMD_REG_RESULT, 0, b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
}
|
n.hostName = cert.RSADecrypterStr(regmsg.Hostname)
|
||||||
currentNode.broadcastNode()
|
n.mainIp = cert.RSADecrypterStr(regmsg.MainIp)
|
||||||
//把本机所有节点同步到注册机器
|
if n.port, err = strconv.Atoi(cert.RSADecrypterStr(regmsg.Port)); err != nil {
|
||||||
go n.writeGetNodeResult(msg.CmdId)
|
n.port = -1
|
||||||
}()
|
}
|
||||||
|
n.goos = cert.RSADecrypterStr(regmsg.Goos)
|
||||||
|
n.addr = n.conn.nodeConn.RemoteAddr().String()
|
||||||
|
if i := strings.Index(n.addr, ":"); i > -1 {
|
||||||
|
n.addr = n.addr[:i]
|
||||||
|
}
|
||||||
|
resultMsg := regmsg
|
||||||
|
resultMsg.UUID = currentNode.uuid
|
||||||
|
resultMsg.Hostname = cert.RSAEncrypterStr(currentNode.hostName)
|
||||||
|
resultMsg.MainIp = cert.RSAEncrypterStr(currentNode.mainIp)
|
||||||
|
resultMsg.Port = cert.RSAEncrypterStr(strconv.Itoa(currentNode.port))
|
||||||
|
resultMsg.Goos = cert.RSAEncrypterStr(currentNode.goos)
|
||||||
|
|
||||||
|
b, _ := json.Marshal(resultMsg)
|
||||||
|
//返回成功结果
|
||||||
|
n.Write(common.CMD_REG_RESULT, 0, b)
|
||||||
|
//储存节点
|
||||||
|
n.uuid = uuid
|
||||||
|
if v, ok := nodeMap.Load(uuid); !ok || v.(*node).conn.closeTag > 0 {
|
||||||
|
n.conn.node = n
|
||||||
|
nodeMap.Store(regmsg.UUID, n)
|
||||||
|
}
|
||||||
|
currentNode.broadcastNode()
|
||||||
|
|
||||||
case common.CMD_REG_RESULT:
|
case common.CMD_REG_RESULT:
|
||||||
var regmsg RegMsg
|
var regmsg RegMsg
|
||||||
err = json.Unmarshal(msg.CmdData, ®msg)
|
err = json.Unmarshal(msg.CmdData, ®msg)
|
||||||
@@ -501,7 +533,7 @@ func (n *node) do(msg *common.Msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//交换节点
|
//交换节点
|
||||||
go n.writeGetNodeResult(msg.CmdId)
|
n.writeGetNodeResult(msg.CmdId)
|
||||||
case common.CMD_REMOTE_REG:
|
case common.CMD_REMOTE_REG:
|
||||||
|
|
||||||
var regmsg RegMsg
|
var regmsg RegMsg
|
||||||
@@ -549,10 +581,9 @@ func (n *node) do(msg *common.Msg) {
|
|||||||
v <- errors.New(regmsg.Err)
|
v <- errors.New(regmsg.Err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
l := clientLock.Lock()
|
|
||||||
if n.uuid != regmsg.UUID {
|
if n.uuid != regmsg.UUID {
|
||||||
var targetNode *node
|
var targetNode *node
|
||||||
if targetNode, ok = nodeMap[regmsg.UUID]; !ok {
|
if _v, ok := nodeMap.Load(regmsg.UUID); !ok {
|
||||||
targetNode = getNewNode(nodeInfo{
|
targetNode = getNewNode(nodeInfo{
|
||||||
UUID: regmsg.UUID,
|
UUID: regmsg.UUID,
|
||||||
HostName: cert.RSADecrypterStr(regmsg.Hostname),
|
HostName: cert.RSADecrypterStr(regmsg.Hostname),
|
||||||
@@ -561,8 +592,9 @@ func (n *node) do(msg *common.Msg) {
|
|||||||
Goos: cert.RSADecrypterStr(regmsg.Goos),
|
Goos: cert.RSADecrypterStr(regmsg.Goos),
|
||||||
}, n)
|
}, n)
|
||||||
|
|
||||||
nodeMap[regmsg.UUID] = targetNode
|
nodeMap.Store(regmsg.UUID, targetNode)
|
||||||
} else {
|
} else {
|
||||||
|
targetNode = _v.(*node)
|
||||||
targetNode.updateNode(nodeInfo{
|
targetNode.updateNode(nodeInfo{
|
||||||
UUID: regmsg.UUID,
|
UUID: regmsg.UUID,
|
||||||
HostName: cert.RSADecrypterStr(regmsg.Hostname),
|
HostName: cert.RSADecrypterStr(regmsg.Hostname),
|
||||||
@@ -575,8 +607,6 @@ func (n *node) do(msg *common.Msg) {
|
|||||||
} else {
|
} else {
|
||||||
v <- n
|
v <- n
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Unlock()
|
|
||||||
n.writeGetNodeResult(msg.CmdId)
|
n.writeGetNodeResult(msg.CmdId)
|
||||||
|
|
||||||
case common.CMD_PING:
|
case common.CMD_PING:
|
||||||
@@ -769,9 +799,6 @@ func (n *node) do(msg *common.Msg) {
|
|||||||
case common.CMD_GET_NODE:
|
case common.CMD_GET_NODE:
|
||||||
n.writeGetNodeResult(msg.CmdId)
|
n.writeGetNodeResult(msg.CmdId)
|
||||||
case common.CMD_GET_NODE_RESULT:
|
case common.CMD_GET_NODE_RESULT:
|
||||||
l := clientLock.Lock()
|
|
||||||
defer l.Unlock()
|
|
||||||
|
|
||||||
var s []nodeInfo
|
var s []nodeInfo
|
||||||
err = json.Unmarshal(msg.CmdData, &s)
|
err = json.Unmarshal(msg.CmdData, &s)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -784,12 +811,12 @@ func (n *node) do(msg *common.Msg) {
|
|||||||
Goos: cert.RSADecrypterStr(_n.Goos),
|
Goos: cert.RSADecrypterStr(_n.Goos),
|
||||||
}
|
}
|
||||||
if _n.UUID != currentNode.uuid {
|
if _n.UUID != currentNode.uuid {
|
||||||
if v, ok := nodeMap[_n.UUID]; !ok {
|
if v, ok := nodeMap.Load(_n.UUID); !ok {
|
||||||
nodeMap[_n.UUID] = getNewNode(_n, n)
|
nodeMap.Store(_n.UUID, getNewNode(_n, n))
|
||||||
} else {
|
} else {
|
||||||
v.hostName = _n.HostName
|
v.(*node).hostName = _n.HostName
|
||||||
v.mainIp = _n.MainIp
|
v.(*node).mainIp = _n.MainIp
|
||||||
v.port, _ = strconv.Atoi(_n.Port)
|
v.(*node).port, _ = strconv.Atoi(_n.Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -817,31 +844,27 @@ func (n *node) do(msg *common.Msg) {
|
|||||||
case common.CMD_ADD_NODE:
|
case common.CMD_ADD_NODE:
|
||||||
var nmsg nodeInfo
|
var nmsg nodeInfo
|
||||||
err = json.Unmarshal(msg.CmdData, &nmsg)
|
err = json.Unmarshal(msg.CmdData, &nmsg)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if v, ok := nodeMap.Load(nmsg.UUID); !ok {
|
||||||
l := clientLock.Lock()
|
|
||||||
defer l.Unlock()
|
|
||||||
if v, ok := nodeMap[nmsg.UUID]; !ok {
|
|
||||||
newNode := getNewNode(nmsg, n)
|
newNode := getNewNode(nmsg, n)
|
||||||
nodeMap[nmsg.UUID] = newNode
|
|
||||||
|
nodeMap.Store(nmsg.UUID, newNode)
|
||||||
} else if nmsg.UUID != currentNode.uuid {
|
} else if nmsg.UUID != currentNode.uuid {
|
||||||
|
n := v.(*node)
|
||||||
port, err := strconv.Atoi(cert.RSADecrypterStr(nmsg.Port))
|
port, err := strconv.Atoi(cert.RSADecrypterStr(nmsg.Port))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
v.port = port
|
n.port = port
|
||||||
} else {
|
} else {
|
||||||
v.port = -1
|
n.port = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
v.mainIp = cert.RSADecrypterStr(nmsg.MainIp)
|
n.mainIp = cert.RSADecrypterStr(nmsg.MainIp)
|
||||||
v.hostName = cert.RSADecrypterStr(nmsg.HostName)
|
n.hostName = cert.RSADecrypterStr(nmsg.HostName)
|
||||||
v.goos = cert.RSADecrypterStr(nmsg.Goos)
|
n.goos = cert.RSADecrypterStr(nmsg.Goos)
|
||||||
v.uuid = nmsg.UUID
|
n.uuid = nmsg.UUID
|
||||||
|
nodeMap.Store(nmsg.UUID, n)
|
||||||
nodeMap[nmsg.UUID] = v
|
|
||||||
|
|
||||||
}
|
}
|
||||||
case common.CMD_DIR:
|
case common.CMD_DIR:
|
||||||
|
|
||||||
@@ -1206,34 +1229,21 @@ func getNewNode(m nodeInfo, n *node) *node {
|
|||||||
func allNodesDo(f func(*node) (bool, error)) (err error) {
|
func allNodesDo(f func(*node) (bool, error)) (err error) {
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
l := clientLock.RLock()
|
nodeMap.Range(func(key, value interface{}) bool {
|
||||||
defer l.RUnlock()
|
n := value.(*node)
|
||||||
|
|
||||||
for _, n := range nodeMap {
|
|
||||||
if n.uuid != currentNode.uuid {
|
if n.uuid != currentNode.uuid {
|
||||||
func() {
|
ok, err = f(n)
|
||||||
|
if err != nil || !ok {
|
||||||
l.RUnlock()
|
return false
|
||||||
defer clientLock.RLock(l)
|
|
||||||
ok, err = f(n)
|
|
||||||
}()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return true
|
||||||
return nil
|
})
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
func (n *node) ping(id uint32) {
|
func (n *node) ping(id uint32) {
|
||||||
|
l := clientLock1.Lock()
|
||||||
l := clientLock.Lock()
|
defer l.Unlock()
|
||||||
|
|
||||||
defer func() {
|
|
||||||
l.Unlock()
|
|
||||||
}()
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
if n.pingTime > n.pongTime {
|
if n.pingTime > n.pongTime {
|
||||||
|
|
||||||
@@ -1293,12 +1303,7 @@ func (n *node) ping(id uint32) {
|
|||||||
func (n *node) Delete(reason string) {
|
func (n *node) Delete(reason string) {
|
||||||
go func() {
|
go func() {
|
||||||
if atomic.CompareAndSwapInt32(&n.isClose, 0, 1) {
|
if atomic.CompareAndSwapInt32(&n.isClose, 0, 1) {
|
||||||
l := clientLock.Lock()
|
|
||||||
_, ok := nodeMap[n.uuid]
|
|
||||||
if ok {
|
|
||||||
delete(nodeMap, n.uuid)
|
|
||||||
}
|
|
||||||
l.Unlock()
|
|
||||||
n.connMap.Range(func(key, value interface{}) bool {
|
n.connMap.Range(func(key, value interface{}) bool {
|
||||||
if v, ok := value.(common.Conn); ok {
|
if v, ok := value.(common.Conn); ok {
|
||||||
v.Close(reason)
|
v.Close(reason)
|
||||||
@@ -1329,6 +1334,7 @@ func (n *node) Delete(reason string) {
|
|||||||
n.shellMap.Delete(key)
|
n.shellMap.Delete(key)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
nodeMap.Delete(n.uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
}()
|
}()
|
||||||
@@ -1424,27 +1430,32 @@ func (n *node) storeConn(v common.Conn) (newID uint32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) writeGetNodeResult(id uint32) {
|
func (n *node) writeGetNodeResult(id uint32) {
|
||||||
l := clientLock.RLock()
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
debug.PrintStack()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
var s []*nodeInfo
|
||||||
|
nodeMap.Range(func(key, value interface{}) bool {
|
||||||
|
_n := value.(*node)
|
||||||
|
if _n.uuid != currentNode.uuid {
|
||||||
|
s = append(s, &nodeInfo{
|
||||||
|
UUID: _n.uuid,
|
||||||
|
HostName: cert.RSAEncrypterStr(_n.hostName),
|
||||||
|
MainIp: cert.RSAEncrypterStr(_n.mainIp),
|
||||||
|
Port: cert.RSAEncrypterStr(strconv.Itoa(_n.port)),
|
||||||
|
Goos: cert.RSAEncrypterStr(_n.goos),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
defer l.RUnlock()
|
b, _ := json.Marshal(s)
|
||||||
|
n.Write(common.CMD_GET_NODE_RESULT, id, b)
|
||||||
|
}()
|
||||||
|
|
||||||
var s []*nodeInfo
|
|
||||||
|
|
||||||
for _, _n := range nodeMap {
|
|
||||||
if _n.uuid != currentNode.uuid {
|
|
||||||
s = append(s, &nodeInfo{
|
|
||||||
UUID: _n.uuid,
|
|
||||||
HostName: cert.RSAEncrypterStr(_n.hostName),
|
|
||||||
MainIp: cert.RSAEncrypterStr(_n.mainIp),
|
|
||||||
Port: cert.RSAEncrypterStr(strconv.Itoa(_n.port)),
|
|
||||||
Goos: cert.RSAEncrypterStr(_n.goos),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
b, _ := json.Marshal(s)
|
|
||||||
n.Write(common.CMD_GET_NODE_RESULT, id, b)
|
|
||||||
}
|
}
|
||||||
func (n *node) updateNode(msg nodeInfo) {
|
func (n *node) updateNode(msg nodeInfo) {
|
||||||
n.hostName = msg.HostName
|
n.hostName = msg.HostName
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
/*
|
|
||||||
*高级shell功能
|
|
||||||
*node节点管理、remoteShell远程shell,config配置管理
|
|
||||||
*/
|
|
||||||
import (
|
|
||||||
"github.com/creack/pty"
|
|
||||||
"io"
|
|
||||||
"os/exec"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
shellMapLock sync.Mutex
|
|
||||||
)
|
|
||||||
|
|
||||||
type StartCmdParam struct {
|
|
||||||
Param string
|
|
||||||
Size *pty.Winsize
|
|
||||||
}
|
|
||||||
type remoteCmd struct {
|
|
||||||
cmdStatus int32
|
|
||||||
cmd *exec.Cmd
|
|
||||||
id uint32
|
|
||||||
stdin io.WriteCloser
|
|
||||||
inChan chan []byte
|
|
||||||
translate func(in []byte) ([]byte, error)
|
|
||||||
ping, pong int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func getRealPath(path string) string {
|
|
||||||
|
|
||||||
path_s := strings.Split(path, "/")
|
|
||||||
realpath := []string{}
|
|
||||||
if len(path_s) == 0 {
|
|
||||||
return "error"
|
|
||||||
}
|
|
||||||
for _, value := range path_s {
|
|
||||||
|
|
||||||
if value == ".." {
|
|
||||||
k := len(realpath)
|
|
||||||
kk := k - 1
|
|
||||||
realpath = append(realpath[:kk], realpath[k:]...)
|
|
||||||
} else {
|
|
||||||
realpath = append(realpath, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(realpath, "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
func getNode(arg string) (*node, error) {
|
|
||||||
l := clientLock.RLock()
|
|
||||||
|
|
||||||
id, err := strconv.Atoi(arg)
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
for _, n := range nodeMap {
|
|
||||||
if n.id == id && n.uuid != currentNode.uuid {
|
|
||||||
l.RUnlock()
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if v, ok := nodeMap[arg]; ok && v.uuid != currentNode.uuid {
|
|
||||||
l.RUnlock()
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
l.RUnlock()
|
|
||||||
|
|
||||||
return connectNew(arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
|
||||||
|
## 作者: Mob2003
|
||||||
|
|
||||||
|
rakshasa开源地址
|
||||||
|
```url
|
||||||
|
https://github.com/Mob2003/rakshasa
|
||||||
|
```
|
||||||
|
|
||||||
|
## 简介
|
||||||
|
|
||||||
|
rakshasa是一个用Go编写的程序,旨在创建一个能够实现**多级代理**,**内网穿透**网络请求。它可以在节点群中任意两个节点之间转发tcp请求和响应,同时支持**socks5代理**,**http代理**,并可**引入外部http、socks5代理池,自动切换请求ip**。
|
||||||
|
|
||||||
|
节点之间使用内置证书的TLS加密TCP通信,再叠加一层自定义秘钥的AES加密。该程序可在所有Go支持的平台上使用,包括Windows和Linux服务器。
|
||||||
|
|
||||||
|
## 名词
|
||||||
|
|
||||||
|
- **主控端** 具有全部功能,能控制所有节点,包括其他主控节点。
|
||||||
|
- **被控端** 只能连接到其他节点,并接收主控端的指令。
|
||||||
|
|
||||||
|
## 缘由
|
||||||
|
|
||||||
|
最初设计 Rakshasa 时,并没有区分主控端和被控端。这在执行 VPN 和代理操作时非常方便。
|
||||||
|
|
||||||
|
然而,在渗透测试场景下,节点往往部署在不安全的设备和网络中。这就可能导致节点所在的设备被攻破,攻击者通过该节点对您的节点群进行反渗透。因此,Rakshasa 需要能够控制节点权限。
|
||||||
|
|
||||||
|
## 权限思考
|
||||||
|
|
||||||
|
一开始,我们考虑了多种解决方案,包括登录验证、连接验证等。但发现这些方法对现有代码的修改过于庞大且耗时。经过简短的思考和面向百度谷歌编程后,决定采用 RSA 非对称加密来处理这个问题。
|
||||||
|
|
||||||
|
RSA 加密的特点是分为**公钥**和**私钥**。在安全系统中,不安全程序使用**公钥**,而安全程序使用**私钥**。
|
||||||
|
|
||||||
|
因此,**被控端**和**主控端**的最大区别在于,主控端拥有私钥,而被控端没有私钥。
|
||||||
|
|
||||||
|
在rakshasa 中,私钥被定义在了 _cert/tls.go_ 文件中,但没有赋值。在编译**主控端**之前,将私钥写入 private.go 文件。这样编译出的程序会自带私钥。在编译**被控端**时,删除 private.go 文件,生成的程序就不包含私钥了。具体的编译代码可以参考 build.go 文件。
|
||||||
|
|
||||||
|
## 权限实现
|
||||||
|
|
||||||
|
有了公钥和私钥后,就需要实现权限区分。
|
||||||
|
|
||||||
|
### 被控端
|
||||||
|
|
||||||
|
被控端没有私钥。它需要连接其他节点进行转发,但不能解密。因此,有两个关键信息不能加密:IP 地址和 UUID。如果 IP 地址被加密,被控端如何进行下一级连接?消息转发和消息控制基于 UUID。如果将 UUID 加密,被控节点也会变成盲目。因此,被控端需要对除 IP 地址和 UUID 之外的其他信息进行加密,并且被控端无法对这些信息进行解密,确保被控端不会反过来控制其他节点。
|
||||||
|
|
||||||
|
|
||||||
|
### 主控端
|
||||||
|
|
||||||
|
主控端拥有私钥。Go 语言自带的库无法实现私钥加密和公钥解密功能,因此采用了第三方库来实现私钥加密。代码中对所有关键主控消息进行了加密,包括但不限于启动 Socks5 正反代理,HTTP 代理,TCP 正反向代理等功能。
|
||||||
|
|
||||||
|
有些同学发现,被控节点启动 Socks5 代理和 HTTP 代理并没有报错。这是因为 Socks5 代理和 HTTP 代理启动时并不需要对方同意,而是在本地直接开启。在使用时才会请求对方节点。这时您会发现,尽管 HTTP 代理已经启动,但无法访问网络。
|
||||||
|
|
||||||
|
## 版本区别
|
||||||
|
|
||||||
|
最后附上rakshasa各个版本的区别
|
||||||
|
|
||||||
|
| | fullnode | node|fullnode_lite|node_lite|
|
||||||
|
| ---- | ---- |---- |---- |---- |
|
||||||
|
|连接其他节点 | √ |√ |√ |√ |
|
||||||
|
|启动本地socks5代理 | √ |√ |√ |√ |
|
||||||
|
|启动本地http代理 | √ |√ |√ |√ |
|
||||||
|
|启动多层代理 | √ |× |√ |× |
|
||||||
|
|远程shell| √ |× |√ |× |
|
||||||
|
|其他远程功能| √ |× |√ |× |
|
||||||
|
| 交互式CLI | √ |√ |× |× |
|
||||||
|
| check_proxy | √ |√ |× |× |
|
||||||
|
|
||||||
+781
-4
@@ -1,11 +1,27 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"cert"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"rakshasa/aes"
|
||||||
"rakshasa/common"
|
"rakshasa/common"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/abiosoft/readline"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/luyu6056/ishell"
|
"github.com/luyu6056/ishell"
|
||||||
|
"golang.org/x/text/encoding/simplifiedchinese"
|
||||||
|
"golang.org/x/text/transform"
|
||||||
)
|
)
|
||||||
|
|
||||||
var rootCli = cliInit()
|
var rootCli = cliInit()
|
||||||
@@ -99,9 +115,9 @@ func cliInit() *ishell.Shell {
|
|||||||
c.Println("参数不对")
|
c.Println("参数不对")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
n, ok := nodeMap[c.Args[0]]
|
n, ok := nodeMap.Load(c.Args[0])
|
||||||
if ok {
|
if ok {
|
||||||
n.Delete("")
|
n.(*node).Delete("")
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
@@ -116,9 +132,9 @@ func cliInit() *ishell.Shell {
|
|||||||
c.Println("参数不对")
|
c.Println("参数不对")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
n, ok := nodeMap[c.Args[0]]
|
n, ok := nodeMap.Load(c.Args[0])
|
||||||
if ok {
|
if ok {
|
||||||
n.Close("debug关闭")
|
n.(*node).Close("debug关闭")
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
@@ -126,3 +142,764 @@ func cliInit() *ishell.Shell {
|
|||||||
}
|
}
|
||||||
return shell
|
return shell
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
configShell := cliInit()
|
||||||
|
configShell.SetPrompt("rakshasa\\config>")
|
||||||
|
configShell.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "info",
|
||||||
|
Help: "打印当前配置",
|
||||||
|
Func: func(c *ishell.Context) {
|
||||||
|
c.Println("当前节点", currentNode.uuid)
|
||||||
|
c.Println("上级节点地址", currentConfig.DstNode)
|
||||||
|
c.Println("通讯密码", currentConfig.Password)
|
||||||
|
c.Println("监听端口", currentConfig.Port)
|
||||||
|
c.Println("监听IP", currentConfig.ListenIp)
|
||||||
|
c.Println("禁止额外连接", currentConfig.Limit)
|
||||||
|
c.Println("配置文件名", currentConfig.FileName)
|
||||||
|
if currentConfig.FileSave {
|
||||||
|
c.Println("当前配置:已写入文件")
|
||||||
|
} else {
|
||||||
|
c.Println("当前配置:未写入文件")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
configShell.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "save",
|
||||||
|
Help: "保存文件",
|
||||||
|
Func: func(c *ishell.Context) {
|
||||||
|
if err := ConfigSave(); err == nil {
|
||||||
|
c.Println("写入成功")
|
||||||
|
} else {
|
||||||
|
c.Println("保存失败", err.Error())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
configShell.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "d",
|
||||||
|
Help: "修改上级节点地址,格式为 ip:端口 多个节点以,隔开 注意:不会立刻连接设置节点, 当发生 节点掉线重连 时候会连接该地址",
|
||||||
|
Func: func(c *ishell.Context) {
|
||||||
|
if len(c.Args) != 1 {
|
||||||
|
c.Println("参数错误,格式为 ip:端口 多个节点以,隔开 如 d 192.168.1.1:8883,192.168.1.2:8883")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dstNode, err := common.ResolveTCPAddr(c.Args[0])
|
||||||
|
if err != nil {
|
||||||
|
c.Println("参数错误,格式为 ip:端口 多个节点以,隔开 如 d 192.168.1.1:8883,192.168.1.2:8883")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentConfig.DstNode = dstNode
|
||||||
|
currentConfig.FileSave = false
|
||||||
|
},
|
||||||
|
})
|
||||||
|
configShell.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "password",
|
||||||
|
Help: "修改通讯密码,立即生效",
|
||||||
|
Func: func(c *ishell.Context) {
|
||||||
|
if len(c.Args) != 1 {
|
||||||
|
c.Println("参数错误,格式为 password \"123456\"")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Println(c.Args)
|
||||||
|
currentConfig.Password = c.Args[0]
|
||||||
|
currentConfig.FileSave = false
|
||||||
|
aes.Key = aes.MD5_B(currentConfig.Password + string(cert.RsaPrivateKey[:16]))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
configShell.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "port",
|
||||||
|
Help: "修改监听端口,立即生效",
|
||||||
|
Func: func(c *ishell.Context) {
|
||||||
|
if len(c.Args) != 1 {
|
||||||
|
c.Println("参数错误,格式为 port 8883")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
port, _ := strconv.Atoi(c.Args[0])
|
||||||
|
if port <= 0 || port > 65535 {
|
||||||
|
c.Println("参数错误,端口范围是1-65535")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Println("正在关闭server监听")
|
||||||
|
if currentNode.listen != nil {
|
||||||
|
currentNode.listen.Close()
|
||||||
|
currentNode.listen = nil
|
||||||
|
}
|
||||||
|
currentConfig.Port = port
|
||||||
|
currentNode.port = port
|
||||||
|
currentConfig.FileSave = false
|
||||||
|
if err := StartServer(fmt.Sprintf(":%d", currentConfig.Port)); err != nil {
|
||||||
|
c.Printf("启动节点失败 %v, 请重新修改监听端口", currentConfig.Port)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
configShell.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "ip",
|
||||||
|
Help: "修改本节点连接ip,当其他节点进行额外连接时候,优先使用此ip连接",
|
||||||
|
Func: func(c *ishell.Context) {
|
||||||
|
if len(c.Args) != 1 {
|
||||||
|
c.Println("参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currentConfig.ListenIp = c.Args[0]
|
||||||
|
currentNode.mainIp = currentConfig.ListenIp
|
||||||
|
currentConfig.FileSave = false
|
||||||
|
|
||||||
|
},
|
||||||
|
})
|
||||||
|
configShell.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "limit",
|
||||||
|
Help: "修改本节点Limit设置,使用方法 limit true",
|
||||||
|
Func: func(c *ishell.Context) {
|
||||||
|
if len(c.Args) != 1 {
|
||||||
|
c.Println("参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentConfig.Limit = c.Args[0] == "true"
|
||||||
|
currentConfig.FileSave = false
|
||||||
|
},
|
||||||
|
})
|
||||||
|
configShell.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "f",
|
||||||
|
Help: "修改配置文件名,使用方法 f config.yaml",
|
||||||
|
Func: func(c *ishell.Context) {
|
||||||
|
if len(c.Args) != 1 {
|
||||||
|
c.Println("参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentConfig.FileName = c.Args[0]
|
||||||
|
currentConfig.FileSave = false
|
||||||
|
},
|
||||||
|
})
|
||||||
|
configShell.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "uuid",
|
||||||
|
Help: "修改本节点UUID设置,使用方法uuid 字串符",
|
||||||
|
Func: func(c *ishell.Context) {
|
||||||
|
if len(c.Args) != 1 {
|
||||||
|
c.Println("参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if id, err := uuid.Parse(c.Args[0]); err == nil {
|
||||||
|
nodeMap.Delete(currentConfig.UUID)
|
||||||
|
currentConfig.UUID = id.String()
|
||||||
|
nodeMap.Store(currentConfig.UUID, currentNode)
|
||||||
|
currentConfig.FileSave = false
|
||||||
|
SetConfig(currentConfig)
|
||||||
|
} else {
|
||||||
|
c.Println("输入的uuid不是合法的uuid,建议使用xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
})
|
||||||
|
rootCli.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "config",
|
||||||
|
Help: "配置管理",
|
||||||
|
Func: func(c *ishell.Context) {
|
||||||
|
configShell.Run()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
remoteShell := cliInit()
|
||||||
|
|
||||||
|
remoteShell.SetPrompt("rakshasa\\remoteshell>")
|
||||||
|
|
||||||
|
fileShell := cliInit()
|
||||||
|
remoteShell.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "file",
|
||||||
|
Help: "连到节点进行文件管理,参数为id或者uuid",
|
||||||
|
Func: func(c *ishell.Context) {
|
||||||
|
if len(c.Args) != 1 {
|
||||||
|
c.Println("参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
workN, _ := getNode(c.Args[0])
|
||||||
|
if workN == nil {
|
||||||
|
c.Println("无法连接节点", c.Args[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if workN != nil {
|
||||||
|
fileShell.Set("node", workN)
|
||||||
|
result := make(chan interface{}, 1)
|
||||||
|
id := workN.storeQuery(result)
|
||||||
|
workN.Write(common.CMD_PWD, id, []byte(cert.RSAEncrypterByPriv(currentConfig.Password)))
|
||||||
|
select {
|
||||||
|
case pwd := <-result:
|
||||||
|
workN.deleteQuery(id)
|
||||||
|
pwd = strings.ReplaceAll(pwd.(string), "\\", "/")
|
||||||
|
fileShell.Set("pwd", pwd)
|
||||||
|
fileShell.SetPrompt(workN.uuid + " " + pwd.(string) + ">")
|
||||||
|
fileShell.Run()
|
||||||
|
case <-time.After(common.CMD_TIMEOUT):
|
||||||
|
workN.deleteQuery(id)
|
||||||
|
c.Println("连接", c.Args[0], "超时")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
})
|
||||||
|
fileShell.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "dir",
|
||||||
|
Help: "打印当前目录文件",
|
||||||
|
Func: func(c *ishell.Context) {
|
||||||
|
pwd := fileShell.Get("pwd")
|
||||||
|
|
||||||
|
n := c.Get("node").(*node)
|
||||||
|
resChan := make(chan interface{}, 1)
|
||||||
|
id := n.storeQuery(resChan)
|
||||||
|
n.Write(common.CMD_DIR, id, []byte(cert.RSAEncrypterByPriv(pwd.(string))))
|
||||||
|
select {
|
||||||
|
case res := <-resChan:
|
||||||
|
n.deleteQuery(id)
|
||||||
|
c.Println(res)
|
||||||
|
case <-time.After(common.CMD_TIMEOUT):
|
||||||
|
n.deleteQuery(id)
|
||||||
|
c.Println("dir time out")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
fileShell.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "cd",
|
||||||
|
Help: "切换工作目录",
|
||||||
|
Func: func(c *ishell.Context) {
|
||||||
|
if len(c.Args) != 1 {
|
||||||
|
c.Println("参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dir := c.Args[0]
|
||||||
|
pwd := fileShell.Get("pwd").(string)
|
||||||
|
n := c.Get("node").(*node)
|
||||||
|
|
||||||
|
if strings.Contains(dir, ":/") || dir[0] == '/' || dir == "~" {
|
||||||
|
pwd = dir
|
||||||
|
} else {
|
||||||
|
pwd += "/" + dir
|
||||||
|
pwd = strings.TrimRight(getRealPath(pwd), "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
resChan := make(chan interface{}, 1)
|
||||||
|
id := n.storeQuery(resChan)
|
||||||
|
n.Write(common.CMD_CD, id, []byte(cert.RSAEncrypterByPriv(pwd)))
|
||||||
|
|
||||||
|
select {
|
||||||
|
case res := <-resChan:
|
||||||
|
n.deleteQuery(id)
|
||||||
|
if err, ok := res.(error); ok {
|
||||||
|
c.Println(err.Error())
|
||||||
|
} else {
|
||||||
|
pwd = res.(string)
|
||||||
|
fileShell.Set("pwd", pwd)
|
||||||
|
c.SetPrompt(n.uuid + " " + pwd + ">")
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-time.After(common.CMD_TIMEOUT):
|
||||||
|
n.deleteQuery(id)
|
||||||
|
c.Println("dir time out")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
fileShell.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "upload",
|
||||||
|
Help: "上传文件 ,upload 本地文件 远程目录(为空传到工作目录)",
|
||||||
|
Func: func(c *ishell.Context) {
|
||||||
|
if len(c.Args) != 1 && len(c.Args) != 2 {
|
||||||
|
c.Println("参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s, err := os.Stat(c.Args[0])
|
||||||
|
if err != nil {
|
||||||
|
c.Println("打开本地文件", c.Args[0], "错误 ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f, err := os.Open(c.Args[0])
|
||||||
|
if err != nil {
|
||||||
|
c.Println("打开本地文件", c.Args[0], "错误 ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
pwd := fileShell.Get("pwd").(string) + "/"
|
||||||
|
n := c.Get("node").(*node)
|
||||||
|
|
||||||
|
if len(c.Args) == 2 {
|
||||||
|
pwd = c.Args[1]
|
||||||
|
}
|
||||||
|
pwd = strings.ReplaceAll(pwd, "\\", "/")
|
||||||
|
c.Args[0] = strings.ReplaceAll(c.Args[0], "\\", "/")
|
||||||
|
i := strings.LastIndex(c.Args[0], "/")
|
||||||
|
if i == -1 {
|
||||||
|
i = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if pwd[len(pwd)-1] == '/' {
|
||||||
|
pwd += c.Args[0][i:]
|
||||||
|
}
|
||||||
|
i = strings.LastIndex(pwd, "/")
|
||||||
|
if i == -1 {
|
||||||
|
i = 0
|
||||||
|
}
|
||||||
|
filename := pwd[i+1:]
|
||||||
|
dir := pwd[:i]
|
||||||
|
dir = strings.TrimRight(getRealPath(dir), "/") + "/"
|
||||||
|
pwd = dir + filename
|
||||||
|
resChan := make(chan interface{}, 9999) //避免收消息阻塞
|
||||||
|
|
||||||
|
filereadChan := make(chan []byte, 10)
|
||||||
|
|
||||||
|
upload := func() {
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
buf := make([]byte, common.MAX_PACKAGE-len(pwd)-9)
|
||||||
|
n, err := f.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resChan <- err
|
||||||
|
c.Println("读取文件", c.Args[0], "错误", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
filereadChan <- buf[:n]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset := 0
|
||||||
|
be := len(pwd) + 1
|
||||||
|
id := n.storeQuery(resChan)
|
||||||
|
defer n.deleteQuery(id)
|
||||||
|
b := []byte(pwd)
|
||||||
|
b = append(b, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
|
c.ProgressBar().Start()
|
||||||
|
go upload()
|
||||||
|
var resnum int
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case data := <-filereadChan:
|
||||||
|
|
||||||
|
b[be] = byte(offset)
|
||||||
|
b[be+1] = byte(offset >> 8)
|
||||||
|
b[be+2] = byte(offset >> 16)
|
||||||
|
b[be+3] = byte(offset >> 24)
|
||||||
|
b[be+4] = byte(offset >> 32)
|
||||||
|
b[be+5] = byte(offset >> 40)
|
||||||
|
b[be+6] = byte(offset >> 48)
|
||||||
|
b[be+7] = byte(offset >> 56)
|
||||||
|
offset += len(data)
|
||||||
|
n.Write(common.CMD_UPLOAD, id, cert.RSAEncrypterByPrivByte(append(b, data...)))
|
||||||
|
case res := <-resChan:
|
||||||
|
switch v := res.(type) {
|
||||||
|
case error:
|
||||||
|
c.ProgressBar().Stop()
|
||||||
|
c.Println("上传失败", res)
|
||||||
|
return
|
||||||
|
case int64:
|
||||||
|
resnum++
|
||||||
|
i := v * 100 / s.Size()
|
||||||
|
c.ProgressBar().Suffix(fmt.Sprint(" ", i, "%"))
|
||||||
|
c.ProgressBar().Progress(int(i))
|
||||||
|
if v == s.Size() {
|
||||||
|
c.ProgressBar().Stop()
|
||||||
|
c.Println(c.Args[0], "上传成功")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resnum >= 5 {
|
||||||
|
go upload()
|
||||||
|
resnum -= 10
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
c.Println("协议错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-time.After(common.CMD_TIMEOUT):
|
||||||
|
c.ProgressBar().Stop()
|
||||||
|
c.Println("upload time out")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
})
|
||||||
|
fileShell.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "download",
|
||||||
|
Help: "下载文件 ,download 远程文件 本地目录(为空本地执行目录)",
|
||||||
|
Func: func(c *ishell.Context) {
|
||||||
|
if len(c.Args) != 1 && len(c.Args) != 2 {
|
||||||
|
c.Println("参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pwd := fileShell.Get("pwd").(string)
|
||||||
|
n := c.Get("node").(*node)
|
||||||
|
file := c.Args[0]
|
||||||
|
file = strings.ReplaceAll(file, "\\", "/")
|
||||||
|
|
||||||
|
if strings.Contains(file, ":/") || file[0] == '/' {
|
||||||
|
pwd = file
|
||||||
|
} else {
|
||||||
|
pwd += "/" + file
|
||||||
|
|
||||||
|
}
|
||||||
|
i := strings.LastIndex(pwd, "/")
|
||||||
|
if i == -1 {
|
||||||
|
i = 0
|
||||||
|
}
|
||||||
|
filename := pwd[i+1:]
|
||||||
|
dir := pwd[:i]
|
||||||
|
dir = strings.TrimRight(getRealPath(dir), "/") + "/"
|
||||||
|
mydir, err := os.Getwd()
|
||||||
|
local := "./" + filename
|
||||||
|
if err == nil {
|
||||||
|
local = mydir + "/" + filename
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.Args) == 2 {
|
||||||
|
s, err := os.Stat(c.Args[1])
|
||||||
|
if err == nil {
|
||||||
|
if s.IsDir() {
|
||||||
|
local = strings.TrimRight(c.Args[1], "/") + "/" + filename
|
||||||
|
} else {
|
||||||
|
local = c.Args[1]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
local = c.Args[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pwd = dir + filename
|
||||||
|
|
||||||
|
result := make(chan interface{}, 999)
|
||||||
|
id := n.storeQuery(result)
|
||||||
|
defer n.deleteQuery(id)
|
||||||
|
b := []byte(pwd)
|
||||||
|
b = append(b, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0}...)
|
||||||
|
total := int64(-1)
|
||||||
|
be := len(pwd) + 1
|
||||||
|
b[be] = byte(total)
|
||||||
|
b[be+1] = byte(total >> 8)
|
||||||
|
b[be+2] = byte(total >> 16)
|
||||||
|
b[be+3] = byte(total >> 24)
|
||||||
|
b[be+4] = byte(total >> 32)
|
||||||
|
b[be+5] = byte(total >> 40)
|
||||||
|
b[be+6] = byte(total >> 48)
|
||||||
|
b[be+7] = byte(total >> 56)
|
||||||
|
n.Write(common.CMD_DOWNLOAD, id, cert.RSAEncrypterByPrivByte(b))
|
||||||
|
c.ProgressBar().Start()
|
||||||
|
size := int64(0)
|
||||||
|
resnum := 0
|
||||||
|
total = 0
|
||||||
|
var f *os.File
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case res := <-result:
|
||||||
|
switch v := res.(type) {
|
||||||
|
case error:
|
||||||
|
c.ProgressBar().Stop()
|
||||||
|
c.Println("下载失败", res)
|
||||||
|
return
|
||||||
|
case int64:
|
||||||
|
var err error
|
||||||
|
size = v
|
||||||
|
f, err = os.OpenFile(local, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
|
||||||
|
if err != nil {
|
||||||
|
c.Println("本地文件 ", local, "写入失败", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
case []byte:
|
||||||
|
if f == nil {
|
||||||
|
c.Println("本地文件 ", local, "不可写入")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resnum++
|
||||||
|
num, err := f.Write(v)
|
||||||
|
if err != nil {
|
||||||
|
c.Println("本地文件 ", local, "写入失败", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if num != len(v) {
|
||||||
|
c.Println("本地文件 ", local, "写入失败,写入量不符")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
total += int64(num)
|
||||||
|
i := total * 100 / size
|
||||||
|
c.ProgressBar().Suffix(fmt.Sprint(" ", i, "%"))
|
||||||
|
c.ProgressBar().Progress(int(i))
|
||||||
|
if total == size {
|
||||||
|
c.ProgressBar().Stop()
|
||||||
|
c.Println(c.Args[0], "下载成功 文件保存到", local)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resnum == 10 {
|
||||||
|
resnum -= 10
|
||||||
|
b[be] = byte(total)
|
||||||
|
b[be+1] = byte(total >> 8)
|
||||||
|
b[be+2] = byte(total >> 16)
|
||||||
|
b[be+3] = byte(total >> 24)
|
||||||
|
b[be+4] = byte(total >> 32)
|
||||||
|
b[be+5] = byte(total >> 40)
|
||||||
|
b[be+6] = byte(total >> 48)
|
||||||
|
b[be+7] = byte(total >> 56)
|
||||||
|
n.Write(common.CMD_DOWNLOAD, id, cert.RSAEncrypterByPrivByte(b))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
c.Println("协议错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-time.After(common.CMD_TIMEOUT):
|
||||||
|
c.ProgressBar().Stop()
|
||||||
|
c.Println("upload time out")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
remoteShell.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "new",
|
||||||
|
Help: "与一个或者多个节点连接,使用方法 new ip:端口 多个地址以,间隔 如1080 127.0.0.1:1081,127.0.0.1:1082",
|
||||||
|
Func: func(c *ishell.Context) {
|
||||||
|
if len(c.Args) != 1 {
|
||||||
|
c.Println("参数错误,使用方法 connect ip:端口")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, addr := range strings.Split(c.Args[0], ",") {
|
||||||
|
_, err := getNode(addr)
|
||||||
|
if err != nil {
|
||||||
|
c.Println("连接", addr, "失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
remoteShell.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "shell",
|
||||||
|
Help: "反弹shell 使用方法 shell id/uuid 启动参数 ,启动参数可为空,win默认启动cmd,linux默认启动bash, 如 shell 1 powershell 。 shell 1 zsh",
|
||||||
|
Func: func(c *ishell.Context) {
|
||||||
|
if len(c.Args) < 1 {
|
||||||
|
c.Println("参数错误,例子 shell 1 powershell")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param := ""
|
||||||
|
if len(c.Args) == 2 {
|
||||||
|
param = c.Args[1]
|
||||||
|
}
|
||||||
|
n, _ := getNode(c.Args[0])
|
||||||
|
if n == nil {
|
||||||
|
c.Println("无法连接节点", c.Args[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res := make(chan interface{}, 999)
|
||||||
|
id := n.storeQuery(res)
|
||||||
|
|
||||||
|
defer n.deleteQuery(id)
|
||||||
|
p := StartCmdParam{
|
||||||
|
Param: param,
|
||||||
|
Size: common.GetSize(),
|
||||||
|
}
|
||||||
|
|
||||||
|
b, _ := json.Marshal(p)
|
||||||
|
n.Write(common.CMD_SHELL, id, cert.RSAEncrypterByPrivByte(b))
|
||||||
|
s := &remoteCmd{
|
||||||
|
cmd: nil,
|
||||||
|
stdin: nil,
|
||||||
|
inChan: make(chan []byte, 999),
|
||||||
|
translate: func(in []byte) ([]byte, error) { return in, nil },
|
||||||
|
pong: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case i := <-res:
|
||||||
|
switch v := i.(type) {
|
||||||
|
case error:
|
||||||
|
c.Println("启动shell失败,错误", v.Error())
|
||||||
|
case []byte:
|
||||||
|
data := v
|
||||||
|
|
||||||
|
s.id = uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16 | uint32(data[3])<<24
|
||||||
|
switch data[4] {
|
||||||
|
case 0: //windows
|
||||||
|
if string(data[len(data)-6:]) == string([]byte{32, 57, 51, 54, 13, 10}) { //活动代码页: 936
|
||||||
|
//gbk转utf8
|
||||||
|
s.translate = func(in []byte) ([]byte, error) {
|
||||||
|
reader := transform.NewReader(bytes.NewReader(in), simplifiedchinese.GBK.NewDecoder())
|
||||||
|
d, e := ioutil.ReadAll(reader)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 1: //linux
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
if !common.EnableTermVt {
|
||||||
|
s.translate = func(in []byte) ([]byte, error) {
|
||||||
|
if in[0] == 27 {
|
||||||
|
r, _ := regexp.Compile(`\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])`)
|
||||||
|
res := r.ReplaceAllString(string(in), "")
|
||||||
|
return []byte(res), nil
|
||||||
|
}
|
||||||
|
return in, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
atomic.CompareAndSwapInt32(&s.cmdStatus, 0, 1)
|
||||||
|
}
|
||||||
|
case <-time.After(common.CMD_TIMEOUT):
|
||||||
|
c.Println("启动shell失败,超时")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
n.shellMap.Store(s.id, s)
|
||||||
|
r, _ := readline.NewEx(&readline.Config{FuncIsTerminal: func() bool { return false }, ForcePrint: true})
|
||||||
|
defer func() {
|
||||||
|
n.shellMap.Delete(s.id)
|
||||||
|
atomic.StoreInt32(&s.cmdStatus, -1)
|
||||||
|
c.Println("请按回车键退出")
|
||||||
|
r.Close()
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
|
||||||
|
for {
|
||||||
|
|
||||||
|
switch s.cmdStatus {
|
||||||
|
case 1:
|
||||||
|
|
||||||
|
input, err := r.ReadlineEx()
|
||||||
|
if err != nil {
|
||||||
|
if err != readline.ErrInterrupt {
|
||||||
|
res <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if s.cmdStatus == 1 {
|
||||||
|
|
||||||
|
n.Write(common.CMD_SHELL_DATA, s.id, []byte{03})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.cmdStatus == 1 {
|
||||||
|
|
||||||
|
n.Write(common.CMD_SHELL_DATA, s.id, []byte(input+"\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
case -1:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
tick := time.NewTicker(common.CMD_TIMEOUT / 2)
|
||||||
|
for {
|
||||||
|
|
||||||
|
select {
|
||||||
|
case b := <-s.inChan:
|
||||||
|
s.pong = time.Now().Unix()
|
||||||
|
if len(b) > 0 {
|
||||||
|
b, err := s.translate(b)
|
||||||
|
if err != nil {
|
||||||
|
c.Println("shell 运行失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print(string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
case v := <-res:
|
||||||
|
if err, ok := v.(error); ok {
|
||||||
|
if err.Error() != "退出shell" {
|
||||||
|
c.Println("运行shell", param, "失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
c.Println("无法处理消息", v)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case <-tick.C:
|
||||||
|
s.ping = time.Now().Unix()
|
||||||
|
if s.ping-s.pong > int64(common.CMD_TIMEOUT/time.Second) {
|
||||||
|
c.Println("shell time out")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n.Write(common.CMD_SHELL_DATA, s.id, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
rootCli.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "remoteshell",
|
||||||
|
Help: "远程shell",
|
||||||
|
Func: func(c *ishell.Context) {
|
||||||
|
remoteShell.Run()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打印节点
|
||||||
|
func printNodes(c *ishell.Context) {
|
||||||
|
l := clientLock.RLock()
|
||||||
|
defer l.RUnlock()
|
||||||
|
var list []*node
|
||||||
|
|
||||||
|
nodeMap.Range(func(key, value interface{}) bool {
|
||||||
|
n := value.(*node)
|
||||||
|
list = append(list, n)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
orderNode(list)
|
||||||
|
c.Println("ID UUID HostName GOOS IP listenIP")
|
||||||
|
c.Println("-----------------------------------------------------------------------------------------------------------------------------")
|
||||||
|
for k, n := range list {
|
||||||
|
n.id = k + 1
|
||||||
|
hostname := bytes.Repeat([]byte(" "), 22)
|
||||||
|
copy(hostname, n.hostName)
|
||||||
|
ip := bytes.Repeat([]byte(" "), 23)
|
||||||
|
if n.uuid == currentNode.uuid {
|
||||||
|
|
||||||
|
copy(ip, "(localhost)"+":"+strconv.Itoa(n.port))
|
||||||
|
} else {
|
||||||
|
copy(ip, n.addr+":"+strconv.Itoa(n.port))
|
||||||
|
}
|
||||||
|
|
||||||
|
listenip := n.mainIp
|
||||||
|
goos := bytes.Repeat([]byte(" "), 11)
|
||||||
|
copy(goos, n.goos)
|
||||||
|
c.Printf("%2d %s %s %s %s %s\n", n.id, n.uuid, hostname, goos, ip, listenip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRealPath(path string) string {
|
||||||
|
|
||||||
|
path_s := strings.Split(path, "/")
|
||||||
|
realpath := []string{}
|
||||||
|
if len(path_s) == 0 {
|
||||||
|
return "error"
|
||||||
|
}
|
||||||
|
for _, value := range path_s {
|
||||||
|
|
||||||
|
if value == ".." {
|
||||||
|
k := len(realpath)
|
||||||
|
kk := k - 1
|
||||||
|
realpath = append(realpath[:kk], realpath[k:]...)
|
||||||
|
} else {
|
||||||
|
realpath = append(realpath, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(realpath, "/")
|
||||||
|
}
|
||||||
|
func printConn() {
|
||||||
|
connMap.Range(func(key, value interface{}) bool {
|
||||||
|
fmt.Println(key)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
+90
-110
@@ -12,6 +12,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"rakshasa/aes"
|
"rakshasa/aes"
|
||||||
"rakshasa/common"
|
"rakshasa/common"
|
||||||
|
"runtime/debug"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -34,13 +35,13 @@ type Conn struct {
|
|||||||
node *node
|
node *node
|
||||||
nodeaddr string
|
nodeaddr string
|
||||||
//key string
|
//key string
|
||||||
remoteAddr string
|
remoteAddr string
|
||||||
inChan chan func()
|
inChan chan func()
|
||||||
OutChan chan []byte
|
OutChan chan []byte
|
||||||
close chan string
|
close chan string
|
||||||
isClient bool
|
isClient bool
|
||||||
nodeConn *tls.Conn
|
nodeConn *tls.Conn
|
||||||
regResult chan RegMsg
|
regResult chan RegMsg
|
||||||
}
|
}
|
||||||
|
|
||||||
type serverListen struct {
|
type serverListen struct {
|
||||||
@@ -495,7 +496,6 @@ func (c *Conn) handlerNodeRead() {
|
|||||||
if common.Debug {
|
if common.Debug {
|
||||||
fmt.Println("fromto", msg.From, msg.To, common.CmdToName[msg.CmdOpteion], int(lengbuf[0])+int(lengbuf[1])<<8)
|
fmt.Println("fromto", msg.From, msg.To, common.CmdToName[msg.CmdOpteion], int(lengbuf[0])+int(lengbuf[1])<<8)
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg.To == common.NoneUUID.String() && c.node == nil {
|
if msg.To == common.NoneUUID.String() && c.node == nil {
|
||||||
c.inChan <- func() {
|
c.inChan <- func() {
|
||||||
newNode := &node{
|
newNode := &node{
|
||||||
@@ -504,83 +504,77 @@ func (c *Conn) handlerNodeRead() {
|
|||||||
newNode.do(msg)
|
newNode.do(msg)
|
||||||
}
|
}
|
||||||
} else if msg.To == currentNode.uuid {
|
} else if msg.To == currentNode.uuid {
|
||||||
|
v, ok := nodeMap.Load(msg.From)
|
||||||
func() {
|
if ok && v.(*node).port != 0 {
|
||||||
l := clientLock.RLock()
|
c.inChan <- func() {
|
||||||
v, ok := nodeMap[msg.From]
|
v.(*node).do(msg)
|
||||||
l.RUnlock()
|
}
|
||||||
if ok && v.port != 0 {
|
} else {
|
||||||
c.inChan <- func() {
|
if !ok {
|
||||||
v.do(msg)
|
newNode := &node{
|
||||||
|
uuid: msg.From,
|
||||||
|
conn: c,
|
||||||
|
waitMsg: []*common.Msg{msg},
|
||||||
}
|
}
|
||||||
} else {
|
result := make(chan interface{}, 1)
|
||||||
l := clientLock.Lock()
|
id := newNode.storeQuery(result)
|
||||||
v, ok := nodeMap[msg.From]
|
nodeMap.Store(msg.From, newNode)
|
||||||
if !ok {
|
newNode.Write(common.CMD_GET_CURRENT_NODE, id, []byte{1}) //获取丢失节点的信息
|
||||||
newNode := &node{
|
go func() {
|
||||||
uuid: msg.From,
|
defer func() {
|
||||||
conn: c,
|
if err := recover(); err != nil {
|
||||||
waitMsg: []*common.Msg{msg},
|
fmt.Println(err)
|
||||||
}
|
debug.PrintStack()
|
||||||
result := make(chan interface{}, 1)
|
}
|
||||||
id := newNode.storeQuery(result)
|
newNode.deleteQuery(id)
|
||||||
if common.Debug {
|
}()
|
||||||
fmt.Printf("nodeMap1 %s %p \r\n", msg.From, newNode)
|
select {
|
||||||
}
|
case res := <-result:
|
||||||
nodeMap[msg.From] = newNode
|
if res == nil {
|
||||||
l.Unlock()
|
for _, m := range newNode.waitMsg {
|
||||||
newNode.Write(common.CMD_GET_CURRENT_NODE, id, []byte{1}) //获取丢失节点的信息
|
c.inChan <- func() {
|
||||||
go func() {
|
newNode.do(m)
|
||||||
defer newNode.deleteQuery(id)
|
|
||||||
select {
|
|
||||||
case res := <-result:
|
|
||||||
if res == nil {
|
|
||||||
|
|
||||||
for _, m := range newNode.waitMsg {
|
|
||||||
c.inChan <- func() {
|
|
||||||
newNode.do(m)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case <-time.After(common.CMD_TIMEOUT):
|
|
||||||
newNode.Close("超时")
|
|
||||||
}
|
}
|
||||||
}()
|
case <-time.After(common.CMD_TIMEOUT):
|
||||||
|
newNode.Close("超时")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if msg.CmdOpteion == common.CMD_GET_CURRENT_NODE_RESULT {
|
if msg.CmdOpteion == common.CMD_GET_CURRENT_NODE_RESULT {
|
||||||
|
n := v.(*node)
|
||||||
var res chan interface{}
|
var res chan interface{}
|
||||||
if _v, ok := v.loadQuery(msg.CmdId); !ok {
|
if _v, ok := n.loadQuery(msg.CmdId); !ok {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
res = _v
|
res = _v
|
||||||
}
|
}
|
||||||
|
|
||||||
var nmsg nodeInfo
|
var nmsg nodeInfo
|
||||||
err = json.Unmarshal(msg.CmdData, &nmsg)
|
err = json.Unmarshal(msg.CmdData, &nmsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res <- err
|
res <- err
|
||||||
return
|
return
|
||||||
}
|
|
||||||
v.hostName = cert.RSADecrypterStr(nmsg.HostName)
|
|
||||||
v.uuid = cert.RSADecrypterStr(nmsg.UUID)
|
|
||||||
if v.port, err = strconv.Atoi(cert.RSADecrypterStr(nmsg.Port)); err != nil {
|
|
||||||
v.port = -1
|
|
||||||
}
|
|
||||||
v.mainIp = cert.RSADecrypterStr(nmsg.MainIp)
|
|
||||||
v.goos = cert.RSADecrypterStr(nmsg.Goos)
|
|
||||||
res <- nil
|
|
||||||
} else {
|
|
||||||
v.waitMsg = append(v.waitMsg, msg)
|
|
||||||
}
|
}
|
||||||
|
n.hostName = cert.RSADecrypterStr(nmsg.HostName)
|
||||||
|
n.uuid = cert.RSADecrypterStr(nmsg.UUID)
|
||||||
|
if n.port, err = strconv.Atoi(cert.RSADecrypterStr(nmsg.Port)); err != nil {
|
||||||
|
n.port = -1
|
||||||
|
}
|
||||||
|
n.mainIp = cert.RSADecrypterStr(nmsg.MainIp)
|
||||||
|
n.goos = cert.RSADecrypterStr(nmsg.Goos)
|
||||||
|
res <- nil
|
||||||
|
} else {
|
||||||
|
v.(*node).waitMsg = append(v.(*node).waitMsg, msg)
|
||||||
|
}
|
||||||
|
|
||||||
l.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -622,7 +616,6 @@ func (c *Conn) handlerNodeRead() {
|
|||||||
func (c *Conn) handle() {
|
func (c *Conn) handle() {
|
||||||
c.OutChan = make(chan []byte, 64)
|
c.OutChan = make(chan []byte, 64)
|
||||||
c.inChan = make(chan func())
|
c.inChan = make(chan func())
|
||||||
|
|
||||||
c.close = make(chan string, 999)
|
c.close = make(chan string, 999)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@@ -648,43 +641,34 @@ func (c *Conn) handle() {
|
|||||||
c.node.ping(0)
|
c.node.ping(0)
|
||||||
c.node.nextPingTime = time.Now().Unix() + 5
|
c.node.nextPingTime = time.Now().Unix() + 5
|
||||||
}
|
}
|
||||||
func() { //返回false则退出handle
|
connMap.Delete(c.remoteAddr)
|
||||||
connMap.Delete(c.remoteAddr)
|
|
||||||
l := clientLock.Lock()
|
|
||||||
defer func() {
|
|
||||||
l.Unlock()
|
|
||||||
}()
|
|
||||||
|
|
||||||
if atomic.CompareAndSwapInt32(&c.closeTag, 0, 1) {
|
|
||||||
if common.Debug {
|
|
||||||
fmt.Println(c.nodeConn.RemoteAddr().String(), "关闭原因", reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.nodeConn != nil {
|
|
||||||
if common.Debug {
|
|
||||||
fmt.Println("執行close1")
|
|
||||||
}
|
|
||||||
c.nodeConn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.node != nil {
|
|
||||||
c.node.Close(reason)
|
|
||||||
//移除上游连接
|
|
||||||
for i := len(upLevelNode) - 1; i >= 0; i-- {
|
|
||||||
n := upLevelNode[i]
|
|
||||||
if n.uuid == c.node.uuid {
|
|
||||||
upLevelNode = append(upLevelNode[:i], upLevelNode[i+1:]...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if common.Debug {
|
|
||||||
fmt.Println("upLevelNode",len(upLevelNode))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if atomic.CompareAndSwapInt32(&c.closeTag, 0, 1) {
|
||||||
|
if common.Debug {
|
||||||
|
fmt.Println(c.nodeConn.RemoteAddr().String(), "关闭原因", reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
if c.nodeConn != nil {
|
||||||
}()
|
if common.Debug {
|
||||||
|
fmt.Println("執行close1")
|
||||||
|
}
|
||||||
|
c.nodeConn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.node != nil {
|
||||||
|
c.node.Close(reason)
|
||||||
|
//移除上游连接
|
||||||
|
for i := len(upLevelNode) - 1; i >= 0; i-- {
|
||||||
|
n := upLevelNode[i]
|
||||||
|
if n.uuid == c.node.uuid {
|
||||||
|
upLevelNode = append(upLevelNode[:i], upLevelNode[i+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if common.Debug {
|
||||||
|
fmt.Println("upLevelNode", len(upLevelNode))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -715,12 +699,8 @@ func (c *Conn) reg() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (c *Conn) WriteToUUID(msg *common.Msg) {
|
func (c *Conn) WriteToUUID(msg *common.Msg) {
|
||||||
|
if n, ok := nodeMap.Load(msg.To); ok {
|
||||||
l := clientLock.RLock()
|
n.(*node).WriteMsg(msg)
|
||||||
defer l.RUnlock()
|
|
||||||
|
|
||||||
if n, ok := nodeMap[msg.To]; ok {
|
|
||||||
n.WriteMsg(msg)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -733,10 +713,10 @@ func (c *Conn) tlsWrite(b []byte) error {
|
|||||||
c.nodeConn.SetWriteDeadline(time.Now().Add(common.WRITE_DEADLINE))
|
c.nodeConn.SetWriteDeadline(time.Now().Add(common.WRITE_DEADLINE))
|
||||||
n, err := c.nodeConn.Write(b)
|
n, err := c.nodeConn.Write(b)
|
||||||
if common.Debug {
|
if common.Debug {
|
||||||
if c.node!=nil{
|
if c.node != nil {
|
||||||
fmt.Println("writeto", c.node.uuid, n)
|
fmt.Println("writeto", c.node.uuid, n)
|
||||||
}else{
|
} else {
|
||||||
fmt.Println("writeto",common.NoneUUID, n)
|
fmt.Println("writeto", common.NoneUUID, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+151
-129
@@ -12,20 +12,42 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"rakshasa/common"
|
"rakshasa/common"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"runtime/debug"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/creack/pty"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
shellMapLock sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
type StartCmdParam struct {
|
||||||
|
Param string
|
||||||
|
Size *pty.Winsize
|
||||||
|
}
|
||||||
|
type remoteCmd struct {
|
||||||
|
cmdStatus int32
|
||||||
|
cmd *exec.Cmd
|
||||||
|
id uint32
|
||||||
|
stdin io.WriteCloser
|
||||||
|
inChan chan []byte
|
||||||
|
translate func(in []byte) ([]byte, error)
|
||||||
|
ping, pong int64
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
currentNode = &node{}
|
currentNode = &node{}
|
||||||
clientLock = &lock{}
|
clientLock = &lock{}
|
||||||
nodeMap = make(map[string]*node)
|
nodeMap = sync.Map{}
|
||||||
upLevelNode []*node //上游节点
|
upLevelNode []*node //上游节点
|
||||||
upNodeWrite = make(chan []byte, 999)
|
upNodeWrite = make(chan []byte, 999)
|
||||||
extNodeIp []string
|
extNodeIp []string
|
||||||
@@ -64,7 +86,7 @@ func InitCurrentNode() {
|
|||||||
addr: currentNode.addr,
|
addr: currentNode.addr,
|
||||||
}
|
}
|
||||||
currentNode.mirrorNode.mirrorNode = currentNode
|
currentNode.mirrorNode.mirrorNode = currentNode
|
||||||
nodeMap[currentNode.uuid] = currentNode
|
nodeMap.Store(currentNode.uuid, currentNode)
|
||||||
//fmt.Println("当前节点UUID", currentNode.uuid)
|
//fmt.Println("当前节点UUID", currentNode.uuid)
|
||||||
go func() {
|
go func() {
|
||||||
for b := range upNodeWrite {
|
for b := range upNodeWrite {
|
||||||
@@ -91,6 +113,40 @@ func InitCurrentNode() {
|
|||||||
nodeTickPing()
|
nodeTickPing()
|
||||||
time.AfterFunc(time.Second*10, checkUpLevelNode)
|
time.AfterFunc(time.Second*10, checkUpLevelNode)
|
||||||
}
|
}
|
||||||
|
func getNode(arg string) (n *node, err error) {
|
||||||
|
id, err := strconv.Atoi(arg)
|
||||||
|
if err == nil {
|
||||||
|
nodeMap.Range(func(key, value interface{}) bool {
|
||||||
|
_n := value.(*node)
|
||||||
|
if _n.id == id {
|
||||||
|
n = _n
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
nodeMap.Range(func(key, value interface{}) bool {
|
||||||
|
node := value.(*node)
|
||||||
|
if fmt.Sprintf("%s:%d", node.mainIp, node.port) == arg {
|
||||||
|
n = node
|
||||||
|
return false
|
||||||
|
} else if fmt.Sprintf("%s:%d", node.addr, node.port) == arg {
|
||||||
|
n = node
|
||||||
|
return false
|
||||||
|
} else if node.uuid == arg {
|
||||||
|
n = node
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if n != nil {
|
||||||
|
return n, nil
|
||||||
|
} else {
|
||||||
|
return connectNew(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
func checkUpLevelNode() {
|
func checkUpLevelNode() {
|
||||||
if len(currentConfig.DstNode) > 0 && len(upLevelNode) == 0 {
|
if len(currentConfig.DstNode) > 0 && len(upLevelNode) == 0 {
|
||||||
|
|
||||||
@@ -114,36 +170,20 @@ func checkUpLevelNode() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func() {
|
nodeMap.Range(func(key, value interface{}) bool {
|
||||||
|
n := value.(*node)
|
||||||
l := clientLock.RLock()
|
if n.uuid != currentNode.uuid {
|
||||||
defer l.RUnlock()
|
if len(n.mainIp) == 0 {
|
||||||
|
getNode(fmt.Sprintf("%s:%d", n.addr, n.port))
|
||||||
for _, n := range nodeMap {
|
}
|
||||||
if n.uuid != currentNode.uuid {
|
if len(upLevelNode) > 0 {
|
||||||
|
return false
|
||||||
func() {
|
|
||||||
l.RUnlock()
|
|
||||||
defer clientLock.RLock(l)
|
|
||||||
|
|
||||||
if len(n.mainIp) == 0 {
|
|
||||||
_, err := getNode(fmt.Sprintf("%s:%d", n.addr, n.port))
|
|
||||||
if common.Debug {
|
|
||||||
fmt.Printf("连接n.addr %s 错误 %v \r\n", fmt.Sprintf("%s:%d", n.addr, n.port), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
if len(upLevelNode) > 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
return true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
time.AfterFunc(time.Second*5, checkUpLevelNode)
|
time.AfterFunc(time.Second*5, checkUpLevelNode)
|
||||||
}
|
}
|
||||||
@@ -153,7 +193,8 @@ func nodeTickPing() {
|
|||||||
defer l.RUnlock()
|
defer l.RUnlock()
|
||||||
|
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
for _, n := range nodeMap {
|
nodeMap.Range(func(key, value interface{}) bool {
|
||||||
|
n := value.(*node)
|
||||||
if n.uuid != currentNode.uuid {
|
if n.uuid != currentNode.uuid {
|
||||||
if n.mainIp != "" {
|
if n.mainIp != "" {
|
||||||
addr1 := fmt.Sprintf("%s:%d", n.mainIp, n.port)
|
addr1 := fmt.Sprintf("%s:%d", n.mainIp, n.port)
|
||||||
@@ -177,8 +218,8 @@ func nodeTickPing() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
})
|
||||||
time.AfterFunc(time.Second*1, nodeTickPing)
|
time.AfterFunc(time.Second*1, nodeTickPing)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,19 +329,19 @@ func connectNew(addr string) (n *node, e error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
n.mainIp = cert.RSADecrypterStr(regmsg.MainIp)
|
n.mainIp = cert.RSADecrypterStr(regmsg.MainIp)
|
||||||
if n.port, err = strconv.Atoi(cert.RSADecrypterStr(regmsg.Port)); err != nil {
|
if n.port, err = strconv.Atoi(cert.RSADecrypterStr(regmsg.Port)); n.port==0 {
|
||||||
n.port = -1
|
n.port = -1
|
||||||
}
|
}
|
||||||
if v, ok := nodeMap[regmsg.UUID]; ok {
|
|
||||||
if v.conn.node != nil && v.conn.node.uuid == regmsg.UUID && v.conn.closeTag == 0 {
|
if v, ok := nodeMap.Load(regmsg.UUID); ok {
|
||||||
|
if v.(*node).conn.node != nil && v.(*node).conn.node.uuid == regmsg.UUID && v.(*node).conn.closeTag == 0 {
|
||||||
n.uuid = "" //清空uuid避免正常的node被删
|
n.uuid = "" //清空uuid避免正常的node被删
|
||||||
n.conn.Close("重复注册") //当前的连接关掉
|
n.conn.Close("重复注册") //当前的连接关掉
|
||||||
n.conn = v.conn
|
v.(*node).mainIp = cert.RSADecrypterStr(regmsg.MainIp)
|
||||||
v.mainIp = cert.RSADecrypterStr(regmsg.MainIp)
|
if v.(*node).port, err = strconv.Atoi(cert.RSADecrypterStr(regmsg.Port)); v.(*node).port==0 {
|
||||||
if v.port, err = strconv.Atoi(cert.RSADecrypterStr(regmsg.Port)); err != nil {
|
v.(*node).port = -1
|
||||||
v.port = -1
|
|
||||||
}
|
}
|
||||||
n = v
|
n = v.(*node)
|
||||||
} else {
|
} else {
|
||||||
n.conn.node = n
|
n.conn.node = n
|
||||||
}
|
}
|
||||||
@@ -308,10 +349,9 @@ func connectNew(addr string) (n *node, e error) {
|
|||||||
} else {
|
} else {
|
||||||
n.conn.node = n
|
n.conn.node = n
|
||||||
}
|
}
|
||||||
|
nodeMap.Store(n.uuid, n)
|
||||||
nodeMap[n.uuid] = n
|
|
||||||
n.reConnectAddrs = []string{addr}
|
n.reConnectAddrs = []string{addr}
|
||||||
|
n.Write(common.CMD_GET_NODE, 0, nil)
|
||||||
return n, nil
|
return n, nil
|
||||||
case <-time.After(time.Second * 10):
|
case <-time.After(time.Second * 10):
|
||||||
return nil, errors.New("time out")
|
return nil, errors.New("time out")
|
||||||
@@ -454,9 +494,6 @@ func (n *node) do(msg *common.Msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case common.CMD_REG:
|
case common.CMD_REG:
|
||||||
func() {
|
|
||||||
l := clientLock.Lock()
|
|
||||||
defer l.Unlock()
|
|
||||||
|
|
||||||
var regmsg RegMsg
|
var regmsg RegMsg
|
||||||
err = json.Unmarshal(msg.CmdData, ®msg)
|
err = json.Unmarshal(msg.CmdData, ®msg)
|
||||||
@@ -468,7 +505,7 @@ func (n *node) do(msg *common.Msg) {
|
|||||||
}
|
}
|
||||||
uuid := regmsg.UUID
|
uuid := regmsg.UUID
|
||||||
if uuid == currentNode.uuid {
|
if uuid == currentNode.uuid {
|
||||||
regmsg.Err = "不能连接自己"
|
regmsg.Err = "请求的UUID相同,无法连接自己,请将节点设置为不同的UUID"
|
||||||
b, _ := json.Marshal(regmsg)
|
b, _ := json.Marshal(regmsg)
|
||||||
n.Write(common.CMD_REG_RESULT, 0, b)
|
n.Write(common.CMD_REG_RESULT, 0, b)
|
||||||
return
|
return
|
||||||
@@ -491,23 +528,20 @@ func (n *node) do(msg *common.Msg) {
|
|||||||
resultMsg.Port = cert.RSAEncrypterStr(strconv.Itoa(currentNode.port))
|
resultMsg.Port = cert.RSAEncrypterStr(strconv.Itoa(currentNode.port))
|
||||||
resultMsg.Goos = cert.RSAEncrypterStr(currentNode.goos)
|
resultMsg.Goos = cert.RSAEncrypterStr(currentNode.goos)
|
||||||
|
|
||||||
b, _ := json.Marshal(resultMsg)
|
b, _ := json.Marshal(resultMsg)
|
||||||
//返回成功结果
|
//返回成功结果
|
||||||
n.Write(common.CMD_REG_RESULT, 0, b)
|
n.Write(common.CMD_REG_RESULT, 0, b)
|
||||||
//储存节点
|
//储存节点
|
||||||
n.uuid = uuid
|
n.uuid = uuid
|
||||||
if v, ok := nodeMap[uuid]; !ok || v.conn.closeTag > 0 {
|
if v, ok := nodeMap.Load(uuid); !ok || v.(*node).conn.closeTag > 0 {
|
||||||
n.conn.node = n
|
n.conn.node = n
|
||||||
if common.Debug {
|
if common.Debug {
|
||||||
fmt.Printf("nodeMap2 %s %p \r\n", regmsg.UUID, n)
|
fmt.Printf("nodeMap2 %s %p \r\n", regmsg.UUID, n)
|
||||||
}
|
|
||||||
nodeMap[regmsg.UUID] = n
|
|
||||||
|
|
||||||
}
|
}
|
||||||
currentNode.broadcastNode()
|
nodeMap.Store(regmsg.UUID, n)
|
||||||
//把本机所有节点同步到注册机器
|
}
|
||||||
go n.writeGetNodeResult(msg.CmdId)
|
currentNode.broadcastNode()
|
||||||
}()
|
|
||||||
case common.CMD_REG_RESULT:
|
case common.CMD_REG_RESULT:
|
||||||
var regmsg RegMsg
|
var regmsg RegMsg
|
||||||
err = json.Unmarshal(msg.CmdData, ®msg)
|
err = json.Unmarshal(msg.CmdData, ®msg)
|
||||||
@@ -520,7 +554,7 @@ func (n *node) do(msg *common.Msg) {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
//交换节点
|
//交换节点
|
||||||
go n.writeGetNodeResult(msg.CmdId)
|
n.writeGetNodeResult(msg.CmdId)
|
||||||
case common.CMD_REMOTE_REG:
|
case common.CMD_REMOTE_REG:
|
||||||
|
|
||||||
var regmsg RegMsg
|
var regmsg RegMsg
|
||||||
@@ -569,9 +603,11 @@ func (n *node) do(msg *common.Msg) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
l := clientLock.Lock()
|
l := clientLock.Lock()
|
||||||
|
defer l.Unlock()
|
||||||
|
|
||||||
if n.uuid != regmsg.UUID {
|
if n.uuid != regmsg.UUID {
|
||||||
var targetNode *node
|
var targetNode *node
|
||||||
if targetNode, ok = nodeMap[regmsg.UUID]; !ok {
|
if _v, ok := nodeMap.Load(regmsg.UUID); !ok {
|
||||||
targetNode = getNewNode(nodeInfo{
|
targetNode = getNewNode(nodeInfo{
|
||||||
UUID: regmsg.UUID,
|
UUID: regmsg.UUID,
|
||||||
HostName: cert.RSADecrypterStr(regmsg.Hostname),
|
HostName: cert.RSADecrypterStr(regmsg.Hostname),
|
||||||
@@ -582,8 +618,9 @@ func (n *node) do(msg *common.Msg) {
|
|||||||
if common.Debug {
|
if common.Debug {
|
||||||
fmt.Printf("nodeMap4 %s %p \r\n", regmsg.UUID, n)
|
fmt.Printf("nodeMap4 %s %p \r\n", regmsg.UUID, n)
|
||||||
}
|
}
|
||||||
nodeMap[regmsg.UUID] = targetNode
|
nodeMap.Store(regmsg.UUID, targetNode)
|
||||||
} else {
|
} else {
|
||||||
|
targetNode = _v.(*node)
|
||||||
targetNode.updateNode(nodeInfo{
|
targetNode.updateNode(nodeInfo{
|
||||||
UUID: regmsg.UUID,
|
UUID: regmsg.UUID,
|
||||||
HostName: cert.RSADecrypterStr(regmsg.Hostname),
|
HostName: cert.RSADecrypterStr(regmsg.Hostname),
|
||||||
@@ -596,8 +633,6 @@ func (n *node) do(msg *common.Msg) {
|
|||||||
} else {
|
} else {
|
||||||
v <- n
|
v <- n
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Unlock()
|
|
||||||
n.writeGetNodeResult(msg.CmdId)
|
n.writeGetNodeResult(msg.CmdId)
|
||||||
|
|
||||||
case common.CMD_PING:
|
case common.CMD_PING:
|
||||||
@@ -805,12 +840,12 @@ func (n *node) do(msg *common.Msg) {
|
|||||||
Goos: cert.RSADecrypterStr(_n.Goos),
|
Goos: cert.RSADecrypterStr(_n.Goos),
|
||||||
}
|
}
|
||||||
if _n.UUID != currentNode.uuid {
|
if _n.UUID != currentNode.uuid {
|
||||||
if v, ok := nodeMap[_n.UUID]; !ok {
|
if v, ok := nodeMap.Load(_n.UUID); !ok {
|
||||||
nodeMap[_n.UUID] = getNewNode(_n, n)
|
nodeMap.Store(_n.UUID, getNewNode(_n, n))
|
||||||
} else {
|
} else {
|
||||||
v.hostName = _n.HostName
|
v.(*node).hostName = _n.HostName
|
||||||
v.mainIp = _n.MainIp
|
v.(*node).mainIp = _n.MainIp
|
||||||
v.port, _ = strconv.Atoi(_n.Port)
|
v.(*node).port, _ = strconv.Atoi(_n.Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -842,34 +877,29 @@ func (n *node) do(msg *common.Msg) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if v, ok := nodeMap.Load(nmsg.UUID); !ok {
|
||||||
l := clientLock.Lock()
|
|
||||||
defer l.Unlock()
|
|
||||||
if v, ok := nodeMap[nmsg.UUID]; !ok {
|
|
||||||
newNode := getNewNode(nmsg, n)
|
newNode := getNewNode(nmsg, n)
|
||||||
if common.Debug {
|
if common.Debug {
|
||||||
fmt.Printf("nodeMap5 %s %p \r\n", nmsg.UUID, newNode)
|
fmt.Printf("nodeMap5 %s %p \r\n", nmsg.UUID, newNode)
|
||||||
}
|
}
|
||||||
|
nodeMap.Store(nmsg.UUID, newNode)
|
||||||
nodeMap[nmsg.UUID] = newNode
|
|
||||||
} else if nmsg.UUID != currentNode.uuid {
|
} else if nmsg.UUID != currentNode.uuid {
|
||||||
|
n := v.(*node)
|
||||||
port, err := strconv.Atoi(cert.RSADecrypterStr(nmsg.Port))
|
port, err := strconv.Atoi(cert.RSADecrypterStr(nmsg.Port))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
v.port = port
|
n.port = port
|
||||||
} else {
|
} else {
|
||||||
v.port = -1
|
n.port = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
v.mainIp = cert.RSADecrypterStr(nmsg.MainIp)
|
n.mainIp = cert.RSADecrypterStr(nmsg.MainIp)
|
||||||
v.hostName = cert.RSADecrypterStr(nmsg.HostName)
|
n.hostName = cert.RSADecrypterStr(nmsg.HostName)
|
||||||
v.goos = cert.RSADecrypterStr(nmsg.Goos)
|
n.goos = cert.RSADecrypterStr(nmsg.Goos)
|
||||||
v.uuid = nmsg.UUID
|
n.uuid = nmsg.UUID
|
||||||
if common.Debug {
|
if common.Debug {
|
||||||
fmt.Printf("nodeMap6 %s %p \r\n", nmsg.UUID, v)
|
fmt.Printf("nodeMap6 %s %p \r\n", nmsg.UUID, v)
|
||||||
}
|
}
|
||||||
|
nodeMap.Store(nmsg.UUID, n)
|
||||||
nodeMap[nmsg.UUID] = v
|
|
||||||
|
|
||||||
}
|
}
|
||||||
case common.CMD_DIR:
|
case common.CMD_DIR:
|
||||||
|
|
||||||
@@ -1240,26 +1270,17 @@ func getNewNode(m nodeInfo, n *node) *node {
|
|||||||
func allNodesDo(f func(*node) (bool, error)) (err error) {
|
func allNodesDo(f func(*node) (bool, error)) (err error) {
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
l := clientLock.RLock()
|
nodeMap.Range(func(key, value interface{}) bool {
|
||||||
defer l.RUnlock()
|
n := value.(*node)
|
||||||
|
|
||||||
for _, n := range nodeMap {
|
|
||||||
if n.uuid != currentNode.uuid {
|
if n.uuid != currentNode.uuid {
|
||||||
func() {
|
ok, err = f(n)
|
||||||
|
if err != nil || !ok {
|
||||||
l.RUnlock()
|
return false
|
||||||
defer clientLock.RLock(l)
|
|
||||||
ok, err = f(n)
|
|
||||||
}()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return true
|
||||||
return nil
|
})
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
func (n *node) ping(id uint32) {
|
func (n *node) ping(id uint32) {
|
||||||
if common.NoPing {
|
if common.NoPing {
|
||||||
@@ -1332,12 +1353,7 @@ func (n *node) ping(id uint32) {
|
|||||||
func (n *node) Delete(reason string) {
|
func (n *node) Delete(reason string) {
|
||||||
go func() {
|
go func() {
|
||||||
if atomic.CompareAndSwapInt32(&n.isClose, 0, 1) {
|
if atomic.CompareAndSwapInt32(&n.isClose, 0, 1) {
|
||||||
l := clientLock.Lock()
|
|
||||||
_, ok := nodeMap[n.uuid]
|
|
||||||
if ok {
|
|
||||||
delete(nodeMap, n.uuid)
|
|
||||||
}
|
|
||||||
l.Unlock()
|
|
||||||
n.connMap.Range(func(key, value interface{}) bool {
|
n.connMap.Range(func(key, value interface{}) bool {
|
||||||
if v, ok := value.(common.Conn); ok {
|
if v, ok := value.(common.Conn); ok {
|
||||||
v.Close(reason)
|
v.Close(reason)
|
||||||
@@ -1368,6 +1384,7 @@ func (n *node) Delete(reason string) {
|
|||||||
n.shellMap.Delete(key)
|
n.shellMap.Delete(key)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
nodeMap.Delete(n.uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
}()
|
}()
|
||||||
@@ -1464,27 +1481,32 @@ func (n *node) storeConn(v common.Conn) (newID uint32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) writeGetNodeResult(id uint32) {
|
func (n *node) writeGetNodeResult(id uint32) {
|
||||||
l := clientLock.RLock()
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
debug.PrintStack()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
var s []*nodeInfo
|
||||||
|
nodeMap.Range(func(key, value interface{}) bool {
|
||||||
|
_n := value.(*node)
|
||||||
|
if _n.uuid != currentNode.uuid {
|
||||||
|
s = append(s, &nodeInfo{
|
||||||
|
UUID: _n.uuid,
|
||||||
|
HostName: cert.RSAEncrypterStr(_n.hostName),
|
||||||
|
MainIp: cert.RSAEncrypterStr(_n.mainIp),
|
||||||
|
Port: cert.RSAEncrypterStr(strconv.Itoa(_n.port)),
|
||||||
|
Goos: cert.RSAEncrypterStr(_n.goos),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
defer l.RUnlock()
|
b, _ := json.Marshal(s)
|
||||||
|
n.Write(common.CMD_GET_NODE_RESULT, id, b)
|
||||||
|
}()
|
||||||
|
|
||||||
var s []*nodeInfo
|
|
||||||
|
|
||||||
for _, _n := range nodeMap {
|
|
||||||
if _n.uuid != currentNode.uuid {
|
|
||||||
s = append(s, &nodeInfo{
|
|
||||||
UUID: _n.uuid,
|
|
||||||
HostName: cert.RSAEncrypterStr(_n.hostName),
|
|
||||||
MainIp: cert.RSAEncrypterStr(_n.mainIp),
|
|
||||||
Port: cert.RSAEncrypterStr(strconv.Itoa(_n.port)),
|
|
||||||
Goos: cert.RSAEncrypterStr(_n.goos),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
b, _ := json.Marshal(s)
|
|
||||||
n.Write(common.CMD_GET_NODE_RESULT, id, b)
|
|
||||||
}
|
}
|
||||||
func (n *node) updateNode(msg nodeInfo) {
|
func (n *node) updateNode(msg nodeInfo) {
|
||||||
n.hostName = msg.HostName
|
n.hostName = msg.HostName
|
||||||
|
|||||||
-832
@@ -1,832 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
/*
|
|
||||||
*高级shell功能
|
|
||||||
*node节点管理、remoteShell远程shell,config配置管理
|
|
||||||
*/
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"cert"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"rakshasa/aes"
|
|
||||||
"rakshasa/common"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
|
|
||||||
"regexp"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/abiosoft/readline"
|
|
||||||
"github.com/creack/pty"
|
|
||||||
"github.com/luyu6056/ishell"
|
|
||||||
"golang.org/x/text/encoding/simplifiedchinese"
|
|
||||||
"golang.org/x/text/transform"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
shellMapLock sync.Mutex
|
|
||||||
)
|
|
||||||
|
|
||||||
type StartCmdParam struct {
|
|
||||||
Param string
|
|
||||||
Size *pty.Winsize
|
|
||||||
}
|
|
||||||
type remoteCmd struct {
|
|
||||||
cmdStatus int32
|
|
||||||
cmd *exec.Cmd
|
|
||||||
id uint32
|
|
||||||
stdin io.WriteCloser
|
|
||||||
inChan chan []byte
|
|
||||||
translate func(in []byte) ([]byte, error)
|
|
||||||
ping, pong int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
|
|
||||||
configShell := cliInit()
|
|
||||||
configShell.SetPrompt("rakshasa\\config>")
|
|
||||||
configShell.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "info",
|
|
||||||
Help: "打印当前配置",
|
|
||||||
Func: func(c *ishell.Context) {
|
|
||||||
c.Println("当前节点", currentNode.uuid)
|
|
||||||
c.Println("上级节点地址", currentConfig.DstNode)
|
|
||||||
c.Println("通讯密码", currentConfig.Password)
|
|
||||||
c.Println("监听端口", currentConfig.Port)
|
|
||||||
c.Println("监听IP", currentConfig.ListenIp)
|
|
||||||
c.Println("禁止额外连接", currentConfig.Limit)
|
|
||||||
c.Println("配置文件名", currentConfig.FileName)
|
|
||||||
if currentConfig.FileSave {
|
|
||||||
c.Println("当前配置:已写入文件")
|
|
||||||
} else {
|
|
||||||
c.Println("当前配置:未写入文件")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
configShell.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "save",
|
|
||||||
Help: "保存文件",
|
|
||||||
Func: func(c *ishell.Context) {
|
|
||||||
if err := ConfigSave(); err == nil {
|
|
||||||
c.Println("写入成功")
|
|
||||||
} else {
|
|
||||||
c.Println("保存失败", err.Error())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
configShell.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "d",
|
|
||||||
Help: "修改上级节点地址,格式为 ip:端口 多个节点以,隔开 注意:不会立刻连接设置节点, 当发生 节点掉线重连 时候会连接该地址",
|
|
||||||
Func: func(c *ishell.Context) {
|
|
||||||
if len(c.Args) != 1 {
|
|
||||||
c.Println("参数错误,格式为 ip:端口 多个节点以,隔开 如 d 192.168.1.1:8883,192.168.1.2:8883")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dstNode, err := common.ResolveTCPAddr(c.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
c.Println("参数错误,格式为 ip:端口 多个节点以,隔开 如 d 192.168.1.1:8883,192.168.1.2:8883")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
currentConfig.DstNode = dstNode
|
|
||||||
currentConfig.FileSave = false
|
|
||||||
},
|
|
||||||
})
|
|
||||||
configShell.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "password",
|
|
||||||
Help: "修改通讯密码,立即生效",
|
|
||||||
Func: func(c *ishell.Context) {
|
|
||||||
if len(c.Args) != 1 {
|
|
||||||
c.Println("参数错误,格式为 password \"123456\"")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.Println(c.Args)
|
|
||||||
currentConfig.Password = c.Args[0]
|
|
||||||
currentConfig.FileSave = false
|
|
||||||
aes.Key = aes.MD5_B(currentConfig.Password + string(cert.RsaPrivateKey[:16]))
|
|
||||||
},
|
|
||||||
})
|
|
||||||
configShell.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "port",
|
|
||||||
Help: "修改监听端口,立即生效",
|
|
||||||
Func: func(c *ishell.Context) {
|
|
||||||
if len(c.Args) != 1 {
|
|
||||||
c.Println("参数错误,格式为 port 8883")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
port, _ := strconv.Atoi(c.Args[0])
|
|
||||||
if port <= 0 || port > 65535 {
|
|
||||||
c.Println("参数错误,端口范围是1-65535")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.Println("正在关闭server监听")
|
|
||||||
if currentNode.listen != nil {
|
|
||||||
currentNode.listen.Close()
|
|
||||||
currentNode.listen = nil
|
|
||||||
}
|
|
||||||
currentConfig.Port = port
|
|
||||||
currentNode.port = port
|
|
||||||
currentConfig.FileSave = false
|
|
||||||
if err := StartServer(fmt.Sprintf(":%d", currentConfig.Port)); err != nil {
|
|
||||||
c.Printf("启动节点失败 %v, 请重新修改监听端口", currentConfig.Port)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
configShell.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "ip",
|
|
||||||
Help: "修改本节点连接ip,当其他节点进行额外连接时候,优先使用此ip连接",
|
|
||||||
Func: func(c *ishell.Context) {
|
|
||||||
if len(c.Args) != 1 {
|
|
||||||
c.Println("参数错误")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
currentConfig.ListenIp = c.Args[0]
|
|
||||||
currentNode.mainIp = currentConfig.ListenIp
|
|
||||||
currentConfig.FileSave = false
|
|
||||||
|
|
||||||
},
|
|
||||||
})
|
|
||||||
configShell.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "limit",
|
|
||||||
Help: "修改本节点Limit设置,使用方法 limit true",
|
|
||||||
Func: func(c *ishell.Context) {
|
|
||||||
if len(c.Args) != 1 {
|
|
||||||
c.Println("参数错误")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
currentConfig.Limit = c.Args[0] == "true"
|
|
||||||
currentConfig.FileSave = false
|
|
||||||
},
|
|
||||||
})
|
|
||||||
configShell.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "f",
|
|
||||||
Help: "修改配置文件名,使用方法 f config.yaml",
|
|
||||||
Func: func(c *ishell.Context) {
|
|
||||||
if len(c.Args) != 1 {
|
|
||||||
c.Println("参数错误")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
currentConfig.FileName = c.Args[0]
|
|
||||||
currentConfig.FileSave = false
|
|
||||||
},
|
|
||||||
})
|
|
||||||
configShell.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "uuid",
|
|
||||||
Help: "修改本节点UUID设置,使用方法uuid 字串符",
|
|
||||||
Func: func(c *ishell.Context) {
|
|
||||||
if len(c.Args) != 1 {
|
|
||||||
c.Println("参数错误")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if id, err := uuid.Parse(c.Args[0]); err == nil {
|
|
||||||
currentConfig.UUID = id.String()
|
|
||||||
currentConfig.FileSave = false
|
|
||||||
SetConfig(currentConfig)
|
|
||||||
} else {
|
|
||||||
c.Println("输入的uuid不是合法的uuid,建议使用xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
})
|
|
||||||
rootCli.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "config",
|
|
||||||
Help: "配置管理",
|
|
||||||
Func: func(c *ishell.Context) {
|
|
||||||
configShell.Run()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
remoteShell := cliInit()
|
|
||||||
|
|
||||||
remoteShell.SetPrompt("rakshasa\\remoteshell>")
|
|
||||||
|
|
||||||
fileShell := cliInit()
|
|
||||||
remoteShell.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "file",
|
|
||||||
Help: "连到节点进行文件管理,参数为id或者uuid",
|
|
||||||
Func: func(c *ishell.Context) {
|
|
||||||
if len(c.Args) != 1 {
|
|
||||||
c.Println("参数错误")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
workN, _ := getNode(c.Args[0])
|
|
||||||
if workN == nil {
|
|
||||||
c.Println("无法连接节点", c.Args[0])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if workN != nil {
|
|
||||||
fileShell.Set("node", workN)
|
|
||||||
result := make(chan interface{}, 1)
|
|
||||||
id := workN.storeQuery(result)
|
|
||||||
workN.Write(common.CMD_PWD, id, []byte(cert.RSAEncrypterByPriv(currentConfig.Password)))
|
|
||||||
select {
|
|
||||||
case pwd := <-result:
|
|
||||||
workN.deleteQuery(id)
|
|
||||||
pwd = strings.ReplaceAll(pwd.(string), "\\", "/")
|
|
||||||
fileShell.Set("pwd", pwd)
|
|
||||||
fileShell.SetPrompt(workN.uuid + " " + pwd.(string) + ">")
|
|
||||||
fileShell.Run()
|
|
||||||
case <-time.After(common.CMD_TIMEOUT):
|
|
||||||
workN.deleteQuery(id)
|
|
||||||
c.Println("连接", c.Args[0], "超时")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
})
|
|
||||||
fileShell.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "dir",
|
|
||||||
Help: "打印当前目录文件",
|
|
||||||
Func: func(c *ishell.Context) {
|
|
||||||
pwd := fileShell.Get("pwd")
|
|
||||||
|
|
||||||
n := c.Get("node").(*node)
|
|
||||||
resChan := make(chan interface{}, 1)
|
|
||||||
id := n.storeQuery(resChan)
|
|
||||||
n.Write(common.CMD_DIR, id, []byte(cert.RSAEncrypterByPriv(pwd.(string))))
|
|
||||||
select {
|
|
||||||
case res := <-resChan:
|
|
||||||
n.deleteQuery(id)
|
|
||||||
c.Println(res)
|
|
||||||
case <-time.After(common.CMD_TIMEOUT):
|
|
||||||
n.deleteQuery(id)
|
|
||||||
c.Println("dir time out")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
fileShell.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "cd",
|
|
||||||
Help: "切换工作目录",
|
|
||||||
Func: func(c *ishell.Context) {
|
|
||||||
if len(c.Args) != 1 {
|
|
||||||
c.Println("参数错误")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dir := c.Args[0]
|
|
||||||
pwd := fileShell.Get("pwd").(string)
|
|
||||||
n := c.Get("node").(*node)
|
|
||||||
|
|
||||||
if strings.Contains(dir, ":/") || dir[0] == '/' || dir == "~" {
|
|
||||||
pwd = dir
|
|
||||||
} else {
|
|
||||||
pwd += "/" + dir
|
|
||||||
pwd = strings.TrimRight(getRealPath(pwd), "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
resChan := make(chan interface{}, 1)
|
|
||||||
id := n.storeQuery(resChan)
|
|
||||||
n.Write(common.CMD_CD, id, []byte(cert.RSAEncrypterByPriv(pwd)))
|
|
||||||
|
|
||||||
select {
|
|
||||||
case res := <-resChan:
|
|
||||||
n.deleteQuery(id)
|
|
||||||
if err, ok := res.(error); ok {
|
|
||||||
c.Println(err.Error())
|
|
||||||
} else {
|
|
||||||
pwd = res.(string)
|
|
||||||
fileShell.Set("pwd", pwd)
|
|
||||||
c.SetPrompt(n.uuid + " " + pwd + ">")
|
|
||||||
}
|
|
||||||
|
|
||||||
case <-time.After(common.CMD_TIMEOUT):
|
|
||||||
n.deleteQuery(id)
|
|
||||||
c.Println("dir time out")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
fileShell.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "upload",
|
|
||||||
Help: "上传文件 ,upload 本地文件 远程目录(为空传到工作目录)",
|
|
||||||
Func: func(c *ishell.Context) {
|
|
||||||
if len(c.Args) != 1 && len(c.Args) != 2 {
|
|
||||||
c.Println("参数错误")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s, err := os.Stat(c.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
c.Println("打开本地文件", c.Args[0], "错误 ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
f, err := os.Open(c.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
c.Println("打开本地文件", c.Args[0], "错误 ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
pwd := fileShell.Get("pwd").(string) + "/"
|
|
||||||
n := c.Get("node").(*node)
|
|
||||||
|
|
||||||
if len(c.Args) == 2 {
|
|
||||||
pwd = c.Args[1]
|
|
||||||
}
|
|
||||||
pwd = strings.ReplaceAll(pwd, "\\", "/")
|
|
||||||
c.Args[0] = strings.ReplaceAll(c.Args[0], "\\", "/")
|
|
||||||
i := strings.LastIndex(c.Args[0], "/")
|
|
||||||
if i == -1 {
|
|
||||||
i = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if pwd[len(pwd)-1] == '/' {
|
|
||||||
pwd += c.Args[0][i:]
|
|
||||||
}
|
|
||||||
i = strings.LastIndex(pwd, "/")
|
|
||||||
if i == -1 {
|
|
||||||
i = 0
|
|
||||||
}
|
|
||||||
filename := pwd[i+1:]
|
|
||||||
dir := pwd[:i]
|
|
||||||
dir = strings.TrimRight(getRealPath(dir), "/") + "/"
|
|
||||||
pwd = dir + filename
|
|
||||||
resChan := make(chan interface{}, 9999) //避免收消息阻塞
|
|
||||||
|
|
||||||
filereadChan := make(chan []byte, 10)
|
|
||||||
|
|
||||||
upload := func() {
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
buf := make([]byte, common.MAX_PACKAGE-len(pwd)-9)
|
|
||||||
n, err := f.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resChan <- err
|
|
||||||
c.Println("读取文件", c.Args[0], "错误", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
filereadChan <- buf[:n]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := 0
|
|
||||||
be := len(pwd) + 1
|
|
||||||
id := n.storeQuery(resChan)
|
|
||||||
defer n.deleteQuery(id)
|
|
||||||
b := []byte(pwd)
|
|
||||||
b = append(b, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
|
||||||
c.ProgressBar().Start()
|
|
||||||
go upload()
|
|
||||||
var resnum int
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case data := <-filereadChan:
|
|
||||||
|
|
||||||
b[be] = byte(offset)
|
|
||||||
b[be+1] = byte(offset >> 8)
|
|
||||||
b[be+2] = byte(offset >> 16)
|
|
||||||
b[be+3] = byte(offset >> 24)
|
|
||||||
b[be+4] = byte(offset >> 32)
|
|
||||||
b[be+5] = byte(offset >> 40)
|
|
||||||
b[be+6] = byte(offset >> 48)
|
|
||||||
b[be+7] = byte(offset >> 56)
|
|
||||||
offset += len(data)
|
|
||||||
n.Write(common.CMD_UPLOAD, id, cert.RSAEncrypterByPrivByte(append(b, data...)))
|
|
||||||
case res := <-resChan:
|
|
||||||
switch v := res.(type) {
|
|
||||||
case error:
|
|
||||||
c.ProgressBar().Stop()
|
|
||||||
c.Println("上传失败", res)
|
|
||||||
return
|
|
||||||
case int64:
|
|
||||||
resnum++
|
|
||||||
i := v * 100 / s.Size()
|
|
||||||
c.ProgressBar().Suffix(fmt.Sprint(" ", i, "%"))
|
|
||||||
c.ProgressBar().Progress(int(i))
|
|
||||||
if v == s.Size() {
|
|
||||||
c.ProgressBar().Stop()
|
|
||||||
c.Println(c.Args[0], "上传成功")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if resnum >= 5 {
|
|
||||||
go upload()
|
|
||||||
resnum -= 10
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
c.Println("协议错误")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
case <-time.After(common.CMD_TIMEOUT):
|
|
||||||
c.ProgressBar().Stop()
|
|
||||||
c.Println("upload time out")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
})
|
|
||||||
fileShell.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "download",
|
|
||||||
Help: "下载文件 ,download 远程文件 本地目录(为空本地执行目录)",
|
|
||||||
Func: func(c *ishell.Context) {
|
|
||||||
if len(c.Args) != 1 && len(c.Args) != 2 {
|
|
||||||
c.Println("参数错误")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pwd := fileShell.Get("pwd").(string)
|
|
||||||
n := c.Get("node").(*node)
|
|
||||||
file := c.Args[0]
|
|
||||||
file = strings.ReplaceAll(file, "\\", "/")
|
|
||||||
|
|
||||||
if strings.Contains(file, ":/") || file[0] == '/' {
|
|
||||||
pwd = file
|
|
||||||
} else {
|
|
||||||
pwd += "/" + file
|
|
||||||
|
|
||||||
}
|
|
||||||
i := strings.LastIndex(pwd, "/")
|
|
||||||
if i == -1 {
|
|
||||||
i = 0
|
|
||||||
}
|
|
||||||
filename := pwd[i+1:]
|
|
||||||
dir := pwd[:i]
|
|
||||||
dir = strings.TrimRight(getRealPath(dir), "/") + "/"
|
|
||||||
mydir, err := os.Getwd()
|
|
||||||
local := "./" + filename
|
|
||||||
if err == nil {
|
|
||||||
local = mydir + "/" + filename
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(c.Args) == 2 {
|
|
||||||
s, err := os.Stat(c.Args[1])
|
|
||||||
if err == nil {
|
|
||||||
if s.IsDir() {
|
|
||||||
local = strings.TrimRight(c.Args[1], "/") + "/" + filename
|
|
||||||
} else {
|
|
||||||
local = c.Args[1]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
local = c.Args[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pwd = dir + filename
|
|
||||||
|
|
||||||
result := make(chan interface{}, 999)
|
|
||||||
id := n.storeQuery(result)
|
|
||||||
defer n.deleteQuery(id)
|
|
||||||
b := []byte(pwd)
|
|
||||||
b = append(b, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0}...)
|
|
||||||
total := int64(-1)
|
|
||||||
be := len(pwd) + 1
|
|
||||||
b[be] = byte(total)
|
|
||||||
b[be+1] = byte(total >> 8)
|
|
||||||
b[be+2] = byte(total >> 16)
|
|
||||||
b[be+3] = byte(total >> 24)
|
|
||||||
b[be+4] = byte(total >> 32)
|
|
||||||
b[be+5] = byte(total >> 40)
|
|
||||||
b[be+6] = byte(total >> 48)
|
|
||||||
b[be+7] = byte(total >> 56)
|
|
||||||
n.Write(common.CMD_DOWNLOAD, id, cert.RSAEncrypterByPrivByte(b))
|
|
||||||
c.ProgressBar().Start()
|
|
||||||
size := int64(0)
|
|
||||||
resnum := 0
|
|
||||||
total = 0
|
|
||||||
var f *os.File
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case res := <-result:
|
|
||||||
switch v := res.(type) {
|
|
||||||
case error:
|
|
||||||
c.ProgressBar().Stop()
|
|
||||||
c.Println("下载失败", res)
|
|
||||||
return
|
|
||||||
case int64:
|
|
||||||
var err error
|
|
||||||
size = v
|
|
||||||
f, err = os.OpenFile(local, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
|
|
||||||
if err != nil {
|
|
||||||
c.Println("本地文件 ", local, "写入失败", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
case []byte:
|
|
||||||
if f == nil {
|
|
||||||
c.Println("本地文件 ", local, "不可写入")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resnum++
|
|
||||||
num, err := f.Write(v)
|
|
||||||
if err != nil {
|
|
||||||
c.Println("本地文件 ", local, "写入失败", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if num != len(v) {
|
|
||||||
c.Println("本地文件 ", local, "写入失败,写入量不符")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
total += int64(num)
|
|
||||||
i := total * 100 / size
|
|
||||||
c.ProgressBar().Suffix(fmt.Sprint(" ", i, "%"))
|
|
||||||
c.ProgressBar().Progress(int(i))
|
|
||||||
if total == size {
|
|
||||||
c.ProgressBar().Stop()
|
|
||||||
c.Println(c.Args[0], "下载成功 文件保存到", local)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if resnum == 10 {
|
|
||||||
resnum -= 10
|
|
||||||
b[be] = byte(total)
|
|
||||||
b[be+1] = byte(total >> 8)
|
|
||||||
b[be+2] = byte(total >> 16)
|
|
||||||
b[be+3] = byte(total >> 24)
|
|
||||||
b[be+4] = byte(total >> 32)
|
|
||||||
b[be+5] = byte(total >> 40)
|
|
||||||
b[be+6] = byte(total >> 48)
|
|
||||||
b[be+7] = byte(total >> 56)
|
|
||||||
n.Write(common.CMD_DOWNLOAD, id, cert.RSAEncrypterByPrivByte(b))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
c.Println("协议错误")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
case <-time.After(common.CMD_TIMEOUT):
|
|
||||||
c.ProgressBar().Stop()
|
|
||||||
c.Println("upload time out")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
remoteShell.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "new",
|
|
||||||
Help: "与一个或者多个节点连接,使用方法 new ip:端口 多个地址以,间隔 如1080 127.0.0.1:1081,127.0.0.1:1082",
|
|
||||||
Func: func(c *ishell.Context) {
|
|
||||||
if len(c.Args) != 1 {
|
|
||||||
c.Println("参数错误,使用方法 connect ip:端口")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, addr := range strings.Split(c.Args[0], ",") {
|
|
||||||
_, err := getNode(addr)
|
|
||||||
if err != nil {
|
|
||||||
c.Println("连接", addr, "失败", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
remoteShell.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "shell",
|
|
||||||
Help: "反弹shell 使用方法 shell id/uuid 启动参数 ,启动参数可为空,win默认启动cmd,linux默认启动bash, 如 shell 1 powershell 。 shell 1 zsh",
|
|
||||||
Func: func(c *ishell.Context) {
|
|
||||||
if len(c.Args) < 1 {
|
|
||||||
c.Println("参数错误,例子 shell 1 powershell")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
param := ""
|
|
||||||
if len(c.Args) == 2 {
|
|
||||||
param = c.Args[1]
|
|
||||||
}
|
|
||||||
n, _ := getNode(c.Args[0])
|
|
||||||
if n == nil {
|
|
||||||
c.Println("无法连接节点", c.Args[0])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
res := make(chan interface{}, 999)
|
|
||||||
id := n.storeQuery(res)
|
|
||||||
|
|
||||||
defer n.deleteQuery(id)
|
|
||||||
p := StartCmdParam{
|
|
||||||
Param: param,
|
|
||||||
Size: common.GetSize(),
|
|
||||||
}
|
|
||||||
|
|
||||||
b, _ := json.Marshal(p)
|
|
||||||
n.Write(common.CMD_SHELL, id, cert.RSAEncrypterByPrivByte(b))
|
|
||||||
s := &remoteCmd{
|
|
||||||
cmd: nil,
|
|
||||||
stdin: nil,
|
|
||||||
inChan: make(chan []byte, 999),
|
|
||||||
translate: func(in []byte) ([]byte, error) { return in, nil },
|
|
||||||
pong: time.Now().Unix(),
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case i := <-res:
|
|
||||||
switch v := i.(type) {
|
|
||||||
case error:
|
|
||||||
c.Println("启动shell失败,错误", v.Error())
|
|
||||||
case []byte:
|
|
||||||
data := v
|
|
||||||
|
|
||||||
s.id = uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16 | uint32(data[3])<<24
|
|
||||||
switch data[4] {
|
|
||||||
case 0: //windows
|
|
||||||
if string(data[len(data)-6:]) == string([]byte{32, 57, 51, 54, 13, 10}) { //活动代码页: 936
|
|
||||||
//gbk转utf8
|
|
||||||
s.translate = func(in []byte) ([]byte, error) {
|
|
||||||
reader := transform.NewReader(bytes.NewReader(in), simplifiedchinese.GBK.NewDecoder())
|
|
||||||
d, e := ioutil.ReadAll(reader)
|
|
||||||
if e != nil {
|
|
||||||
return nil, e
|
|
||||||
}
|
|
||||||
return d, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 1: //linux
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
if !common.EnableTermVt {
|
|
||||||
s.translate = func(in []byte) ([]byte, error) {
|
|
||||||
if in[0] == 27 {
|
|
||||||
r, _ := regexp.Compile(`\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])`)
|
|
||||||
res := r.ReplaceAllString(string(in), "")
|
|
||||||
return []byte(res), nil
|
|
||||||
}
|
|
||||||
return in, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
atomic.CompareAndSwapInt32(&s.cmdStatus, 0, 1)
|
|
||||||
}
|
|
||||||
case <-time.After(common.CMD_TIMEOUT):
|
|
||||||
c.Println("启动shell失败,超时")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
n.shellMap.Store(s.id, s)
|
|
||||||
r, _ := readline.NewEx(&readline.Config{FuncIsTerminal: func() bool { return false }, ForcePrint: true})
|
|
||||||
defer func() {
|
|
||||||
n.shellMap.Delete(s.id)
|
|
||||||
atomic.StoreInt32(&s.cmdStatus, -1)
|
|
||||||
c.Println("请按回车键退出")
|
|
||||||
r.Close()
|
|
||||||
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
|
|
||||||
for {
|
|
||||||
|
|
||||||
switch s.cmdStatus {
|
|
||||||
case 1:
|
|
||||||
|
|
||||||
input, err := r.ReadlineEx()
|
|
||||||
if err != nil {
|
|
||||||
if err != readline.ErrInterrupt {
|
|
||||||
res <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if s.cmdStatus == 1 {
|
|
||||||
|
|
||||||
n.Write(common.CMD_SHELL_DATA, s.id, []byte{03})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if s.cmdStatus == 1 {
|
|
||||||
|
|
||||||
n.Write(common.CMD_SHELL_DATA, s.id, []byte(input+"\n"))
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0:
|
|
||||||
time.Sleep(time.Millisecond * 100)
|
|
||||||
case -1:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
tick := time.NewTicker(common.CMD_TIMEOUT / 2)
|
|
||||||
for {
|
|
||||||
|
|
||||||
select {
|
|
||||||
case b := <-s.inChan:
|
|
||||||
s.pong = time.Now().Unix()
|
|
||||||
if len(b) > 0 {
|
|
||||||
b, err := s.translate(b)
|
|
||||||
if err != nil {
|
|
||||||
c.Println("shell 运行失败", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Print(string(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
case v := <-res:
|
|
||||||
if err, ok := v.(error); ok {
|
|
||||||
if err.Error() != "退出shell" {
|
|
||||||
c.Println("运行shell", param, "失败", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
c.Println("无法处理消息", v)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
case <-tick.C:
|
|
||||||
s.ping = time.Now().Unix()
|
|
||||||
if s.ping-s.pong > int64(common.CMD_TIMEOUT/time.Second) {
|
|
||||||
c.Println("shell time out")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
n.Write(common.CMD_SHELL_DATA, s.id, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
rootCli.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "remoteshell",
|
|
||||||
Help: "远程shell",
|
|
||||||
Func: func(c *ishell.Context) {
|
|
||||||
remoteShell.Run()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打印节点
|
|
||||||
func printNodes(c *ishell.Context) {
|
|
||||||
l := clientLock.RLock()
|
|
||||||
defer l.RUnlock()
|
|
||||||
var list []*node
|
|
||||||
for _, n := range nodeMap {
|
|
||||||
list = append(list, n)
|
|
||||||
}
|
|
||||||
orderNode(list)
|
|
||||||
c.Println("ID UUID HostName GOOS IP listenIP")
|
|
||||||
c.Println("-----------------------------------------------------------------------------------------------------------------------------")
|
|
||||||
for k, n := range list {
|
|
||||||
n.id = k + 1
|
|
||||||
hostname := bytes.Repeat([]byte(" "), 22)
|
|
||||||
copy(hostname, n.hostName)
|
|
||||||
ip := bytes.Repeat([]byte(" "), 23)
|
|
||||||
if n.uuid == currentNode.uuid {
|
|
||||||
|
|
||||||
copy(ip, "(localhost)"+":"+strconv.Itoa(n.port))
|
|
||||||
} else {
|
|
||||||
copy(ip, n.addr+":"+strconv.Itoa(n.port))
|
|
||||||
}
|
|
||||||
|
|
||||||
listenip := n.mainIp
|
|
||||||
goos := bytes.Repeat([]byte(" "), 11)
|
|
||||||
copy(goos, n.goos)
|
|
||||||
c.Printf("%2d %s %s %s %s %s\n", n.id, n.uuid, hostname, goos, ip, listenip)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getRealPath(path string) string {
|
|
||||||
|
|
||||||
path_s := strings.Split(path, "/")
|
|
||||||
realpath := []string{}
|
|
||||||
if len(path_s) == 0 {
|
|
||||||
return "error"
|
|
||||||
}
|
|
||||||
for _, value := range path_s {
|
|
||||||
|
|
||||||
if value == ".." {
|
|
||||||
k := len(realpath)
|
|
||||||
kk := k - 1
|
|
||||||
realpath = append(realpath[:kk], realpath[k:]...)
|
|
||||||
} else {
|
|
||||||
realpath = append(realpath, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(realpath, "/")
|
|
||||||
}
|
|
||||||
func printConn() {
|
|
||||||
connMap.Range(func(key, value interface{}) bool {
|
|
||||||
fmt.Println(key)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
func getNode(arg string) (*node, error) {
|
|
||||||
l := clientLock.Lock()
|
|
||||||
defer l.Unlock()
|
|
||||||
id, err := strconv.Atoi(arg)
|
|
||||||
if err == nil {
|
|
||||||
for _, n := range nodeMap {
|
|
||||||
if n.id == id {
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for _, node := range nodeMap {
|
|
||||||
if fmt.Sprintf("%s:%d", node.mainIp, node.port) == arg {
|
|
||||||
return node, nil
|
|
||||||
} else if fmt.Sprintf("%s:%d", node.addr, node.port) == arg {
|
|
||||||
return node, nil
|
|
||||||
} else if node.uuid == arg {
|
|
||||||
return node, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return connectNew(arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user