diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 224b689..d2ce30f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,5 +47,6 @@ jobs: run: | go run contrib/bindata/main.go -pkg shell -o code/client/rule/shell/assets.go -prefix html/shell html/shell/... go run contrib/bindata/main.go -pkg vnc -o code/client/rule/vnc/assets.go -prefix html/vnc html/vnc/... + go run contrib/bindata/main.go -pkg code -o code/client/rule/code/assets.go -prefix html/code html/code/... go run contrib/bindata/main.go -pkg dashboard -o code/client/dashboard/assets.go -prefix html/dashboard html/dashboard/... go build -v code/client/main.go diff --git a/.gitignore b/.gitignore index 76fa527..e466a40 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ /bin -/test /build /release /tmp /logs /run -/*.yaml \ No newline at end of file +/*.yaml +/test_conf \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b0c20e9..9f1a989 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -127,4 +127,10 @@ # v0.9.1 -1. 修正客户端在断网后会假死的问题 \ No newline at end of file +1. 修正客户端在断网后会假死的问题 + +# v0.10.0 + +1. go版本升级到1.18.4 +2. 新增code-server支持 +3. 优化disconnect处理逻辑 \ No newline at end of file diff --git a/README.md b/README.md index 9812f60..c6401be 100644 --- a/README.md +++ b/README.md @@ -41,46 +41,34 @@ dashboard页面 ![dashboard](docs/imgs/dashboard.png) -linux命令行效果 +命令行 -![linux-shell](docs/imgs/shell_linux.png) +| platform | 386 | amd64 | arm | arm64 | +| -------- | :-: | :---: | :-: | :---: | +| windows | ✅ | ✅ | ✅ | ✅ | +| macos | | ✅ | | ✅ | +| linux | ✅ | ✅ | ✅ | ✅ | -windows命令行效果 +![shell](docs/imgs/shell.gif) -![windows-shell](docs/imgs/shell_win.png) +远程桌面 -windows2008远程桌面 +| platform | 386 | amd64 | arm | arm64 | +| -------- | :-: | :---: | :-: | :---: | +| windows | ✅ | ✅ | ❌ | ❌ | +| macos | | ✅ | | ✅ | +| linux | ✅ | ✅ | ❌ | ❌ | -![win2008-vnc](docs/imgs/vnc_win2008.png) +![vnc](docs/imgs/vnc.gif) -windows10远程桌面 - -![win10-vnc](docs/imgs/vnc_win10.png) - -windows11远程桌面 - -![win11-vnc](docs/imgs/vnc_win11.png) - -ubuntu远程桌面 - -![ubuntu-vnc](docs/imgs/vnc_ubuntu.png) - -fedora远程桌面 - -![fedora-vnc](docs/imgs/vnc_fedora.png) - -deepin远程桌面 - -![deepin-vnc](docs/imgs/vnc_deepin.png) - -macos远程桌面 - -![macos-vnc](docs/imgs/vnc_macos.png) - -windows读取剪贴板内容 +windows剪贴板内容 ![vnc-clipboard](docs/imgs/vnc_clipboard.png) +code-server支持 + +![code-server](docs/imgs/code_server.png) + ## 性能 在vmware环境下创建4C2G(AMD Ryzen 7 4800U with Radeon Graphics)测试环境,并进行all in one部署server、remote端和local端,使用bench规则进行压测,结果如下: diff --git a/build b/build index 254f55e..39d68fb 100755 --- a/build +++ b/build @@ -1,6 +1,6 @@ #!/bin/sh -VERSION=0.9.1 +VERSION=0.10.0 DOCKER_BUILDKIT=1 docker build -t natpass -f contrib/build/Dockerfile . docker run --rm \ diff --git a/build_all b/build_all index 78eed97..10b3c98 100755 --- a/build_all +++ b/build_all @@ -1,6 +1,6 @@ #!/bin/sh -VERSION=0.9.1 +VERSION=0.10.0 DOCKER_BUILDKIT=1 docker build -t natpass -f contrib/build/Dockerfile . docker run --rm \ diff --git a/code/client/app/app.go b/code/client/app/app.go index b940dcc..d827254 100644 --- a/code/client/app/app.go +++ b/code/client/app/app.go @@ -11,6 +11,7 @@ import ( "github.com/lwch/natpass/code/client/global" "github.com/lwch/natpass/code/client/rule" "github.com/lwch/natpass/code/client/rule/bench" + "github.com/lwch/natpass/code/client/rule/code" "github.com/lwch/natpass/code/client/rule/shell" "github.com/lwch/natpass/code/client/rule/vnc" "github.com/lwch/natpass/code/network" @@ -77,6 +78,10 @@ func (a *App) run() { b := bench.New(t) mgr.Add(b) go b.Handle(a.conn) + case "code-server": + cs := code.New(t, a.cfg.ReadTimeout, a.cfg.WriteTimeout) + mgr.Add(cs) + go cs.Handle(a.conn) } } @@ -93,11 +98,14 @@ func (a *App) run() { a.vncCreate(a.confDir, mgr, a.conn, msg) case network.ConnectRequest_bench: a.benchCreate(a.confDir, mgr, a.conn, msg) + case network.ConnectRequest_code: + a.codeCreate(a.confDir, mgr, a.conn, msg) } default: linkID = msg.GetLinkId() } if len(linkID) > 0 { + a.conn.ChanClose(linkID) logging.Error("link of %s not found, type=%s", linkID, msg.GetXType().String()) continue @@ -105,6 +113,13 @@ func (a *App) run() { } }() + go func() { + for { + id := <-a.conn.ChanDisconnect() + mgr.OnDisconnect(id) + } + }() + if a.cfg.DashboardEnabled { go func() { a.conn.Wait() diff --git a/code/client/app/callback.go b/code/client/app/callback.go index 16cd99b..8fc2f18 100644 --- a/code/client/app/callback.go +++ b/code/client/app/callback.go @@ -5,6 +5,7 @@ import ( "github.com/lwch/natpass/code/client/conn" "github.com/lwch/natpass/code/client/global" "github.com/lwch/natpass/code/client/rule" + "github.com/lwch/natpass/code/client/rule/code" "github.com/lwch/natpass/code/client/rule/shell" "github.com/lwch/natpass/code/client/rule/vnc" "github.com/lwch/natpass/code/network" @@ -12,7 +13,7 @@ import ( func (a *App) shellCreate(mgr *rule.Mgr, conn *conn.Conn, msg *network.Msg) { create := msg.GetCreq() - tn := mgr.Get(create.GetName(), msg.GetFrom()) + tn := mgr.GetLinked(create.GetName(), msg.GetFrom()) if tn == nil { tn = shell.New(global.Rule{ Name: create.GetName(), @@ -21,7 +22,7 @@ func (a *App) shellCreate(mgr *rule.Mgr, conn *conn.Conn, msg *network.Msg) { Exec: create.GetCshell().GetExec(), Env: create.GetCshell().GetEnv(), }, a.cfg.ReadTimeout, a.cfg.WriteTimeout) - mgr.Add(tn) + mgr.Add(tn.(rule.Rule)) } lk := tn.NewLink(msg.GetLinkId(), msg.GetFrom(), nil, conn).(*shell.Link) logging.Info("create link %s for shell rule [%s] from %s to %s", @@ -39,7 +40,7 @@ func (a *App) shellCreate(mgr *rule.Mgr, conn *conn.Conn, msg *network.Msg) { func (a *App) vncCreate(confDir string, mgr *rule.Mgr, conn *conn.Conn, msg *network.Msg) { create := msg.GetCreq() - tn := mgr.Get(create.GetName(), msg.GetFrom()) + tn := mgr.GetLinked(create.GetName(), msg.GetFrom()) if tn == nil { tn = vnc.New(global.Rule{ Name: create.GetName(), @@ -47,7 +48,7 @@ func (a *App) vncCreate(confDir string, mgr *rule.Mgr, conn *conn.Conn, msg *net Type: "vnc", Fps: create.GetCvnc().GetFps(), }, a.cfg.ReadTimeout, a.cfg.WriteTimeout) - mgr.Add(tn) + mgr.Add(tn.(rule.Rule)) } lk := tn.NewLink(msg.GetLinkId(), msg.GetFrom(), nil, conn).(*vnc.Link) logging.Info("create link %s for vnc rule [%s] from %s to %s", @@ -71,3 +72,28 @@ func (a *App) benchCreate(confDir string, mgr *rule.Mgr, conn *conn.Conn, msg *n msg.GetFrom(), a.cfg.ID) conn.SendConnectOK(msg.GetFrom(), msg.GetLinkId()) } + +func (a *App) codeCreate(confDir string, mgr *rule.Mgr, conn *conn.Conn, msg *network.Msg) { + create := msg.GetCreq() + tn := mgr.GetLinked(create.GetName(), msg.GetFrom()) + if tn == nil { + tn = code.New(global.Rule{ + Name: create.GetName(), + Target: msg.GetFrom(), + Type: "code-server", + }, a.cfg.ReadTimeout, a.cfg.WriteTimeout) + mgr.Add(tn.(rule.Rule)) + } + workspace := tn.NewLink(msg.GetLinkId(), msg.GetFrom(), nil, conn).(*code.Workspace) + logging.Info("create link %s for code-server rule [%s] from %s to %s", + msg.GetLinkId(), create.GetName(), + msg.GetFrom(), a.cfg.ID) + err := workspace.Exec(a.cfg.CodeDir) + if err != nil { + logging.Error("create vnc failed: %v", err) + conn.SendConnectError(msg.GetFrom(), msg.GetLinkId(), err.Error()) + return + } + conn.SendConnectOK(msg.GetFrom(), msg.GetLinkId()) + workspace.Forward() +} diff --git a/code/client/conn/conn.go b/code/client/conn/conn.go index b493b6e..cbd0344 100644 --- a/code/client/conn/conn.go +++ b/code/client/conn/conn.go @@ -3,6 +3,7 @@ package conn import ( "context" "crypto/tls" + "io" "net" "strings" "sync" @@ -15,16 +16,19 @@ import ( "github.com/lwch/runtime" ) +const dropBlockTimeout = 10 * time.Minute + // Conn connection type Conn struct { sync.RWMutex - cfg *global.Configure - conn *network.Conn - read map[string]chan *network.Msg // link id => channel - unknownRead chan *network.Msg // read message without link - write chan *network.Msg - lockDrop sync.RWMutex - drop map[string]time.Time + cfg *global.Configure + conn *network.Conn + read map[string]chan *network.Msg // link id => channel + unknownRead chan *network.Msg // read message without link + onDisconnect chan string + write chan *network.Msg + lockDrop sync.RWMutex + drop map[string]time.Time // runtime ctx context.Context cancel context.CancelFunc @@ -33,11 +37,12 @@ type Conn struct { // New new connection func New(cfg *global.Configure) *Conn { conn := &Conn{ - cfg: cfg, - read: make(map[string]chan *network.Msg), - unknownRead: make(chan *network.Msg, 1024), - write: make(chan *network.Msg, 1024), - drop: make(map[string]time.Time), + cfg: cfg, + read: make(map[string]chan *network.Msg), + unknownRead: make(chan *network.Msg, 1024), + onDisconnect: make(chan string, 1024), + write: make(chan *network.Msg, 10*1024*1024), + drop: make(map[string]time.Time), } runtime.Assert(conn.connect()) conn.ctx, conn.cancel = context.WithCancel(context.Background()) @@ -90,11 +95,68 @@ func writeHandshake(conn *network.Conn, cfg *global.Configure) error { return conn.WriteMessage(&msg, 5*time.Second) } +func (conn *Conn) isDrop(linkID string) bool { + conn.lockDrop.RLock() + defer conn.lockDrop.RUnlock() + _, ok := conn.drop[linkID] + return ok +} + +func (conn *Conn) getChan(linkID string) chan *network.Msg { + conn.RLock() + ch := conn.read[linkID] + conn.RUnlock() + if ch == nil { + ch = conn.unknownRead + } + return ch +} + +func (conn *Conn) hookDispatch(ch chan *network.Msg, msg *network.Msg) bool { + switch msg.GetXType() { + case network.Msg_disconnect: + conn.lockDrop.Lock() + conn.drop[msg.GetLinkId()] = time.Now().Add(dropBlockTimeout) + conn.lockDrop.Unlock() + conn.onDisconnect <- msg.GetLinkId() + logging.Info("connection %s disconnected", msg.GetLinkId()) + return false + } + return true +} + func (conn *Conn) loopRead() { defer utils.Recover("loopRead") defer conn.close() defer conn.cancel() var timeout int + run := func(msg *network.Msg) bool { + timeout = 0 + if msg.GetXType() == network.Msg_keepalive { + return true + } + logging.Debug("read message %s(%s) from %s", + msg.GetXType().String(), msg.GetLinkId(), msg.GetFrom()) + linkID := msg.GetLinkId() + if conn.isDrop(linkID) { + return true + } + ch := conn.getChan(linkID) + if !conn.hookDispatch(ch, msg) { + return true + } + select { + case ch <- msg: + case <-time.After(conn.cfg.WriteTimeout): + logging.Error("drop message: %s", msg.GetXType().String()) + conn.lockDrop.Lock() + conn.drop[msg.GetLinkId()] = time.Now().Add(dropBlockTimeout) + conn.lockDrop.Unlock() + case <-conn.ctx.Done(): + return false + } + return true + } for { msg, _, err := conn.conn.ReadMessage(conn.cfg.ReadTimeout) if err != nil { @@ -106,36 +168,17 @@ func (conn *Conn) loopRead() { } continue } + if strings.Contains(err.Error(), "use of closed network connection") { + logging.Error("read message: %v", err) + return + } + if err == io.EOF { + return + } logging.Error("read message: %v", err) continue } - timeout = 0 - if msg.GetXType() == network.Msg_keepalive { - continue - } - logging.Debug("read message %s(%s) from %s", - msg.GetXType().String(), msg.GetLinkId(), msg.GetFrom()) - linkID := msg.GetLinkId() - conn.lockDrop.RLock() - _, drop := conn.drop[linkID] - conn.lockDrop.RUnlock() - if drop { - continue - } - conn.RLock() - ch := conn.read[linkID] - conn.RUnlock() - if ch == nil { - ch = conn.unknownRead - } - select { - case ch <- msg: - case <-time.After(conn.cfg.ReadTimeout): - logging.Error("drop message: %s", msg.GetXType().String()) - conn.lockDrop.Lock() - conn.drop[msg.GetLinkId()] = time.Now().Add(time.Minute) - conn.lockDrop.Unlock() - case <-conn.ctx.Done(): + if !run(msg) { return } } @@ -182,7 +225,7 @@ func (conn *Conn) AddLink(id string) { logging.Info("add link %s", id) conn.Lock() if _, ok := conn.read[id]; !ok { - conn.read[id] = make(chan *network.Msg, 10) + conn.read[id] = make(chan *network.Msg, 1024) } conn.Unlock() } @@ -207,6 +250,11 @@ func (conn *Conn) ChanUnknown() <-chan *network.Msg { return conn.unknownRead } +// ChanDisconnect get channel of disconnect +func (conn *Conn) ChanDisconnect() <-chan string { + return conn.onDisconnect +} + func (conn *Conn) checkDrop() { for { time.Sleep(time.Second) @@ -232,3 +280,18 @@ func (conn *Conn) checkDrop() { func (conn *Conn) Wait() { <-conn.ctx.Done() } + +// ChanClose close read chan +func (conn *Conn) ChanClose(id string) { + conn.Lock() + ch := conn.read[id] + if ch != nil { + close(ch) + } + delete(conn.read, id) + conn.Unlock() + + conn.lockDrop.Lock() + conn.drop[id] = time.Now().Add(dropBlockTimeout) + conn.lockDrop.Unlock() +} diff --git a/code/client/conn/send_code.go b/code/client/conn/send_code.go new file mode 100644 index 0000000..f9ac533 --- /dev/null +++ b/code/client/conn/send_code.go @@ -0,0 +1,178 @@ +package conn + +import ( + "net/http" + "time" + + "github.com/lwch/logging" + "github.com/lwch/natpass/code/network" + "google.golang.org/protobuf/proto" +) + +func makeCodeHeader(header http.Header) map[string]*network.CodeHeaderValues { + ret := make(map[string]*network.CodeHeaderValues) + for key, values := range header { + data := make([]string, len(values)) + copy(data, values) + ret[key] = &network.CodeHeaderValues{ + Values: data, + } + } + return ret +} + +// SendCodeRequest send request +func (conn *Conn) SendCodeRequest(to, linkID string, requestID uint64, + method, uri string, body []byte, header http.Header) uint64 { + var msg network.Msg + msg.To = to + msg.XType = network.Msg_code_request + msg.LinkId = linkID + msg.Payload = &network.Msg_Csreq{ + Csreq: &network.CodeRequest{ + RequestId: requestID, + Method: method, + Uri: uri, + Body: dup(body), + Header: makeCodeHeader(header), + }, + } + select { + case conn.write <- &msg: + data, _ := proto.Marshal(&msg) + return uint64(len(data)) + case <-time.After(conn.cfg.WriteTimeout): + logging.Info("send: droped %s", network.Msg_code_request.String()) + return 0 + } +} + +// SendCodeConnect send connect +func (conn *Conn) SendCodeConnect(to, linkID string, requestID uint64, + uri string, header http.Header) uint64 { + var msg network.Msg + msg.To = to + msg.XType = network.Msg_code_connect + msg.LinkId = linkID + msg.Payload = &network.Msg_Csconn{ + Csconn: &network.CodeConnect{ + RequestId: requestID, + Uri: uri, + Header: makeCodeHeader(header), + }, + } + select { + case conn.write <- &msg: + data, _ := proto.Marshal(&msg) + return uint64(len(data)) + case <-time.After(conn.cfg.WriteTimeout): + logging.Info("send: droped %s", network.Msg_code_connect.String()) + return 0 + } +} + +// SendCodeResponseHeader send response header +func (conn *Conn) SendCodeResponseHeader(to, linkID string, requestID uint64, + code uint32, header http.Header) uint64 { + var msg network.Msg + msg.To = to + msg.XType = network.Msg_code_response_hdr + msg.LinkId = linkID + msg.Payload = &network.Msg_CsrepHdr{ + CsrepHdr: &network.CodeResponseHeader{ + RequestId: requestID, + Code: code, + Header: makeCodeHeader(header), + }, + } + select { + case conn.write <- &msg: + data, _ := proto.Marshal(&msg) + return uint64(len(data)) + case <-time.After(conn.cfg.WriteTimeout): + logging.Info("send: droped %s", network.Msg_code_response_hdr.String()) + return 0 + } +} + +// SendCodeResponseBody send response body +func (conn *Conn) SendCodeResponseBody(to, linkID string, requestID uint64, + idx uint32, ok, done bool, data []byte) uint64 { + var mask uint32 + if ok { + mask |= 1 + } + if done { + mask |= 2 + } + var msg network.Msg + msg.To = to + msg.XType = network.Msg_code_response_body + msg.LinkId = linkID + msg.Payload = &network.Msg_CsrepBody{ + CsrepBody: &network.CodeResponseBody{ + RequestId: requestID, + Index: idx, + Mask: mask, + Body: dup(data), + }, + } + select { + case conn.write <- &msg: + data, _ := proto.Marshal(&msg) + return uint64(len(data)) + case <-time.After(conn.cfg.WriteTimeout): + logging.Info("send: droped %s", network.Msg_code_response_body.String()) + return 0 + } +} + +// SendCodeResponseConnect send response connect +func (conn *Conn) SendCodeResponseConnect(to, linkID string, requestID uint64, + ok bool, msg string, header http.Header) uint64 { + var m network.Msg + m.To = to + m.XType = network.Msg_code_connect_response + m.LinkId = linkID + m.Payload = &network.Msg_CsconnRep{ + CsconnRep: &network.CodeConnectResponse{ + RequestId: requestID, + Ok: ok, + Msg: msg, + Header: makeCodeHeader(header), + }, + } + select { + case conn.write <- &m: + data, _ := proto.Marshal(&m) + return uint64(len(data)) + case <-time.After(conn.cfg.WriteTimeout): + logging.Info("send: droped %s", network.Msg_code_connect_response.String()) + return 0 + } +} + +// SendCodeData send data +func (conn *Conn) SendCodeData(to, linkID string, requestID uint64, + ok bool, t int, body []byte) uint64 { + var m network.Msg + m.To = to + m.XType = network.Msg_code_data + m.LinkId = linkID + m.Payload = &network.Msg_Csdata{ + Csdata: &network.CodeData{ + RequestId: requestID, + Ok: ok, + Type: uint32(t), + Data: dup(body), + }, + } + select { + case conn.write <- &m: + data, _ := proto.Marshal(&m) + return uint64(len(data)) + case <-time.After(conn.cfg.WriteTimeout): + logging.Info("send: droped %s", network.Msg_code_data.String()) + return 0 + } +} diff --git a/code/client/conn/send_conn.go b/code/client/conn/send_conn.go index 56d5f7a..eee6681 100644 --- a/code/client/conn/send_conn.go +++ b/code/client/conn/send_conn.go @@ -53,6 +53,13 @@ func (conn *Conn) SendConnectReq(id string, cfg global.Rule) { XType: network.ConnectRequest_bench, }, } + case "code-server": + msg.Payload = &network.Msg_Creq{ + Creq: &network.ConnectRequest{ + Name: cfg.Name, + XType: network.ConnectRequest_code, + }, + } } select { case conn.write <- &msg: diff --git a/code/client/conn/send_shell.go b/code/client/conn/send_shell.go index feb67b5..91c1756 100644 --- a/code/client/conn/send_shell.go +++ b/code/client/conn/send_shell.go @@ -9,11 +9,6 @@ import ( // SendShellData send shell data func (conn *Conn) SendShellData(to string, id string, data []byte) uint64 { - dup := func(data []byte) []byte { - ret := make([]byte, len(data)) - copy(ret, data) - return ret - } var msg network.Msg msg.To = to msg.XType = network.Msg_shell_data diff --git a/code/client/conn/send_vnc.go b/code/client/conn/send_vnc.go index 218be67..1603fc7 100644 --- a/code/client/conn/send_vnc.go +++ b/code/client/conn/send_vnc.go @@ -10,11 +10,6 @@ import ( // SendVNCImage send vnc image data func (conn *Conn) SendVNCImage(to string, id string, screen, rect image.Rectangle, encode network.VncImageEncoding, data []byte) { - dup := func(data []byte) []byte { - ret := make([]byte, len(data)) - copy(ret, data) - return ret - } var msg network.Msg msg.To = to msg.XType = network.Msg_vnc_image diff --git a/code/client/conn/utils.go b/code/client/conn/utils.go new file mode 100644 index 0000000..fe8ce12 --- /dev/null +++ b/code/client/conn/utils.go @@ -0,0 +1,7 @@ +package conn + +func dup(data []byte) []byte { + ret := make([]byte, len(data)) + copy(ret, data) + return ret +} diff --git a/code/client/dashboard/h_info.go b/code/client/dashboard/h_info.go index ee3307e..4f47cb4 100644 --- a/code/client/dashboard/h_info.go +++ b/code/client/dashboard/h_info.go @@ -16,11 +16,14 @@ func (db *Dashboard) Info(w http.ResponseWriter, r *http.Request) { } ret.Rules = len(db.cfg.Rules) db.mgr.Range(func(t rule.Rule) { - n := len(t.GetLinks()) - ret.VirtualLinks += n - if t.GetTypeName() == "shell" || - t.GetTypeName() == "vnc" { - ret.Session += n + lr, ok := t.(rule.LinkedRule) + if ok { + n := len(lr.GetLinks()) + ret.VirtualLinks += n + if t.GetTypeName() == "shell" || + t.GetTypeName() == "vnc" { + ret.Session += n + } } }) w.Header().Set("Content-Type", "application/json") diff --git a/code/client/dashboard/h_rules.go b/code/client/dashboard/h_rules.go index 4e1a00d..8284fb9 100644 --- a/code/client/dashboard/h_rules.go +++ b/code/client/dashboard/h_rules.go @@ -18,24 +18,29 @@ func (db *Dashboard) Rules(w http.ResponseWriter, r *http.Request) { } type item struct { Name string `json:"name"` - Remote string `json:"remote"` + Remote string `json:"remote,omitempty"` Port uint16 `json:"port"` Type string `json:"type"` Links []link `json:"links"` } var ret []item db.mgr.Range(func(t rule.Rule) { + lr, isLR := t.(rule.LinkedRule) var it item it.Name = t.GetName() - it.Remote = t.GetRemote() + if isLR { + it.Remote = lr.GetRemote() + } it.Port = t.GetPort() it.Type = t.GetTypeName() - for _, l := range t.GetLinks() { - var lk link - lk.ID = l.GetID() - lk.RecvBytes, lk.SendBytes = l.GetBytes() - lk.RecvPacket, lk.SendPacket = l.GetPackets() - it.Links = append(it.Links, lk) + if isLR { + for _, l := range lr.GetLinks() { + var lk link + lk.ID = l.GetID() + lk.RecvBytes, lk.SendBytes = l.GetBytes() + lk.RecvPacket, lk.SendPacket = l.GetPackets() + it.Links = append(it.Links, lk) + } } ret = append(ret, it) }) diff --git a/code/client/global/conf.go b/code/client/global/conf.go index a1920f9..9e08933 100644 --- a/code/client/global/conf.go +++ b/code/client/global/conf.go @@ -42,6 +42,7 @@ type Configure struct { DashboardListen string DashboardPort uint16 Rules []Rule + CodeDir string } // LoadConf load configure file @@ -65,12 +66,13 @@ func LoadConf(dir string) *Configure { Listen string `yaml:"listen"` Port uint16 `yaml:"port"` } `yaml:"dashboard"` - Rules []Rule `yaml:"rules"` + Rules []Rule `yaml:"rules"` + CodeDir string `yaml:"codedir"` } runtime.Assert(yaml.Decode(dir, &cfg)) for i, t := range cfg.Rules { switch t.Type { - case "shell", "vnc", "bench": + case "shell", "vnc", "bench", "code-server": default: panic(fmt.Sprintf("unsupported type: %s", t.Type)) } @@ -87,6 +89,11 @@ func LoadConf(dir string) *Configure { runtime.Assert(err) cfg.Log.Dir = filepath.Join(filepath.Dir(dir), cfg.Log.Dir) } + if !filepath.IsAbs(cfg.CodeDir) { + dir, err := os.Executable() + runtime.Assert(err) + cfg.CodeDir = filepath.Join(filepath.Dir(dir), cfg.CodeDir) + } return &Configure{ ID: cfg.ID, Server: cfg.Server, @@ -101,5 +108,6 @@ func LoadConf(dir string) *Configure { DashboardListen: cfg.Dashboard.Listen, DashboardPort: cfg.Dashboard.Port, Rules: cfg.Rules, + CodeDir: cfg.CodeDir, } } diff --git a/code/client/main.go b/code/client/main.go index 85a6432..caffffc 100644 --- a/code/client/main.go +++ b/code/client/main.go @@ -104,7 +104,8 @@ func main() { switch *act { case "install": runtime.Assert(sv.Install()) - utils.BuildLogDir(cfg.LogDir, *user) + utils.BuildDir(cfg.LogDir, *user) + utils.BuildDir(cfg.CodeDir, *user) case "uninstall": runtime.Assert(sv.Uninstall()) default: diff --git a/code/client/rule/code/.gitignore b/code/client/rule/code/.gitignore new file mode 100644 index 0000000..51dba36 --- /dev/null +++ b/code/client/rule/code/.gitignore @@ -0,0 +1 @@ +/assets.go \ No newline at end of file diff --git a/code/client/rule/code/code.go b/code/client/rule/code/code.go new file mode 100644 index 0000000..5016d04 --- /dev/null +++ b/code/client/rule/code/code.go @@ -0,0 +1,164 @@ +package code + +import ( + "errors" + "fmt" + "net" + "net/http" + "sync" + "time" + + "github.com/lwch/logging" + "github.com/lwch/natpass/code/client/conn" + "github.com/lwch/natpass/code/client/global" + "github.com/lwch/natpass/code/client/rule" + "github.com/lwch/natpass/code/network" + "github.com/lwch/runtime" +) + +// Code code-server handler +type Code struct { + sync.RWMutex + Name string + cfg global.Rule + workspace map[string]*Workspace + readTimeout time.Duration + writeTimeout time.Duration +} + +// New new code-server handler +func New(cfg global.Rule, readTimeout, writeTimeout time.Duration) *Code { + return &Code{ + Name: cfg.Name, + cfg: cfg, + workspace: make(map[string]*Workspace), + readTimeout: readTimeout, + writeTimeout: writeTimeout, + } +} + +// GetName get code-server rule name +func (code *Code) GetName() string { + return code.Name +} + +// GetTypeName get code-server rule type name +func (code *Code) GetTypeName() string { + return "code-server" +} + +// GetPort get listen port +func (code *Code) GetPort() uint16 { + return code.cfg.LocalPort +} + +// GetTarget get target of this rule +func (code *Code) GetTarget() string { + return code.cfg.Target +} + +// GetLinks get rule links +func (code *Code) GetLinks() []rule.Link { + ret := make([]rule.Link, 0, len(code.workspace)) + code.RLock() + for _, link := range code.workspace { + ret = append(ret, link) + } + code.RUnlock() + return ret +} + +// GetRemote get remote target name +func (code *Code) GetRemote() string { + return code.cfg.Target +} + +// NewLink new link +func (code *Code) NewLink(id, remote string, localConn net.Conn, remoteConn *conn.Conn) rule.Link { + remoteConn.AddLink(id) + ws := newWorkspace(code, id, code.cfg.Name, remote, remoteConn) + code.Lock() + code.workspace[ws.id] = ws + code.Unlock() + return ws +} + +// OnDisconnect on disconnect message +func (code *Code) OnDisconnect(id string) { + code.RLock() + workspace := code.workspace[id] + code.RUnlock() + if workspace != nil { + workspace.Close(false) + } +} + +// Handle handle code-server +func (code *Code) Handle(c *conn.Conn) { + defer func() { + if err := recover(); err != nil { + logging.Error("close code-server: %s, err=%v", code.Name, err) + } + }() + pf := func(cb func(*conn.Conn, http.ResponseWriter, *http.Request)) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + cb(c, w, r) + } + } + mux := http.NewServeMux() + mux.HandleFunc("/new", pf(code.New)) + mux.HandleFunc("/info", code.Info) + mux.HandleFunc("/forward/", pf(code.Forward)) + mux.HandleFunc("/", pf(code.Render)) + svr := &http.Server{ + Addr: fmt.Sprintf("%s:%d", code.cfg.LocalAddr, code.cfg.LocalPort), + Handler: mux, + } + runtime.Assert(svr.ListenAndServe()) +} + +func (code *Code) remove(id string) { + code.Lock() + delete(code.workspace, id) + code.Unlock() +} + +func (code *Code) new(conn *conn.Conn) (string, error) { + id, err := runtime.UUID(16, "0123456789abcdef") + if err != nil { + logging.Error("failed to generate link_id for code-server: %s, err=%v", + code.Name, err) + return "", err + } + link := code.NewLink(id, code.cfg.Target, nil, conn).(*Workspace) + conn.SendConnectReq(id, code.cfg) + ch := conn.ChanRead(id) + var repMsg *network.Msg + for { + var msg *network.Msg + select { + case msg = <-ch: + case <-time.After(time.Minute): + logging.Error("create code-server %s by rule %s failed, timtout", link.id, link.parent.Name) + return "", errWaitingTimeout + } + if msg.GetXType() != network.Msg_connect_rep { + conn.Reset(id, msg) + time.Sleep(code.readTimeout / 10) + continue + } + rep := msg.GetCrep() + if !rep.GetOk() { + logging.Error("create code-server %s by rule %s failed, err=%s", + link.id, link.parent.Name, rep.GetMsg()) + return "", errors.New(rep.GetMsg()) + } + repMsg = msg + break + } + logging.Info("create link %s for code-server rule [%s] from %s to %s", + link.GetID(), code.cfg.Name, + repMsg.GetTo(), repMsg.GetFrom()) + go link.localRead() + return id, nil +} diff --git a/code/client/rule/code/h_forward.go b/code/client/rule/code/h_forward.go new file mode 100644 index 0000000..2f17012 --- /dev/null +++ b/code/client/rule/code/h_forward.go @@ -0,0 +1,71 @@ +package code + +import ( + "net/http" + "strings" + + "github.com/lwch/logging" + "github.com/lwch/natpass/code/client/conn" +) + +// Forward forward code-server requests +func (code *Code) Forward(conn *conn.Conn, w http.ResponseWriter, r *http.Request) { + srcPath := r.URL.Path + srcQuery := r.URL.Query() + name := strings.TrimPrefix(r.URL.Path, "/forward/") + name = name[:strings.Index(name, "/")] + + r.URL.Path = strings.TrimPrefix(r.URL.Path, "/forward/"+name) + if len(r.URL.Path) == 0 { + r.URL.Path = "/" + } + + var id string + + const argName = "natpass_connection_id" + + if r.URL.Path == "/" && len(r.FormValue(argName)) == 0 { + var err error + id, err = code.new(conn) + if err != nil { + logging.Error("can not create workspace for [%s]: %v", code.Name, err) + http.Error(w, err.Error(), http.StatusBadGateway) + return + } + http.SetCookie(w, &http.Cookie{ + Name: "__NATPASS_CONNECTION_ID__", + Value: id, + }) + srcQuery.Set(argName, id) + http.Redirect(w, r, srcPath+"?"+srcQuery.Encode(), http.StatusTemporaryRedirect) + return + } + + cookie, err := r.Cookie("__NATPASS_CONNECTION_ID__") + if err != nil { + logging.Error("get connection id: %v", err) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + id = cookie.Value + + code.RLock() + workspace := code.workspace[id] + code.RUnlock() + + if workspace == nil { + http.NotFound(w, r) + return + } + + if code.isWebsocket(r) { + code.handleWebsocket(workspace, w, r) + } else { + code.handleRequest(conn, workspace, w, r) + } +} + +func (code *Code) isWebsocket(r *http.Request) bool { + upgrade := r.Header.Get("Connection") + return upgrade == "Upgrade" +} diff --git a/code/client/rule/code/h_forward_request.go b/code/client/rule/code/h_forward_request.go new file mode 100644 index 0000000..6d3ea58 --- /dev/null +++ b/code/client/rule/code/h_forward_request.go @@ -0,0 +1,83 @@ +package code + +import ( + "bytes" + "fmt" + "io" + "net/http" + + "github.com/lwch/logging" + "github.com/lwch/natpass/code/client/conn" + "github.com/lwch/natpass/code/network" +) + +func (code *Code) handleRequest(conn *conn.Conn, workspace *Workspace, w http.ResponseWriter, r *http.Request) { + reqID, err := workspace.SendRequest(r) + if err != nil { + logging.Error("send_request: %v", err) + http.Error(w, err.Error(), http.StatusBadGateway) + return + } + defer workspace.closeMessage(reqID) + resp := workspace.onResponse(reqID) + if resp == nil { + logging.Error("waiting for [%s] [%s] no response for request, uri=%s, request_id=%d", + workspace.id, workspace.name, r.URL.Path, reqID) + http.Error(w, "no response", http.StatusInternalServerError) + return + } + + if resp.GetXType() != network.Msg_code_response_hdr { + logging.Error("got invalid message type [%s] [%s]: %s", + workspace.id, workspace.name, resp.GetXType().String()) + http.Error(w, "invalid message type", http.StatusServiceUnavailable) + return + } + + hdr := resp.GetCsrepHdr() + for key, values := range hdr.GetHeader() { + for _, v := range values.GetValues() { + w.Header().Add(key, v) + } + } + + var idx uint32 + var buf bytes.Buffer + for { + msg := workspace.onResponse(reqID) + if msg == nil { + logging.Error("no response [%s] [%s]", workspace.id, workspace.name) + http.Error(w, "no response", http.StatusBadGateway) + return + } + if msg.GetXType() != network.Msg_code_response_body { + logging.Error("got invalid message type [%s] [%s]: %s", + workspace.id, workspace.name, resp.GetXType().String()) + http.Error(w, "invalid message type", http.StatusServiceUnavailable) + return + } + resp := msg.GetCsrepBody() + if resp.GetIndex() != idx { + logging.Error("loss data [%s] [%s]", workspace.id, workspace.name) + http.Error(w, "loss data", http.StatusResetContent) + return + } + if resp.GetMask()&1 == 0 { + logging.Error("read data [%s] [%s]: %s", workspace.id, workspace.name, string(resp.GetBody())) + http.Error(w, fmt.Sprintf("read error: %s", string(resp.GetBody())), http.StatusResetContent) + return + } + _, err = io.Copy(&buf, bytes.NewReader(resp.GetBody())) + if err != nil { + logging.Error("write body: %v", err) + http.Error(w, fmt.Sprintf("save data: %v", err), http.StatusInternalServerError) + return + } + if resp.GetMask()&2 > 0 { + w.WriteHeader(int(hdr.GetCode())) + io.Copy(w, &buf) + return + } + idx++ + } +} diff --git a/code/client/rule/code/h_forward_websocket.go b/code/client/rule/code/h_forward_websocket.go new file mode 100644 index 0000000..6f0a534 --- /dev/null +++ b/code/client/rule/code/h_forward_websocket.go @@ -0,0 +1,62 @@ +package code + +import ( + "net/http" + "sync" + + "github.com/gorilla/websocket" + "github.com/lwch/logging" + "github.com/lwch/natpass/code/network" +) + +var upgrader = websocket.Upgrader{ + EnableCompression: true, +} + +func (code *Code) handleWebsocket(workspace *Workspace, w http.ResponseWriter, r *http.Request) { + reqID, err := workspace.SendConnect(r) + if err != nil { + logging.Error("send_connect: %v", err) + http.Error(w, err.Error(), http.StatusBadGateway) + return + } + defer workspace.closeMessage(reqID) + resp := workspace.onResponse(reqID) + if resp == nil { + logging.Error("waiting for [%s] [%s] no response for websocket, request_id=%d", + workspace.id, workspace.name, reqID) + http.Error(w, "no response", http.StatusInternalServerError) + return + } + + if resp.GetXType() != network.Msg_code_connect_response { + logging.Error("got invalid message type [%s] [%s]: %s", + workspace.id, workspace.name, resp.GetXType().String()) + http.Error(w, "invalid message type", http.StatusServiceUnavailable) + return + } + + response := resp.GetCsconnRep() + if !response.GetOk() { + logging.Error("can not create websocket connection [%s] [%s]: %s", + workspace.id, workspace.name, response.GetMsg()) + http.Error(w, response.GetMsg(), http.StatusBadGateway) + return + } + + local, err := upgrader.Upgrade(w, r, nil) + if err != nil { + logging.Error("upgrade websocket connection [%s] [%s]: %v", + workspace.id, workspace.name, err) + http.Error(w, err.Error(), http.StatusBadGateway) + return + } + defer local.Close() + + var wg sync.WaitGroup + + wg.Add(2) + go workspace.ws2remote(&wg, reqID, local) + go workspace.remote2ws(&wg, reqID, local) + wg.Wait() +} diff --git a/code/client/rule/code/h_info.go b/code/client/rule/code/h_info.go new file mode 100644 index 0000000..f9e1311 --- /dev/null +++ b/code/client/rule/code/h_info.go @@ -0,0 +1,34 @@ +package code + +import ( + "encoding/json" + "net/http" + + "github.com/lwch/logging" +) + +// Info get workspace info +func (code *Code) Info(w http.ResponseWriter, r *http.Request) { + id := r.FormValue("id") + code.RLock() + workspace := code.workspace[id] + code.RUnlock() + if workspace == nil { + http.NotFound(w, r) + return + } + data, err := json.Marshal(map[string]interface{}{ + "name": code.Name, + "send_bytes": workspace.sendBytes, + "send_packet": workspace.sendPacket, + "recv_bytes": workspace.recvBytes, + "recv_packet": workspace.recvPacket, + }) + if err != nil { + logging.Error("marshal: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + w.Write(data) +} diff --git a/code/client/rule/code/h_new.go b/code/client/rule/code/h_new.go new file mode 100644 index 0000000..f643ede --- /dev/null +++ b/code/client/rule/code/h_new.go @@ -0,0 +1,12 @@ +package code + +import ( + "net/http" + + "github.com/lwch/natpass/code/client/conn" +) + +// New new code-server workspace +func (code *Code) New(conn *conn.Conn, w http.ResponseWriter, r *http.Request) { + w.Write([]byte(code.cfg.Name)) +} diff --git a/code/client/rule/code/h_render.go b/code/client/rule/code/h_render.go new file mode 100644 index 0000000..0f9fdfa --- /dev/null +++ b/code/client/rule/code/h_render.go @@ -0,0 +1,35 @@ +package code + +import ( + "bytes" + "io" + "mime" + "net/http" + "path/filepath" + "strings" + "text/template" + + "github.com/lwch/natpass/code/client/conn" +) + +// Render render code-server +func (code *Code) Render(conn *conn.Conn, w http.ResponseWriter, r *http.Request) { + dir := strings.TrimPrefix(r.URL.Path, "/") + data, err := Asset(dir) + if err == nil { + ctype := mime.TypeByExtension(filepath.Ext(dir)) + if ctype == "" { + ctype = http.DetectContentType(data) + } + w.Header().Set("Content-Type", ctype) + io.Copy(w, bytes.NewReader(data)) + return + } + data, _ = Asset("index.html") + tpl, err := template.New("all").Parse(string(data)) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + tpl.Execute(w, code) +} diff --git a/code/client/rule/code/workspace.go b/code/client/rule/code/workspace.go new file mode 100644 index 0000000..bb76189 --- /dev/null +++ b/code/client/rule/code/workspace.go @@ -0,0 +1,274 @@ +package code + +import ( + "bufio" + "errors" + "io" + "net" + "net/http" + "os" + "os/exec" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/gorilla/websocket" + "github.com/lwch/logging" + "github.com/lwch/natpass/code/client/conn" + "github.com/lwch/natpass/code/network" + "github.com/lwch/natpass/code/utils" + "google.golang.org/protobuf/proto" +) + +var errWaitingTimeout = errors.New("waiting for code-server startup more than 1 minute") + +// Workspace workspace of code-server +type Workspace struct { + sync.RWMutex + parent *Code + id string + target string + name string + exec *exec.Cmd + cli *http.Client + dailer websocket.Dialer + remote *conn.Conn + // runtime + sendBytes uint64 + recvBytes uint64 + sendPacket uint64 + recvPacket uint64 + requestID uint64 + onListen chan struct{} + onMessage map[uint64]chan *network.Msg +} + +func newWorkspace(parent *Code, id, name, target string, remote *conn.Conn) *Workspace { + name = strings.ReplaceAll(name, "/", "_") + name = strings.ReplaceAll(name, "\\", "_") + return &Workspace{ + parent: parent, + id: id, + target: target, + name: name, + remote: remote, + onListen: make(chan struct{}), + onMessage: make(map[uint64]chan *network.Msg), + } +} + +// GetID get link id +func (ws *Workspace) GetID() string { + return ws.id +} + +// GetBytes get send and recv bytes +func (ws *Workspace) GetBytes() (uint64, uint64) { + return ws.recvBytes, ws.sendBytes +} + +// GetPackets get send and recv packets +func (ws *Workspace) GetPackets() (uint64, uint64) { + return ws.recvPacket, ws.sendPacket +} + +// Exec execute code-server +func (ws *Workspace) Exec(dir string) error { + workdir := filepath.Join(dir, ws.name) + err := os.MkdirAll(workdir, 0755) + if err != nil { + logging.Error("can not create work dir[%s]: %v", workdir, err) + return err + } + sockDir := filepath.Join(workdir, ws.id+".sock") + logging.Info("EXTENSIONS_GALLERY=%s", os.Getenv("EXTENSIONS_GALLERY")) + ws.exec = exec.Command("code-server", + "--auth", "none", + "--socket", sockDir, + "--user-data-dir", filepath.Join(workdir, "data"), + "--extensions-dir", filepath.Join(workdir, "extensions"), + "--config", filepath.Join(workdir, "conf")) + ws.exec.Env = os.Environ() + stdout, err := ws.exec.StdoutPipe() + if err != nil { + logging.Error("can not get stdout pipe for link [%s] name [%s]", ws.id, ws.name) + return err + } + stderr, err := ws.exec.StderrPipe() + if err != nil { + logging.Error("can not get stderr pipe for link [%s] name [%s]", ws.id, ws.name) + return err + } + err = ws.exec.Start() + if err != nil { + logging.Error("can not start code-server for link [%s] name [%s]", ws.id, ws.name) + return err + } + go func() { + err = ws.exec.Wait() + if err != nil { + logging.Error("code-server [%s] [%s] exited: %v", ws.id, ws.name, err) + return + } + logging.Info("code-server for link [%s] name [%s] exited", ws.id, ws.name) + }() + ws.cli = &http.Client{ + Transport: &http.Transport{ + Dial: func(network, addr string) (net.Conn, error) { + return net.Dial("unix", sockDir) + }, + }, + } + ws.dailer = websocket.Dialer{ + NetDial: func(network, addr string) (net.Conn, error) { + return net.Dial("unix", sockDir) + }, + } + go ws.log(stdout, stderr) + select { + case <-time.After(time.Minute): + return errWaitingTimeout + case <-ws.onListen: + return nil + } +} + +// Close close workspace +func (ws *Workspace) Close(send bool) { + if ws.exec != nil && ws.exec.Process != nil { + ws.exec.Process.Kill() + } + if send { + ws.remote.SendDisconnect(ws.target, ws.id) + } + ws.parent.remove(ws.id) + ws.remote.ChanClose(ws.id) +} + +func (ws *Workspace) log(stdout, stderr io.ReadCloser) { + defer stdout.Close() + defer stderr.Close() + + var wg sync.WaitGroup + wg.Add(2) + + watch := func(target io.Reader) { + defer wg.Done() + s := bufio.NewScanner(target) + for s.Scan() { + if strings.Contains(s.Text(), "listening on") && + strings.Contains(s.Text(), ws.id+".sock") { + ws.onListen <- struct{}{} + } + logging.Info("code-server [%s] [%s]: %s", ws.id, ws.name, s.Text()) + } + } + + go watch(stdout) + go watch(stderr) + wg.Wait() +} + +// Forward forward data from remote node +func (ws *Workspace) Forward() { + go ws.remoteRead() +} + +func (ws *Workspace) remoteRead() { + defer utils.Recover("remoteRead") + defer ws.Close(true) + ch := ws.remote.ChanRead(ws.id) + for { + msg := <-ch + if msg == nil { + return + } + data, _ := proto.Marshal(msg) + ws.recvBytes += uint64(len(data)) + ws.recvPacket++ + switch msg.GetXType() { + case network.Msg_code_request: + go ws.handleRequest(msg) + case network.Msg_code_connect: + ws.Lock() + ws.onMessage[msg.GetCsconn().GetRequestId()] = make(chan *network.Msg, 1024) + ws.Unlock() + go ws.handleConnect(msg) + case network.Msg_code_data: + ws.writeMessage(msg.GetCsdata().GetRequestId(), msg) + } + } +} + +func (ws *Workspace) closeMessage(reqID uint64) { + ws.Lock() + if ch, ok := ws.onMessage[reqID]; ok { + close(ch) + delete(ws.onMessage, reqID) + } + ws.Unlock() +} + +func (ws *Workspace) localRead() { + defer utils.Recover("localRead") + defer ws.Close(true) + ch := ws.remote.ChanRead(ws.id) + for { + msg := <-ch + if msg == nil { + return + } + data, _ := proto.Marshal(msg) + ws.recvBytes += uint64(len(data)) + ws.recvPacket++ + switch msg.GetXType() { + case network.Msg_code_response_hdr: + ws.writeMessage(msg.GetCsrepHdr().GetRequestId(), msg) + case network.Msg_code_response_body: + ws.writeMessage(msg.GetCsrepBody().GetRequestId(), msg) + case network.Msg_code_connect_response: + ws.writeMessage(msg.GetCsconnRep().GetRequestId(), msg) + case network.Msg_code_data: + ws.writeMessage(msg.GetCsdata().GetRequestId(), msg) + } + } +} + +func (ws *Workspace) writeMessage(reqID uint64, msg *network.Msg) { + defer utils.Recover("writeMessage") + ws.RLock() + ch := ws.onMessage[reqID] + ws.RUnlock() + if ch != nil { + select { + case ch <- msg: + case <-time.After(ws.parent.writeTimeout): + logging.Error("code-server [%s] [%s]: message %s droped", + ws.id, ws.name, msg.GetXType().String()) + } + } +} + +func (ws *Workspace) chanResponse(reqID uint64) <-chan *network.Msg { + ws.RLock() + defer ws.RUnlock() + return ws.onMessage[reqID] +} + +func (ws *Workspace) onResponse(reqID uint64) *network.Msg { + ws.RLock() + ch := ws.onMessage[reqID] + ws.RUnlock() + if ch != nil { + select { + case msg := <-ch: + return msg + case <-time.After(ws.parent.readTimeout): + logging.Info("code-server [%s] [%s]: waiting for request %d timeout", + ws.id, ws.name, reqID) + return nil + } + } + return nil +} diff --git a/code/client/rule/code/workspace_local_request.go b/code/client/rule/code/workspace_local_request.go new file mode 100644 index 0000000..79c77b8 --- /dev/null +++ b/code/client/rule/code/workspace_local_request.go @@ -0,0 +1,56 @@ +package code + +import ( + "io/ioutil" + "net/http" + "strings" + "sync/atomic" + + "github.com/lwch/logging" + "github.com/lwch/natpass/code/network" +) + +// SendRequest send request from local node +func (ws *Workspace) SendRequest(r *http.Request) (uint64, error) { + reqID := atomic.AddUint64(&ws.requestID, 1) + body, err := ioutil.ReadAll(r.Body) + if err != nil { + logging.Error("send request for workspace [%s] [%s]: %v", ws.id, ws.name, err) + return 0, err + } + ws.Lock() + ws.onMessage[reqID] = make(chan *network.Msg, 1024) + ws.Unlock() + send := ws.remote.SendCodeRequest(ws.target, ws.id, reqID, + r.Method, r.URL.RequestURI(), body, r.Header) + ws.sendBytes += send + ws.sendPacket++ + return reqID, nil +} + +// SendConnect send websocket connect action from local node +func (ws *Workspace) SendConnect(r *http.Request) (uint64, error) { + reqID := atomic.AddUint64(&ws.requestID, 1) + ws.Lock() + ws.onMessage[reqID] = make(chan *network.Msg, 1024) + ws.Unlock() + + hdr := make(http.Header) + for key, values := range r.Header { + if strings.HasPrefix(key, "Sec-") { + continue + } + for _, value := range values { + hdr.Add(key, value) + } + } + + hdr.Del("Connection") + hdr.Del("Upgrade") + + send := ws.remote.SendCodeConnect(ws.target, ws.id, reqID, + r.URL.RequestURI(), hdr) + ws.sendBytes += send + ws.sendPacket++ + return reqID, nil +} diff --git a/code/client/rule/code/workspace_remote_response.go b/code/client/rule/code/workspace_remote_response.go new file mode 100644 index 0000000..d438423 --- /dev/null +++ b/code/client/rule/code/workspace_remote_response.go @@ -0,0 +1,160 @@ +package code + +import ( + "bytes" + "io" + "net/http" + "sync" + + "github.com/gorilla/websocket" + "github.com/lwch/logging" + "github.com/lwch/natpass/code/network" + "github.com/lwch/natpass/code/utils" +) + +func (ws *Workspace) handleRequest(msg *network.Msg) { + defer utils.Recover("handleRequest") + req := msg.GetCsreq() + request, err := http.NewRequest(req.GetMethod(), "http://unix"+req.GetUri(), bytes.NewReader(req.GetBody())) + if err != nil { + logging.Error("build request [%s] [%s]: %v", ws.id, ws.name, err) + ws.remote.SendCodeResponseHeader(ws.target, ws.id, req.GetRequestId(), + http.StatusInternalServerError, nil) + ws.remote.SendCodeResponseBody(ws.target, ws.id, req.GetRequestId(), + 0, false, true, []byte(err.Error())) + return + } + for key, values := range req.GetHeader() { + for _, v := range values.GetValues() { + request.Header.Add(key, v) + } + } + response, err := ws.cli.Do(request) + if err != nil { + logging.Error("call request [%s] [%s] [%s]: %v", ws.id, ws.name, req.GetUri(), err) + ws.remote.SendCodeResponseHeader(ws.target, ws.id, req.GetRequestId(), + http.StatusInternalServerError, nil) + ws.remote.SendCodeResponseBody(ws.target, ws.id, req.GetRequestId(), + 0, false, true, []byte(err.Error())) + return + } + defer response.Body.Close() + send := ws.remote.SendCodeResponseHeader(ws.target, ws.id, req.GetRequestId(), uint32(response.StatusCode), response.Header) + ws.sendBytes += send + ws.sendPacket++ + buf := make([]byte, 32*1024) // 32k block read + var idx uint32 + for { + n, err := response.Body.Read(buf) + if err != nil { + if err == io.EOF { + send := ws.remote.SendCodeResponseBody(ws.target, ws.id, req.GetRequestId(), idx, true, true, buf[:n]) + ws.sendBytes += send + ws.sendPacket++ + return + } + send := ws.remote.SendCodeResponseBody(ws.target, ws.id, req.GetRequestId(), idx, false, true, []byte(err.Error())) + ws.sendBytes += send + ws.sendPacket++ + logging.Error("call request [%s] [%s] [%s] read response data: %v", ws.id, ws.name, req.GetUri(), err) + return + } + send := ws.remote.SendCodeResponseBody(ws.target, ws.id, req.GetRequestId(), idx, true, false, buf[:n]) + ws.sendBytes += send + ws.sendPacket++ + idx++ + } +} + +func (ws *Workspace) handleConnect(msg *network.Msg) { + defer utils.Recover("handleConnect") + + connect := msg.GetCsconn() + hdr := make(http.Header) + for key, values := range connect.GetHeader() { + for _, value := range values.GetValues() { + hdr.Add(key, value) + } + } + + remote, resp, err := ws.dailer.Dial("ws://unix"+connect.GetUri(), hdr) + if err != nil { + logging.Error("dial websocket [%s] [%s]: %v", ws.id, ws.name, err) + send := ws.remote.SendCodeResponseConnect(ws.target, ws.id, connect.GetRequestId(), + false, err.Error(), nil) + ws.sendBytes += send + ws.sendPacket++ + return + } + defer remote.Close() + defer resp.Body.Close() + send := ws.remote.SendCodeResponseConnect(ws.target, ws.id, connect.GetRequestId(), + true, "", resp.Header) + ws.sendBytes += send + ws.sendPacket++ + + var wg sync.WaitGroup + + wg.Add(2) + go ws.ws2remote(&wg, connect.GetRequestId(), remote) + go ws.remote2ws(&wg, connect.GetRequestId(), remote) + wg.Wait() +} + +func (ws *Workspace) ws2remote(wg *sync.WaitGroup, reqID uint64, conn *websocket.Conn) { + defer wg.Done() + defer conn.Close() + defer ws.closeMessage(reqID) + for { + t, data, err := conn.ReadMessage() + if err != nil { + logging.Error("read_message [%s] [%s]: %v", ws.id, ws.name, err) + ws.SendData(reqID, false, websocket.TextMessage, []byte(err.Error())) + return + } + ws.SendData(reqID, true, t, data) + } +} + +// SendData send websocket data +func (ws *Workspace) SendData(reqID uint64, ok bool, t int, body []byte) { + for i := 0; i < len(body); i += 32 * 1024 { + end := i + 32*1024 + if end > len(body) { + end = len(body) + } + send := ws.remote.SendCodeData(ws.target, ws.id, reqID, + ok, t, body[i:end]) + ws.sendBytes += send + ws.sendPacket++ + } +} + +func (ws *Workspace) remote2ws(wg *sync.WaitGroup, reqID uint64, conn *websocket.Conn) { + defer wg.Done() + defer conn.Close() + defer ws.closeMessage(reqID) + ch := ws.chanResponse(reqID) + for { + msg := <-ch + if msg == nil { + return + } + if msg.GetXType() != network.Msg_code_data { + logging.Error("got invalid message type [%s] [%s]: %s", + ws.id, ws.name, msg.GetXType().String()) + return + } + data := msg.GetCsdata() + if !data.GetOk() { + logging.Error("read message [%s] [%s]: %s", + ws.id, ws.name, string(data.GetData())) + return + } + err := conn.WriteMessage(int(data.GetType()), data.GetData()) + if err != nil { + logging.Error("write_message [%s] [%s]: %v", ws.id, ws.name, err) + return + } + } +} diff --git a/code/client/rule/mgr.go b/code/client/rule/mgr.go index daa19f9..68d9333 100644 --- a/code/client/rule/mgr.go +++ b/code/client/rule/mgr.go @@ -18,13 +18,18 @@ type Link interface { // Rule rule interface type Rule interface { - NewLink(id, remote string, localConn net.Conn, remoteConn *conn.Conn) Link GetName() string - GetRemote() string GetPort() uint16 GetTypeName() string +} + +// LinkedRule linked rule interface +type LinkedRule interface { + NewLink(id, remote string, localConn net.Conn, remoteConn *conn.Conn) Link + GetRemote() string GetTarget() string GetLinks() []Link + OnDisconnect(string) } // Mgr rule manager @@ -45,13 +50,17 @@ func (mgr *Mgr) Add(rule Rule) { mgr.rules = append(mgr.rules, rule) } -// Get get rule by name -func (mgr *Mgr) Get(name, remote string) Rule { +// GetLinked get rule by name +func (mgr *Mgr) GetLinked(name, remote string) LinkedRule { mgr.RLock() defer mgr.RUnlock() - for _, t := range mgr.rules { - if t.GetName() == name && t.GetRemote() == remote { - return t + for _, r := range mgr.rules { + lr, ok := r.(LinkedRule) + if !ok { + continue + } + if r.GetName() == name && lr.GetRemote() == remote { + return lr } } return nil @@ -65,3 +74,16 @@ func (mgr *Mgr) Range(fn func(Rule)) { fn(t) } } + +// OnDisconnect on disconnect message +func (mgr *Mgr) OnDisconnect(id string) { + var links []LinkedRule + mgr.Range(func(r Rule) { + if lr, ok := r.(LinkedRule); ok { + links = append(links, lr) + } + }) + for _, link := range links { + go link.OnDisconnect(id) + } +} diff --git a/code/client/rule/shell/h_ws.go b/code/client/rule/shell/h_ws.go index f205bdb..f870cff 100644 --- a/code/client/rule/shell/h_ws.go +++ b/code/client/rule/shell/h_ws.go @@ -47,7 +47,7 @@ func (shell *Shell) localForward(id string, local *websocket.Conn) { shell.RLock() link := shell.links[id] shell.RUnlock() - defer link.Close() + defer link.Close(true) for { _, data, err := local.ReadMessage() if err != nil { @@ -66,7 +66,7 @@ func (shell *Shell) remoteForward(id string, local *websocket.Conn) { link := shell.links[id] shell.RUnlock() ch := link.remote.ChanRead(id) - defer link.Close() + defer link.Close(true) for { msg := <-ch if msg == nil { @@ -84,10 +84,6 @@ func (shell *Shell) remoteForward(id string, local *websocket.Conn) { } logging.Debug("remote read %d bytes: name=%s, id=%s", len(msg.GetSdata().GetData()), shell.Name, id) - case network.Msg_disconnect: - logging.Info("shell %s by rule %s closed by remote", - link.id, link.parent.Name) - return } } } diff --git a/code/client/rule/shell/link.go b/code/client/rule/shell/link.go index 09df2f9..f0204c7 100644 --- a/code/client/rule/shell/link.go +++ b/code/client/rule/shell/link.go @@ -45,14 +45,17 @@ func (link *Link) GetPackets() (uint64, uint64) { } // Close close link -func (link *Link) Close() { +func (link *Link) Close(send bool) { link.onClose() p, err := os.FindProcess(link.pid) if err == nil { p.Kill() } - link.remote.SendDisconnect(link.target, link.id) + if send { + link.remote.SendDisconnect(link.target, link.id) + } link.parent.remove(link.id) + link.remote.ChanClose(link.id) } // Forward forward data @@ -63,7 +66,7 @@ func (link *Link) Forward() { func (link *Link) remoteRead() { defer utils.Recover("remoteRead") - defer link.Close() + defer link.Close(true) ch := link.remote.ChanRead(link.id) for { msg := <-ch @@ -84,16 +87,13 @@ func (link *Link) remoteRead() { link.parent.Name, link.id, err) return } - case network.Msg_disconnect: - logging.Info("shell %s link %s closed by remote", link.parent.Name, link.id) - return } } } func (link *Link) localRead() { defer utils.Recover("localRead") - defer link.Close() + defer link.Close(true) buf := make([]byte, 16*1024) for { n, err := link.stdout.Read(buf) diff --git a/code/client/rule/shell/shell.go b/code/client/rule/shell/shell.go index 5909fa2..89596d6 100644 --- a/code/client/rule/shell/shell.go +++ b/code/client/rule/shell/shell.go @@ -86,6 +86,16 @@ func (shell *Shell) GetPort() uint16 { return shell.cfg.LocalPort } +// OnDisconnect on disconnect message +func (shell *Shell) OnDisconnect(id string) { + shell.RLock() + link := shell.links[id] + shell.RUnlock() + if link != nil { + link.Close(false) + } +} + // Handle handle shell func (shell *Shell) Handle(c *conn.Conn) { defer func() { diff --git a/code/client/rule/vnc/h_new.go b/code/client/rule/vnc/h_new.go index c88c254..6d30a41 100644 --- a/code/client/rule/vnc/h_new.go +++ b/code/client/rule/vnc/h_new.go @@ -15,7 +15,7 @@ import ( // New new vnc func (v *VNC) New(conn *conn.Conn, w http.ResponseWriter, r *http.Request) { if v.link != nil { - v.link.close() + v.link.Close(true) } q := r.FormValue("quality") s := r.FormValue("show_cursor") diff --git a/code/client/rule/vnc/link.go b/code/client/rule/vnc/link.go index d11bd62..5ef1731 100644 --- a/code/client/rule/vnc/link.go +++ b/code/client/rule/vnc/link.go @@ -82,7 +82,7 @@ func (link *Link) Forward() { } func (link *Link) remoteRead() { - defer link.close() + defer link.Close(true) ch := link.remote.ChanRead(link.id) for { msg := <-ch @@ -110,9 +110,6 @@ func (link *Link) remoteRead() { data := link.ps.GetClipboard() link.remote.SendVNCClipboardData(link.target, link.id, true, data) } - case network.Msg_disconnect: - logging.Info("link %s disconnected", link.id) - return } } } @@ -120,7 +117,7 @@ func (link *Link) remoteRead() { func (link *Link) localRead() { // TODO: exit by context defer utils.Recover("capture") - defer link.close() + defer link.Close(true) img, err := link.ps.Capture(3 * time.Second) if err != nil { logging.Error("capture: %v", err) @@ -152,11 +149,16 @@ func (link *Link) localRead() { } } -func (link *Link) close() { +// Close close link +func (link *Link) Close(send bool) { if link.ps != nil { link.ps.Close() } - link.remote.SendDisconnect(link.target, link.id) + if send { + link.remote.SendDisconnect(link.target, link.id) + } + link.parent.remove(link.id) + link.remote.ChanClose(link.id) } func cut(src *image.RGBA, rect image.Rectangle) *image.RGBA { diff --git a/code/client/rule/vnc/vnc.go b/code/client/rule/vnc/vnc.go index 2ea02fa..b453077 100644 --- a/code/client/rule/vnc/vnc.go +++ b/code/client/rule/vnc/vnc.go @@ -47,7 +47,7 @@ func (v *VNC) NewLink(id, remote string, localConn net.Conn, remoteConn *conn.Co remote: remoteConn, } if v.link != nil { - v.link.close() + v.link.Close(true) } v.link = link return link @@ -86,6 +86,11 @@ func (v *VNC) GetPort() uint16 { return v.cfg.LocalPort } +// OnDisconnect on disconnect message +func (v *VNC) OnDisconnect(id string) { + // TODO +} + // Handle handle shell func (v *VNC) Handle(c *conn.Conn) { defer func() { @@ -110,3 +115,7 @@ func (v *VNC) Handle(c *conn.Conn) { } runtime.Assert(svr.ListenAndServe()) } + +func (v *VNC) remove(id string) { + v.link = nil +} diff --git a/code/network/code.pb.go b/code/network/code.pb.go new file mode 100644 index 0000000..bf6671a --- /dev/null +++ b/code/network/code.pb.go @@ -0,0 +1,725 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc v3.21.2 +// source: code.proto + +package network + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type CodeHeaderValues struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Values []string `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` +} + +func (x *CodeHeaderValues) Reset() { + *x = CodeHeaderValues{} + if protoimpl.UnsafeEnabled { + mi := &file_code_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CodeHeaderValues) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CodeHeaderValues) ProtoMessage() {} + +func (x *CodeHeaderValues) ProtoReflect() protoreflect.Message { + mi := &file_code_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CodeHeaderValues.ProtoReflect.Descriptor instead. +func (*CodeHeaderValues) Descriptor() ([]byte, []int) { + return file_code_proto_rawDescGZIP(), []int{0} +} + +func (x *CodeHeaderValues) GetValues() []string { + if x != nil { + return x.Values + } + return nil +} + +// normal request +type CodeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Method string `protobuf:"bytes,2,opt,name=method,proto3" json:"method,omitempty"` + Uri string `protobuf:"bytes,3,opt,name=uri,proto3" json:"uri,omitempty"` + Body []byte `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"` + Header map[string]*CodeHeaderValues `protobuf:"bytes,5,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *CodeRequest) Reset() { + *x = CodeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_code_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CodeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CodeRequest) ProtoMessage() {} + +func (x *CodeRequest) ProtoReflect() protoreflect.Message { + mi := &file_code_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CodeRequest.ProtoReflect.Descriptor instead. +func (*CodeRequest) Descriptor() ([]byte, []int) { + return file_code_proto_rawDescGZIP(), []int{1} +} + +func (x *CodeRequest) GetRequestId() uint64 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *CodeRequest) GetMethod() string { + if x != nil { + return x.Method + } + return "" +} + +func (x *CodeRequest) GetUri() string { + if x != nil { + return x.Uri + } + return "" +} + +func (x *CodeRequest) GetBody() []byte { + if x != nil { + return x.Body + } + return nil +} + +func (x *CodeRequest) GetHeader() map[string]*CodeHeaderValues { + if x != nil { + return x.Header + } + return nil +} + +type CodeResponseHeader struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Code uint32 `protobuf:"varint,2,opt,name=code,proto3" json:"code,omitempty"` + Header map[string]*CodeHeaderValues `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *CodeResponseHeader) Reset() { + *x = CodeResponseHeader{} + if protoimpl.UnsafeEnabled { + mi := &file_code_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CodeResponseHeader) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CodeResponseHeader) ProtoMessage() {} + +func (x *CodeResponseHeader) ProtoReflect() protoreflect.Message { + mi := &file_code_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CodeResponseHeader.ProtoReflect.Descriptor instead. +func (*CodeResponseHeader) Descriptor() ([]byte, []int) { + return file_code_proto_rawDescGZIP(), []int{2} +} + +func (x *CodeResponseHeader) GetRequestId() uint64 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *CodeResponseHeader) GetCode() uint32 { + if x != nil { + return x.Code + } + return 0 +} + +func (x *CodeResponseHeader) GetHeader() map[string]*CodeHeaderValues { + if x != nil { + return x.Header + } + return nil +} + +type CodeResponseBody struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Index uint32 `protobuf:"varint,2,opt,name=index,proto3" json:"index,omitempty"` + Mask uint32 `protobuf:"varint,3,opt,name=mask,proto3" json:"mask,omitempty"` + Body []byte `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"` +} + +func (x *CodeResponseBody) Reset() { + *x = CodeResponseBody{} + if protoimpl.UnsafeEnabled { + mi := &file_code_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CodeResponseBody) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CodeResponseBody) ProtoMessage() {} + +func (x *CodeResponseBody) ProtoReflect() protoreflect.Message { + mi := &file_code_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CodeResponseBody.ProtoReflect.Descriptor instead. +func (*CodeResponseBody) Descriptor() ([]byte, []int) { + return file_code_proto_rawDescGZIP(), []int{3} +} + +func (x *CodeResponseBody) GetRequestId() uint64 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *CodeResponseBody) GetIndex() uint32 { + if x != nil { + return x.Index + } + return 0 +} + +func (x *CodeResponseBody) GetMask() uint32 { + if x != nil { + return x.Mask + } + return 0 +} + +func (x *CodeResponseBody) GetBody() []byte { + if x != nil { + return x.Body + } + return nil +} + +// websocket +type CodeConnect struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Uri string `protobuf:"bytes,2,opt,name=uri,proto3" json:"uri,omitempty"` + Header map[string]*CodeHeaderValues `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *CodeConnect) Reset() { + *x = CodeConnect{} + if protoimpl.UnsafeEnabled { + mi := &file_code_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CodeConnect) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CodeConnect) ProtoMessage() {} + +func (x *CodeConnect) ProtoReflect() protoreflect.Message { + mi := &file_code_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CodeConnect.ProtoReflect.Descriptor instead. +func (*CodeConnect) Descriptor() ([]byte, []int) { + return file_code_proto_rawDescGZIP(), []int{4} +} + +func (x *CodeConnect) GetRequestId() uint64 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *CodeConnect) GetUri() string { + if x != nil { + return x.Uri + } + return "" +} + +func (x *CodeConnect) GetHeader() map[string]*CodeHeaderValues { + if x != nil { + return x.Header + } + return nil +} + +type CodeConnectResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Ok bool `protobuf:"varint,2,opt,name=ok,proto3" json:"ok,omitempty"` + Msg string `protobuf:"bytes,3,opt,name=msg,proto3" json:"msg,omitempty"` + Header map[string]*CodeHeaderValues `protobuf:"bytes,4,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *CodeConnectResponse) Reset() { + *x = CodeConnectResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_code_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CodeConnectResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CodeConnectResponse) ProtoMessage() {} + +func (x *CodeConnectResponse) ProtoReflect() protoreflect.Message { + mi := &file_code_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CodeConnectResponse.ProtoReflect.Descriptor instead. +func (*CodeConnectResponse) Descriptor() ([]byte, []int) { + return file_code_proto_rawDescGZIP(), []int{5} +} + +func (x *CodeConnectResponse) GetRequestId() uint64 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *CodeConnectResponse) GetOk() bool { + if x != nil { + return x.Ok + } + return false +} + +func (x *CodeConnectResponse) GetMsg() string { + if x != nil { + return x.Msg + } + return "" +} + +func (x *CodeConnectResponse) GetHeader() map[string]*CodeHeaderValues { + if x != nil { + return x.Header + } + return nil +} + +type CodeData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Ok bool `protobuf:"varint,2,opt,name=ok,proto3" json:"ok,omitempty"` + Type uint32 `protobuf:"varint,3,opt,name=type,proto3" json:"type,omitempty"` + Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *CodeData) Reset() { + *x = CodeData{} + if protoimpl.UnsafeEnabled { + mi := &file_code_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CodeData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CodeData) ProtoMessage() {} + +func (x *CodeData) ProtoReflect() protoreflect.Message { + mi := &file_code_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CodeData.ProtoReflect.Descriptor instead. +func (*CodeData) Descriptor() ([]byte, []int) { + return file_code_proto_rawDescGZIP(), []int{6} +} + +func (x *CodeData) GetRequestId() uint64 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *CodeData) GetOk() bool { + if x != nil { + return x.Ok + } + return false +} + +func (x *CodeData) GetType() uint32 { + if x != nil { + return x.Type + } + return 0 +} + +func (x *CodeData) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +var File_code_proto protoreflect.FileDescriptor + +var file_code_proto_rawDesc = []byte{ + 0x0a, 0x0a, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x22, 0x2c, 0x0a, 0x12, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x68, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x22, 0xfe, 0x01, 0x0a, 0x0c, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, + 0x72, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x12, 0x0a, + 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, + 0x79, 0x12, 0x39, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x21, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6f, 0x64, 0x65, + 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x1a, 0x56, 0x0a, 0x0b, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe4, 0x01, 0x0a, 0x14, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1d, 0x0a, + 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, + 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, + 0x12, 0x41, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x29, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x5f, + 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2e, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x1a, 0x56, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6f, + 0x64, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x71, 0x0a, 0x12, 0x63, + 0x6f, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x62, 0x6f, 0x64, + 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, + 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6d, 0x61, 0x73, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, + 0x64, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xd2, + 0x01, 0x0a, 0x0c, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, + 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x10, + 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, + 0x12, 0x39, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x21, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x5f, + 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x1a, 0x56, 0x0a, 0x0b, 0x48, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0xf4, 0x01, 0x0a, 0x15, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, + 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x0e, 0x0a, 0x02, + 0x6f, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x10, 0x0a, 0x03, + 0x6d, 0x73, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x42, + 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, + 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x1a, 0x56, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6f, 0x64, + 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x62, 0x0a, 0x09, 0x63, 0x6f, + 0x64, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0c, + 0x5a, 0x0a, 0x2e, 0x2f, 0x3b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_code_proto_rawDescOnce sync.Once + file_code_proto_rawDescData = file_code_proto_rawDesc +) + +func file_code_proto_rawDescGZIP() []byte { + file_code_proto_rawDescOnce.Do(func() { + file_code_proto_rawDescData = protoimpl.X.CompressGZIP(file_code_proto_rawDescData) + }) + return file_code_proto_rawDescData +} + +var file_code_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_code_proto_goTypes = []interface{}{ + (*CodeHeaderValues)(nil), // 0: network.code_header_values + (*CodeRequest)(nil), // 1: network.code_request + (*CodeResponseHeader)(nil), // 2: network.code_response_header + (*CodeResponseBody)(nil), // 3: network.code_response_body + (*CodeConnect)(nil), // 4: network.code_connect + (*CodeConnectResponse)(nil), // 5: network.code_connect_response + (*CodeData)(nil), // 6: network.code_data + nil, // 7: network.code_request.HeaderEntry + nil, // 8: network.code_response_header.HeaderEntry + nil, // 9: network.code_connect.HeaderEntry + nil, // 10: network.code_connect_response.HeaderEntry +} +var file_code_proto_depIdxs = []int32{ + 7, // 0: network.code_request.header:type_name -> network.code_request.HeaderEntry + 8, // 1: network.code_response_header.header:type_name -> network.code_response_header.HeaderEntry + 9, // 2: network.code_connect.header:type_name -> network.code_connect.HeaderEntry + 10, // 3: network.code_connect_response.header:type_name -> network.code_connect_response.HeaderEntry + 0, // 4: network.code_request.HeaderEntry.value:type_name -> network.code_header_values + 0, // 5: network.code_response_header.HeaderEntry.value:type_name -> network.code_header_values + 0, // 6: network.code_connect.HeaderEntry.value:type_name -> network.code_header_values + 0, // 7: network.code_connect_response.HeaderEntry.value:type_name -> network.code_header_values + 8, // [8:8] is the sub-list for method output_type + 8, // [8:8] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name +} + +func init() { file_code_proto_init() } +func file_code_proto_init() { + if File_code_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_code_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CodeHeaderValues); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_code_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CodeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_code_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CodeResponseHeader); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_code_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CodeResponseBody); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_code_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CodeConnect); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_code_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CodeConnectResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_code_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CodeData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_code_proto_rawDesc, + NumEnums: 0, + NumMessages: 11, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_code_proto_goTypes, + DependencyIndexes: file_code_proto_depIdxs, + MessageInfos: file_code_proto_msgTypes, + }.Build() + File_code_proto = out.File + file_code_proto_rawDesc = nil + file_code_proto_goTypes = nil + file_code_proto_depIdxs = nil +} diff --git a/code/network/code.proto b/code/network/code.proto new file mode 100644 index 0000000..3761eb9 --- /dev/null +++ b/code/network/code.proto @@ -0,0 +1,51 @@ +syntax = "proto3"; + +package network; +option go_package="./;network"; + +message code_header_values { + repeated string values = 1; +} + +// normal request +message code_request { + uint64 request_id = 1; + string method = 2; + string uri = 3; + bytes body = 4; + map header = 5; +} + +message code_response_header { + uint64 request_id = 1; + uint32 code = 2; + map header = 3; +} + +message code_response_body { + uint64 request_id = 1; + uint32 index = 2; + uint32 mask = 3; + bytes body = 4; +} + +// websocket +message code_connect { + uint64 request_id = 1; + string uri = 2; + map header = 3; +} + +message code_connect_response { + uint64 request_id = 1; + bool ok = 2; + string msg = 3; + map header = 4; +} + +message code_data { + uint64 request_id = 1; + bool ok = 2; + uint32 type = 3; + bytes data = 4; +} \ No newline at end of file diff --git a/code/network/connect.pb.go b/code/network/connect.pb.go index 709868b..029e37a 100644 --- a/code/network/connect.pb.go +++ b/code/network/connect.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 -// protoc v3.6.1 +// protoc-gen-go v1.28.0 +// protoc v3.21.2 // source: connect.proto package network @@ -28,6 +28,7 @@ const ( ConnectRequest_shell ConnectRequestType = 2 // shell ConnectRequest_vnc ConnectRequestType = 3 // vnc ConnectRequest_bench ConnectRequestType = 4 // benchmark + ConnectRequest_code ConnectRequestType = 5 // code-server ) // Enum value maps for ConnectRequestType. @@ -38,6 +39,7 @@ var ( 2: "shell", 3: "vnc", 4: "bench", + 5: "code", } ConnectRequestType_value = map[string]int32{ "tcp": 0, @@ -45,6 +47,7 @@ var ( "shell": 2, "vnc": 3, "bench": 4, + "code": 5, } ) @@ -429,7 +432,7 @@ var file_connect_proto_rawDesc = []byte{ 0x01, 0x28, 0x0d, 0x52, 0x03, 0x66, 0x70, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, 0xaa, 0x02, 0x0a, 0x0f, 0x63, + 0x28, 0x08, 0x52, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, 0xb4, 0x02, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x32, 0x0a, 0x05, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, @@ -444,16 +447,16 @@ var file_connect_proto_rawDesc = []byte{ 0x06, 0x63, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x12, 0x2a, 0x0a, 0x04, 0x63, 0x76, 0x6e, 0x63, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x76, 0x6e, 0x63, 0x48, 0x00, 0x52, 0x04, 0x63, - 0x76, 0x6e, 0x63, 0x22, 0x37, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x74, + 0x76, 0x6e, 0x63, 0x22, 0x41, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x74, 0x63, 0x70, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x75, 0x64, 0x70, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x76, 0x6e, 0x63, 0x10, - 0x03, 0x12, 0x09, 0x0a, 0x05, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x10, 0x04, 0x42, 0x09, 0x0a, 0x07, - 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x34, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, - 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x6d, - 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x42, 0x0c, 0x5a, - 0x0a, 0x2e, 0x2f, 0x3b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x03, 0x12, 0x09, 0x0a, 0x05, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, + 0x63, 0x6f, 0x64, 0x65, 0x10, 0x05, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x22, 0x34, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x42, 0x0c, 0x5a, 0x0a, 0x2e, 0x2f, 0x3b, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/code/network/connect.proto b/code/network/connect.proto index e189837..093a9f8 100644 --- a/code/network/connect.proto +++ b/code/network/connect.proto @@ -26,6 +26,7 @@ message connect_request { shell = 2; // shell vnc = 3; // vnc bench = 4; // benchmark + code = 5; // code-server } string name = 1; // rule name type _type = 2; // rule type diff --git a/code/network/forward.pb.go b/code/network/forward.pb.go index 5303fb2..cd91bd1 100644 --- a/code/network/forward.pb.go +++ b/code/network/forward.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 -// protoc v3.6.1 +// protoc-gen-go v1.28.0 +// protoc v3.21.2 // source: forward.proto package network diff --git a/code/network/msg.pb.go b/code/network/msg.pb.go index fdad4c3..e60fd04 100644 --- a/code/network/msg.pb.go +++ b/code/network/msg.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 -// protoc v3.6.1 +// protoc-gen-go v1.28.0 +// protoc v3.21.2 // source: msg.proto package network @@ -41,6 +41,13 @@ const ( Msg_vnc_cad MsgType = 24 // ctrl+alt+del Msg_vnc_scroll MsgType = 25 Msg_vnc_clipboard MsgType = 26 + // code-server + Msg_code_request MsgType = 30 + Msg_code_response_hdr MsgType = 31 + Msg_code_response_body MsgType = 32 + Msg_code_connect MsgType = 33 + Msg_code_connect_response MsgType = 34 + Msg_code_data MsgType = 35 ) // Enum value maps for MsgType. @@ -62,24 +69,36 @@ var ( 24: "vnc_cad", 25: "vnc_scroll", 26: "vnc_clipboard", + 30: "code_request", + 31: "code_response_hdr", + 32: "code_response_body", + 33: "code_connect", + 34: "code_connect_response", + 35: "code_data", } MsgType_value = map[string]int32{ - "unknown": 0, - "handshake": 1, - "keepalive": 2, - "connect_req": 3, - "connect_rep": 4, - "disconnect": 5, - "forward": 6, - "shell_resize": 10, - "shell_data": 11, - "vnc_ctrl": 20, - "vnc_image": 21, - "vnc_mouse": 22, - "vnc_keyboard": 23, - "vnc_cad": 24, - "vnc_scroll": 25, - "vnc_clipboard": 26, + "unknown": 0, + "handshake": 1, + "keepalive": 2, + "connect_req": 3, + "connect_rep": 4, + "disconnect": 5, + "forward": 6, + "shell_resize": 10, + "shell_data": 11, + "vnc_ctrl": 20, + "vnc_image": 21, + "vnc_mouse": 22, + "vnc_keyboard": 23, + "vnc_cad": 24, + "vnc_scroll": 25, + "vnc_clipboard": 26, + "code_request": 30, + "code_response_hdr": 31, + "code_response_body": 32, + "code_connect": 33, + "code_connect_response": 34, + "code_data": 35, } ) @@ -179,6 +198,12 @@ type Msg struct { // *Msg_Vkbd // *Msg_Vscroll // *Msg_Vclipboard + // *Msg_Csreq + // *Msg_CsrepHdr + // *Msg_CsrepBody + // *Msg_Csconn + // *Msg_CsconnRep + // *Msg_Csdata Payload isMsg_Payload `protobuf_oneof:"payload"` } @@ -333,6 +358,48 @@ func (x *Msg) GetVclipboard() *VncClipboard { return nil } +func (x *Msg) GetCsreq() *CodeRequest { + if x, ok := x.GetPayload().(*Msg_Csreq); ok { + return x.Csreq + } + return nil +} + +func (x *Msg) GetCsrepHdr() *CodeResponseHeader { + if x, ok := x.GetPayload().(*Msg_CsrepHdr); ok { + return x.CsrepHdr + } + return nil +} + +func (x *Msg) GetCsrepBody() *CodeResponseBody { + if x, ok := x.GetPayload().(*Msg_CsrepBody); ok { + return x.CsrepBody + } + return nil +} + +func (x *Msg) GetCsconn() *CodeConnect { + if x, ok := x.GetPayload().(*Msg_Csconn); ok { + return x.Csconn + } + return nil +} + +func (x *Msg) GetCsconnRep() *CodeConnectResponse { + if x, ok := x.GetPayload().(*Msg_CsconnRep); ok { + return x.CsconnRep + } + return nil +} + +func (x *Msg) GetCsdata() *CodeData { + if x, ok := x.GetPayload().(*Msg_Csdata); ok { + return x.Csdata + } + return nil +} + type isMsg_Payload interface { isMsg_Payload() } @@ -387,6 +454,31 @@ type Msg_Vclipboard struct { Vclipboard *VncClipboard `protobuf:"bytes,35,opt,name=vclipboard,proto3,oneof"` } +type Msg_Csreq struct { + // code-server + Csreq *CodeRequest `protobuf:"bytes,40,opt,name=csreq,proto3,oneof"` +} + +type Msg_CsrepHdr struct { + CsrepHdr *CodeResponseHeader `protobuf:"bytes,41,opt,name=csrep_hdr,json=csrepHdr,proto3,oneof"` +} + +type Msg_CsrepBody struct { + CsrepBody *CodeResponseBody `protobuf:"bytes,42,opt,name=csrep_body,json=csrepBody,proto3,oneof"` +} + +type Msg_Csconn struct { + Csconn *CodeConnect `protobuf:"bytes,43,opt,name=csconn,proto3,oneof"` +} + +type Msg_CsconnRep struct { + CsconnRep *CodeConnectResponse `protobuf:"bytes,44,opt,name=csconn_rep,json=csconnRep,proto3,oneof"` +} + +type Msg_Csdata struct { + Csdata *CodeData `protobuf:"bytes,45,opt,name=csdata,proto3,oneof"` +} + func (*Msg_Hsp) isMsg_Payload() {} func (*Msg_Creq) isMsg_Payload() {} @@ -411,6 +503,18 @@ func (*Msg_Vscroll) isMsg_Payload() {} func (*Msg_Vclipboard) isMsg_Payload() {} +func (*Msg_Csreq) isMsg_Payload() {} + +func (*Msg_CsrepHdr) isMsg_Payload() {} + +func (*Msg_CsrepBody) isMsg_Payload() {} + +func (*Msg_Csconn) isMsg_Payload() {} + +func (*Msg_CsconnRep) isMsg_Payload() {} + +func (*Msg_Csdata) isMsg_Payload() {} + var File_msg_proto protoreflect.FileDescriptor var file_msg_proto_rawDesc = []byte{ @@ -418,70 +522,99 @@ var file_msg_proto_rawDesc = []byte{ 0x77, 0x6f, 0x72, 0x6b, 0x1a, 0x0d, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0d, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0b, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, - 0x09, 0x76, 0x6e, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x25, 0x0a, 0x11, 0x68, 0x61, - 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, - 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x65, 0x6e, - 0x63, 0x22, 0xad, 0x07, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x26, 0x0a, 0x05, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x52, 0x04, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x17, 0x0a, 0x07, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x69, 0x64, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x69, 0x6e, 0x6b, 0x49, 0x64, 0x12, 0x2e, - 0x0a, 0x03, 0x68, 0x73, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, - 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x00, 0x52, 0x03, 0x68, 0x73, 0x70, 0x12, 0x2e, - 0x0a, 0x04, 0x63, 0x72, 0x65, 0x71, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x63, 0x72, 0x65, 0x71, 0x12, 0x2f, - 0x0a, 0x04, 0x63, 0x72, 0x65, 0x70, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x72, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x04, 0x63, 0x72, 0x65, 0x70, 0x12, - 0x24, 0x0a, 0x05, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, - 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, - 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x31, 0x0a, 0x07, 0x73, 0x72, 0x65, 0x73, 0x69, 0x7a, 0x65, - 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x2e, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x5f, 0x72, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x48, 0x00, 0x52, - 0x07, 0x73, 0x72, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x73, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x2e, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x05, - 0x73, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x63, 0x74, 0x72, 0x6c, 0x18, 0x1e, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x76, - 0x6e, 0x63, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x48, 0x00, 0x52, 0x05, 0x76, 0x63, - 0x74, 0x72, 0x6c, 0x12, 0x28, 0x0a, 0x04, 0x76, 0x69, 0x6d, 0x67, 0x18, 0x1f, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x12, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x76, 0x6e, 0x63, 0x5f, - 0x69, 0x6d, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x04, 0x76, 0x69, 0x6d, 0x67, 0x12, 0x2c, 0x0a, - 0x06, 0x76, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x18, 0x20, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, - 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x76, 0x6e, 0x63, 0x5f, 0x6d, 0x6f, 0x75, 0x73, - 0x65, 0x48, 0x00, 0x52, 0x06, 0x76, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x76, - 0x6b, 0x62, 0x64, 0x18, 0x21, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x2e, 0x76, 0x6e, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, - 0x48, 0x00, 0x52, 0x04, 0x76, 0x6b, 0x62, 0x64, 0x12, 0x2f, 0x0a, 0x07, 0x76, 0x73, 0x63, 0x72, - 0x6f, 0x6c, 0x6c, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x2e, 0x76, 0x6e, 0x63, 0x5f, 0x73, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x48, 0x00, - 0x52, 0x07, 0x76, 0x73, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x12, 0x38, 0x0a, 0x0a, 0x76, 0x63, 0x6c, - 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x18, 0x23, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, - 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x76, 0x6e, 0x63, 0x5f, 0x63, 0x6c, 0x69, 0x70, - 0x62, 0x6f, 0x61, 0x72, 0x64, 0x48, 0x00, 0x52, 0x0a, 0x76, 0x63, 0x6c, 0x69, 0x70, 0x62, 0x6f, - 0x61, 0x72, 0x64, 0x22, 0x80, 0x02, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, - 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x68, 0x61, 0x6e, - 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x6b, 0x65, 0x65, 0x70, - 0x61, 0x6c, 0x69, 0x76, 0x65, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x64, 0x69, 0x73, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x10, 0x05, 0x12, 0x0b, 0x0a, 0x07, 0x66, 0x6f, 0x72, - 0x77, 0x61, 0x72, 0x64, 0x10, 0x06, 0x12, 0x10, 0x0a, 0x0c, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x5f, - 0x72, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x10, 0x0a, 0x12, 0x0e, 0x0a, 0x0a, 0x73, 0x68, 0x65, 0x6c, - 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x10, 0x0b, 0x12, 0x0c, 0x0a, 0x08, 0x76, 0x6e, 0x63, 0x5f, - 0x63, 0x74, 0x72, 0x6c, 0x10, 0x14, 0x12, 0x0d, 0x0a, 0x09, 0x76, 0x6e, 0x63, 0x5f, 0x69, 0x6d, - 0x61, 0x67, 0x65, 0x10, 0x15, 0x12, 0x0d, 0x0a, 0x09, 0x76, 0x6e, 0x63, 0x5f, 0x6d, 0x6f, 0x75, - 0x73, 0x65, 0x10, 0x16, 0x12, 0x10, 0x0a, 0x0c, 0x76, 0x6e, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x62, - 0x6f, 0x61, 0x72, 0x64, 0x10, 0x17, 0x12, 0x0b, 0x0a, 0x07, 0x76, 0x6e, 0x63, 0x5f, 0x63, 0x61, - 0x64, 0x10, 0x18, 0x12, 0x0e, 0x0a, 0x0a, 0x76, 0x6e, 0x63, 0x5f, 0x73, 0x63, 0x72, 0x6f, 0x6c, - 0x6c, 0x10, 0x19, 0x12, 0x11, 0x0a, 0x0d, 0x76, 0x6e, 0x63, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x62, - 0x6f, 0x61, 0x72, 0x64, 0x10, 0x1a, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x42, 0x0c, 0x5a, 0x0a, 0x2e, 0x2f, 0x3b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x09, 0x76, 0x6e, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0a, 0x63, 0x6f, 0x64, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x25, 0x0a, 0x11, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, + 0x61, 0x6b, 0x65, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x65, + 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x65, 0x6e, 0x63, 0x22, 0xf5, 0x0a, + 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x26, 0x0a, 0x05, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x6d, + 0x73, 0x67, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, + 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x74, + 0x6f, 0x12, 0x17, 0x0a, 0x07, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x6c, 0x69, 0x6e, 0x6b, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x03, 0x68, 0x73, + 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x48, 0x00, 0x52, 0x03, 0x68, 0x73, 0x70, 0x12, 0x2e, 0x0a, 0x04, 0x63, 0x72, + 0x65, 0x71, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x63, 0x72, 0x65, 0x71, 0x12, 0x2f, 0x0a, 0x04, 0x63, 0x72, + 0x65, 0x70, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x04, 0x63, 0x72, 0x65, 0x70, 0x12, 0x24, 0x0a, 0x05, 0x5f, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x04, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x31, 0x0a, 0x07, 0x73, 0x72, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x73, 0x68, 0x65, + 0x6c, 0x6c, 0x5f, 0x72, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x48, 0x00, 0x52, 0x07, 0x73, 0x72, 0x65, + 0x73, 0x69, 0x7a, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x73, 0x64, 0x61, 0x74, 0x61, 0x18, 0x15, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x73, 0x68, + 0x65, 0x6c, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x05, 0x73, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x63, 0x74, 0x72, 0x6c, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x76, 0x6e, 0x63, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x48, 0x00, 0x52, 0x05, 0x76, 0x63, 0x74, 0x72, 0x6c, 0x12, + 0x28, 0x0a, 0x04, 0x76, 0x69, 0x6d, 0x67, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x76, 0x6e, 0x63, 0x5f, 0x69, 0x6d, 0x61, 0x67, + 0x65, 0x48, 0x00, 0x52, 0x04, 0x76, 0x69, 0x6d, 0x67, 0x12, 0x2c, 0x0a, 0x06, 0x76, 0x6d, 0x6f, + 0x75, 0x73, 0x65, 0x18, 0x20, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x2e, 0x76, 0x6e, 0x63, 0x5f, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x48, 0x00, 0x52, + 0x06, 0x76, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x76, 0x6b, 0x62, 0x64, 0x18, + 0x21, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, + 0x76, 0x6e, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x48, 0x00, 0x52, 0x04, + 0x76, 0x6b, 0x62, 0x64, 0x12, 0x2f, 0x0a, 0x07, 0x76, 0x73, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x18, + 0x22, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, + 0x76, 0x6e, 0x63, 0x5f, 0x73, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x48, 0x00, 0x52, 0x07, 0x76, 0x73, + 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x12, 0x38, 0x0a, 0x0a, 0x76, 0x63, 0x6c, 0x69, 0x70, 0x62, 0x6f, + 0x61, 0x72, 0x64, 0x18, 0x23, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x2e, 0x76, 0x6e, 0x63, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, + 0x64, 0x48, 0x00, 0x52, 0x0a, 0x76, 0x63, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x12, + 0x2d, 0x0a, 0x05, 0x63, 0x73, 0x72, 0x65, 0x71, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x63, 0x73, 0x72, 0x65, 0x71, 0x12, 0x3c, + 0x0a, 0x09, 0x63, 0x73, 0x72, 0x65, 0x70, 0x5f, 0x68, 0x64, 0x72, 0x18, 0x29, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6f, 0x64, 0x65, + 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x48, 0x00, 0x52, 0x08, 0x63, 0x73, 0x72, 0x65, 0x70, 0x48, 0x64, 0x72, 0x12, 0x3c, 0x0a, 0x0a, + 0x63, 0x73, 0x72, 0x65, 0x70, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x2a, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x5f, + 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x48, 0x00, 0x52, + 0x09, 0x63, 0x73, 0x72, 0x65, 0x70, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x73, + 0x63, 0x6f, 0x6e, 0x6e, 0x18, 0x2b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x48, 0x00, 0x52, 0x06, 0x63, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x12, 0x3f, 0x0a, 0x0a, 0x63, + 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x72, 0x65, 0x70, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1e, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, + 0x00, 0x52, 0x09, 0x63, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x52, 0x65, 0x70, 0x12, 0x2c, 0x0a, 0x06, + 0x63, 0x73, 0x64, 0x61, 0x74, 0x61, 0x18, 0x2d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, + 0x48, 0x00, 0x52, 0x06, 0x63, 0x73, 0x64, 0x61, 0x74, 0x61, 0x22, 0xfd, 0x02, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, + 0x12, 0x0d, 0x0a, 0x09, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x10, 0x01, 0x12, + 0x0d, 0x0a, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x10, 0x02, 0x12, 0x0f, + 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x10, 0x03, 0x12, + 0x0f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x10, 0x04, + 0x12, 0x0e, 0x0a, 0x0a, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x10, 0x05, + 0x12, 0x0b, 0x0a, 0x07, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x10, 0x06, 0x12, 0x10, 0x0a, + 0x0c, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x5f, 0x72, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x10, 0x0a, 0x12, + 0x0e, 0x0a, 0x0a, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x10, 0x0b, 0x12, + 0x0c, 0x0a, 0x08, 0x76, 0x6e, 0x63, 0x5f, 0x63, 0x74, 0x72, 0x6c, 0x10, 0x14, 0x12, 0x0d, 0x0a, + 0x09, 0x76, 0x6e, 0x63, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x10, 0x15, 0x12, 0x0d, 0x0a, 0x09, + 0x76, 0x6e, 0x63, 0x5f, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x10, 0x16, 0x12, 0x10, 0x0a, 0x0c, 0x76, + 0x6e, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x10, 0x17, 0x12, 0x0b, 0x0a, + 0x07, 0x76, 0x6e, 0x63, 0x5f, 0x63, 0x61, 0x64, 0x10, 0x18, 0x12, 0x0e, 0x0a, 0x0a, 0x76, 0x6e, + 0x63, 0x5f, 0x73, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x10, 0x19, 0x12, 0x11, 0x0a, 0x0d, 0x76, 0x6e, + 0x63, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x10, 0x1a, 0x12, 0x10, 0x0a, + 0x0c, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0x1e, 0x12, + 0x15, 0x0a, 0x11, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x5f, 0x68, 0x64, 0x72, 0x10, 0x1f, 0x12, 0x16, 0x0a, 0x12, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x72, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x10, 0x20, 0x12, 0x10, + 0x0a, 0x0c, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x10, 0x21, + 0x12, 0x19, 0x0a, 0x15, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x10, 0x22, 0x12, 0x0d, 0x0a, 0x09, 0x63, + 0x6f, 0x64, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x10, 0x23, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x0c, 0x5a, 0x0a, 0x2e, 0x2f, 0x3b, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -499,20 +632,26 @@ func file_msg_proto_rawDescGZIP() []byte { var file_msg_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_msg_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_msg_proto_goTypes = []interface{}{ - (MsgType)(0), // 0: network.msg.type - (*HandshakePayload)(nil), // 1: network.handshake_payload - (*Msg)(nil), // 2: network.msg - (*ConnectRequest)(nil), // 3: network.connect_request - (*ConnectResponse)(nil), // 4: network.connect_response - (*Data)(nil), // 5: network.data - (*ShellResize)(nil), // 6: network.shell_resize - (*ShellData)(nil), // 7: network.shell_data - (*VncControl)(nil), // 8: network.vnc_control - (*VncImage)(nil), // 9: network.vnc_image - (*VncMouse)(nil), // 10: network.vnc_mouse - (*VncKeyboard)(nil), // 11: network.vnc_keyboard - (*VncScroll)(nil), // 12: network.vnc_scroll - (*VncClipboard)(nil), // 13: network.vnc_clipboard + (MsgType)(0), // 0: network.msg.type + (*HandshakePayload)(nil), // 1: network.handshake_payload + (*Msg)(nil), // 2: network.msg + (*ConnectRequest)(nil), // 3: network.connect_request + (*ConnectResponse)(nil), // 4: network.connect_response + (*Data)(nil), // 5: network.data + (*ShellResize)(nil), // 6: network.shell_resize + (*ShellData)(nil), // 7: network.shell_data + (*VncControl)(nil), // 8: network.vnc_control + (*VncImage)(nil), // 9: network.vnc_image + (*VncMouse)(nil), // 10: network.vnc_mouse + (*VncKeyboard)(nil), // 11: network.vnc_keyboard + (*VncScroll)(nil), // 12: network.vnc_scroll + (*VncClipboard)(nil), // 13: network.vnc_clipboard + (*CodeRequest)(nil), // 14: network.code_request + (*CodeResponseHeader)(nil), // 15: network.code_response_header + (*CodeResponseBody)(nil), // 16: network.code_response_body + (*CodeConnect)(nil), // 17: network.code_connect + (*CodeConnectResponse)(nil), // 18: network.code_connect_response + (*CodeData)(nil), // 19: network.code_data } var file_msg_proto_depIdxs = []int32{ 0, // 0: network.msg._type:type_name -> network.msg.type @@ -528,11 +667,17 @@ var file_msg_proto_depIdxs = []int32{ 11, // 10: network.msg.vkbd:type_name -> network.vnc_keyboard 12, // 11: network.msg.vscroll:type_name -> network.vnc_scroll 13, // 12: network.msg.vclipboard:type_name -> network.vnc_clipboard - 13, // [13:13] is the sub-list for method output_type - 13, // [13:13] is the sub-list for method input_type - 13, // [13:13] is the sub-list for extension type_name - 13, // [13:13] is the sub-list for extension extendee - 0, // [0:13] is the sub-list for field type_name + 14, // 13: network.msg.csreq:type_name -> network.code_request + 15, // 14: network.msg.csrep_hdr:type_name -> network.code_response_header + 16, // 15: network.msg.csrep_body:type_name -> network.code_response_body + 17, // 16: network.msg.csconn:type_name -> network.code_connect + 18, // 17: network.msg.csconn_rep:type_name -> network.code_connect_response + 19, // 18: network.msg.csdata:type_name -> network.code_data + 19, // [19:19] is the sub-list for method output_type + 19, // [19:19] is the sub-list for method input_type + 19, // [19:19] is the sub-list for extension type_name + 19, // [19:19] is the sub-list for extension extendee + 0, // [0:19] is the sub-list for field type_name } func init() { file_msg_proto_init() } @@ -544,6 +689,7 @@ func file_msg_proto_init() { file_forward_proto_init() file_shell_proto_init() file_vnc_proto_init() + file_code_proto_init() if !protoimpl.UnsafeEnabled { file_msg_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HandshakePayload); i { @@ -583,6 +729,12 @@ func file_msg_proto_init() { (*Msg_Vkbd)(nil), (*Msg_Vscroll)(nil), (*Msg_Vclipboard)(nil), + (*Msg_Csreq)(nil), + (*Msg_CsrepHdr)(nil), + (*Msg_CsrepBody)(nil), + (*Msg_Csconn)(nil), + (*Msg_CsconnRep)(nil), + (*Msg_Csdata)(nil), } type x struct{} out := protoimpl.TypeBuilder{ diff --git a/code/network/msg.proto b/code/network/msg.proto index 62107e9..22c9f4b 100644 --- a/code/network/msg.proto +++ b/code/network/msg.proto @@ -7,6 +7,7 @@ import "connect.proto"; import "forward.proto"; import "shell.proto"; import "vnc.proto"; +import "code.proto"; message handshake_payload { bytes enc = 1; @@ -32,6 +33,13 @@ message msg { vnc_cad = 24; // ctrl+alt+del vnc_scroll = 25; vnc_clipboard = 26; + // code-server + code_request = 30; + code_response_hdr = 31; + code_response_body = 32; + code_connect = 33; + code_connect_response = 34; + code_data = 35; } type _type = 1; string from = 2; @@ -52,5 +60,12 @@ message msg { vnc_keyboard vkbd = 33; vnc_scroll vscroll = 34; vnc_clipboard vclipboard = 35; + // code-server + code_request csreq = 40; + code_response_header csrep_hdr = 41; + code_response_body csrep_body = 42; + code_connect csconn = 43; + code_connect_response csconn_rep = 44; + code_data csdata = 45; } } \ No newline at end of file diff --git a/code/network/shell.pb.go b/code/network/shell.pb.go index ea25237..8878947 100644 --- a/code/network/shell.pb.go +++ b/code/network/shell.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 -// protoc v3.6.1 +// protoc-gen-go v1.28.0 +// protoc v3.21.2 // source: shell.proto package network diff --git a/code/network/vnc.pb.go b/code/network/vnc.pb.go index 706ce66..2835492 100644 --- a/code/network/vnc.pb.go +++ b/code/network/vnc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 -// protoc v3.6.1 +// protoc-gen-go v1.28.0 +// protoc v3.21.2 // source: vnc.proto package network diff --git a/code/server/main.go b/code/server/main.go index 13478d7..60775c9 100644 --- a/code/server/main.go +++ b/code/server/main.go @@ -123,7 +123,7 @@ func main() { switch *act { case "install": runtime.Assert(sv.Install()) - utils.BuildLogDir(cfg.LogDir, *user) + utils.BuildDir(cfg.LogDir, *user) case "uninstall": runtime.Assert(sv.Uninstall()) default: diff --git a/code/utils/utils.go b/code/utils/utils.go index e263e2e..555879f 100644 --- a/code/utils/utils.go +++ b/code/utils/utils.go @@ -8,8 +8,8 @@ import ( "github.com/lwch/runtime" ) -// BuildLogDir mkdir for log and chown -func BuildLogDir(dir, u string) { +// BuildDir mkdir and chown +func BuildDir(dir, u string) { runtime.Assert(os.MkdirAll(dir, 0755)) if len(u) > 0 { us, err := user.Lookup(u) diff --git a/conf/common.yaml b/conf/common.yaml index 40cb166..f93c3d3 100644 --- a/conf/common.yaml +++ b/conf/common.yaml @@ -5,4 +5,5 @@ link: log: dir: ./logs # 路径,相对于可执行文件所在目录的相对路径 size: 50M # 单个文件大小 - rotate: 7 # 保留数量 \ No newline at end of file + rotate: 7 # 保留数量 +codedir: ./code # code-server配置保存路径 \ No newline at end of file diff --git a/conf/rule.d/code-server.yaml b/conf/rule.d/code-server.yaml new file mode 100644 index 0000000..4afd38d --- /dev/null +++ b/conf/rule.d/code-server.yaml @@ -0,0 +1,10 @@ +# 1. 需先在受控端主机上安装code-server并添加到PATH环境变量 +# 下载地址:https://github.com/coder/code-server/releases +# 2. code-server默认使用OpenVSX扩展商店 +# 若需切换到微软扩展商店可通过配置系统环境变量EXTENSIONS_GALLERY进行切换 +# 详见说明:https://github.com/coder/code-server/blob/10f57bac65f9aa5938df4e495da39c608fbf7798/docs/FAQ.md#how-do-i-use-my-own-extensions-marketplace +- name: code-server # 链路名称 + target: remote # 目标客户端ID + type: code-server # code-server + local_addr: 0.0.0.0 # 本地监听地址 + local_port: 8000 # 本地监听端口号 \ No newline at end of file diff --git a/conf/rule.d/vnc.yaml b/conf/rule.d/vnc.yaml index 4ac49be..e7ff30f 100644 --- a/conf/rule.d/vnc.yaml +++ b/conf/rule.d/vnc.yaml @@ -1,3 +1,4 @@ +# 目前不支持arm架构的windows和linux操作系统 - name: vnc # 链路名称 target: remote # 目标客户端ID type: vnc # web vnc diff --git a/contrib/build/Dockerfile b/contrib/build/Dockerfile index ba2eda2..2f07796 100644 --- a/contrib/build/Dockerfile +++ b/contrib/build/Dockerfile @@ -1,6 +1,6 @@ ARG APT_MIRROR=mirrors.ustc.edu.cn ARG LIBPNG_VERSION=1.6.37 -ARG GO_VERSION=1.18.3 +ARG GO_VERSION=1.18.4 ARG GO_PROXY=https://goproxy.cn,direct FROM lwch/darwin-crosscompiler:11.3 diff --git a/contrib/build/build b/contrib/build/build index 768f39a..b344055 100755 --- a/contrib/build/build +++ b/contrib/build/build @@ -21,10 +21,10 @@ go run contrib/bindata/main.go -pkg shell -o code/client/rule/shell/assets.go \ -prefix html/shell "$@" html/shell/... go run contrib/bindata/main.go -pkg vnc -o code/client/rule/vnc/assets.go \ -prefix html/vnc "$@" html/vnc/... +go run contrib/bindata/main.go -pkg code -o code/client/rule/code/assets.go \ + -prefix html/code "$@" html/code/... go run contrib/bindata/main.go -pkg dashboard -o code/client/dashboard/assets.go \ -prefix html/dashboard "$@" html/dashboard/... go build -ldflags "$LDFLAGS" -o bin/np-svr code/server/*.go -# CC=x86_64-w64-mingw32-gcc \ -# CXX=x86_64-w64-mingw32-g++ \ go build -ldflags "$LDFLAGS" -tags "$TAGS" -o bin/np-cli code/client/*.go \ No newline at end of file diff --git a/contrib/build/build_all.go b/contrib/build/build_all.go index 833e860..0aed974 100644 --- a/contrib/build/build_all.go +++ b/contrib/build/build_all.go @@ -136,6 +136,15 @@ func bindata() { cmd.Stderr = os.Stderr runtime.Assert(cmd.Run()) + cmd = exec.Command("go", "run", "contrib/bindata/main.go", + "-pkg", "code", + "-o", "code/client/rule/code/assets.go", + "-prefix", "html/code", + "html/code/...") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + runtime.Assert(cmd.Run()) + cmd = exec.Command("go", "run", "contrib/bindata/main.go", "-pkg", "dashboard", "-o", "code/client/dashboard/assets.go", diff --git a/docs/imgs/code_server.png b/docs/imgs/code_server.png new file mode 100755 index 0000000..6359eaa Binary files /dev/null and b/docs/imgs/code_server.png differ diff --git a/docs/imgs/shell.gif b/docs/imgs/shell.gif new file mode 100755 index 0000000..2343195 Binary files /dev/null and b/docs/imgs/shell.gif differ diff --git a/docs/imgs/vnc.gif b/docs/imgs/vnc.gif new file mode 100755 index 0000000..a8d05b6 Binary files /dev/null and b/docs/imgs/vnc.gif differ diff --git a/go.mod b/go.mod index 02db657..4527b88 100644 --- a/go.mod +++ b/go.mod @@ -13,10 +13,11 @@ require ( github.com/lwch/rdesktop v1.1.1 github.com/lwch/runtime v0.0.0-20190520054850-8c97e19e0c6d github.com/lwch/yaml v0.0.0-20220711084242-14c4f5845abe + golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 // indirect golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect - golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d + golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 golang.org/x/text v0.3.7 - golang.org/x/tools v0.1.11 // indirect + golang.org/x/tools v0.1.12 // indirect golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect google.golang.org/protobuf v1.28.0 ) diff --git a/go.sum b/go.sum index 67ae0fd..77b3178 100644 --- a/go.sum +++ b/go.sum @@ -24,11 +24,13 @@ github.com/lwch/runtime v0.0.0-20190520054850-8c97e19e0c6d/go.mod h1:uEt0zu1MDC7 github.com/lwch/yaml v0.0.0-20220711084242-14c4f5845abe h1:PJ1zNqqiHW2q480seGnr9G8/Oke/JxF4HscxEuV+a6Y= github.com/lwch/yaml v0.0.0-20220711084242-14c4f5845abe/go.mod h1:n4iNSR61Lth9WtFYMYdw/eo92v/AZpz/ml9FuYJ0vd0= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/image v0.0.0-20220617043117-41969df76e82 h1:KpZB5pUSBvrHltNEdK/tw0xlPeD13M6M6aGP32gKqiw= golang.org/x/image v0.0.0-20220617043117-41969df76e82/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY= +golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 h1:/eM0PCrQI2xd471rI+snWuu251/+/jpBpZqir2mPdnU= +golang.org/x/image v0.0.0-20220722155232-062f8c9fd539/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -40,8 +42,10 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -49,9 +53,12 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d h1:/m5NbqQelATgoSPVC2Z23sR4kVNokFwDDyWh/3rGY+I= -golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -63,8 +70,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= -golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY= -golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/html/code/common.js b/html/code/common.js new file mode 120000 index 0000000..d91842a --- /dev/null +++ b/html/code/common.js @@ -0,0 +1 @@ +../js/common.js \ No newline at end of file diff --git a/html/code/fontawesome b/html/code/fontawesome new file mode 120000 index 0000000..f342816 --- /dev/null +++ b/html/code/fontawesome @@ -0,0 +1 @@ +../fontawesome-free-5.15.4 \ No newline at end of file diff --git a/html/code/index.css b/html/code/index.css new file mode 100644 index 0000000..2f60618 --- /dev/null +++ b/html/code/index.css @@ -0,0 +1,41 @@ +html { + height: 100%; +} +body { + margin: 0 !important; + padding: 0 !important; + height: 100% !important; +} +.container-fluid { + height: 100%; + overflow-y: hidden; +} +#header { + padding-top: 4px; +} +#fullscreen { + float: right; + margin-right: 5px; +} +#info { + float: right; + margin-right: 5px; +} +#closed { + color: red; + float: right; + margin-right: 5px; + font-weight: bold; +} +label, span { + line-height: 38px; +} +#fullscreen { + cursor: pointer; + font-size: 38px; +} +#code { + width: 100%; + height: calc(100% - 42px); + border: none; +} \ No newline at end of file diff --git a/html/code/index.html b/html/code/index.html new file mode 100644 index 0000000..bf96219 --- /dev/null +++ b/html/code/index.html @@ -0,0 +1,25 @@ + + + + + + code - [{{.Name}}] + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/html/code/index.js b/html/code/index.js new file mode 100644 index 0000000..58f2bd2 --- /dev/null +++ b/html/code/index.js @@ -0,0 +1,47 @@ +var page = { + init: function() { + $('#fullscreen').click(page.fullscreen); + $('#code').on('load', function() { + var qry = $('#code')[0].contentWindow.location.search; + var params = new URLSearchParams(qry); + page.id = params.get('natpass_connection_id'); + }); + page.connect(); + setInterval(page.update_info, page.secs*1000); + }, + connect: function() { + $.get('/new', function(ret) { + page.name = ret; + $('#code').attr('src', `/forward/${page.name}/`); + }); + }, + update_info: function() { + if (!page.id) { + return; + } + $.get('/info?id='+page.id, function(ret) { + var send_bytes = ret.send_bytes - page.send; + var recv_bytes = ret.recv_bytes - page.recv; + if (send_bytes < 0) { + send_bytes = 0; + } + if (recv_bytes < 0) { + recv_bytes = 0; + } + page.send = ret.send_bytes; + page.recv = ret.recv_bytes; + var str = 'send: '+humanize.bytes(send_bytes/page.secs)+'/s, '+ + 'recv: '+humanize.bytes(recv_bytes/page.secs)+'/s'; + $('#info').text(str); + }); + }, + fullscreen: function() { + $('#code')[0].requestFullscreen(); + }, + id: '', + name: '', + secs: 2, + send: 0, + recv: 0 +}; +$(document).ready(page.init); \ No newline at end of file diff --git a/html/code/jquery b/html/code/jquery new file mode 120000 index 0000000..03e6035 --- /dev/null +++ b/html/code/jquery @@ -0,0 +1 @@ +../jquery \ No newline at end of file diff --git a/html/dashboard/js/index.js b/html/dashboard/js/index.js index d5211aa..3ebf503 100644 --- a/html/dashboard/js/index.js +++ b/html/dashboard/js/index.js @@ -36,13 +36,9 @@ var page = { }); var op = ''; switch (rule.type) { - case 'reverse': - op = ` - http - https`; - break; case 'shell': case 'vnc': + case 'code-server': op = `连接`; break; } diff --git a/html/dashboard/js/terminal.js b/html/dashboard/js/terminal.js index 7093cd4..9896317 100644 --- a/html/dashboard/js/terminal.js +++ b/html/dashboard/js/terminal.js @@ -15,7 +15,8 @@ var page = { $('#terms').empty(); $.each(ret, function(_, rule) { if (rule.type != 'shell' && - rule.type != 'vnc') { + rule.type != 'vnc' && + rule.type != 'code-server') { return; } $('#terms').append($(``)); diff --git a/test/code-server-forward/main.go b/test/code-server-forward/main.go new file mode 100644 index 0000000..116f9f0 --- /dev/null +++ b/test/code-server-forward/main.go @@ -0,0 +1,146 @@ +package main + +import ( + "fmt" + "io" + "net" + "net/http" + "os" + "os/exec" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/gorilla/websocket" + "github.com/lwch/runtime" +) + +var cli = &http.Client{ + Transport: &http.Transport{ + Dial: func(network, addr string) (net.Conn, error) { + return net.Dial("unix", "./code-server/code-server.sock") + }, + }, +} +var upgrader = websocket.Upgrader{} +var dialer = websocket.Dialer{ + NetDial: func(network, addr string) (net.Conn, error) { + return net.Dial("unix", "./code-server/code-server.sock") + }, +} + +func main() { + dir := "/home/lwch/src/natpass/code-server" + exec := exec.Command("code-server", "--disable-update-check", + "--auth", "none", + "--socket", filepath.Join(dir, "code-server.sock"), + "--user-data-dir", filepath.Join(dir, "data"), + "--extensions-dir", filepath.Join(dir, "extensions"), ".") + exec.Stdout = os.Stdout + exec.Stderr = os.Stderr + runtime.Assert(exec.Start()) + time.Sleep(time.Second) + + go exec.Wait() + + conn, err := net.Dial("unix", "./code-server/code-server.sock") + runtime.Assert(err) + conn.Close() + + http.HandleFunc("/", next) + http.ListenAndServe(":8001", nil) +} + +func normal(w http.ResponseWriter, r *http.Request) { + u := r.URL + u.Scheme = "http" + u.Host = "unix" + req, err := http.NewRequest(r.Method, u.String(), r.Body) + runtime.Assert(err) + + for key, values := range r.Header { + for _, v := range values { + req.Header.Add(key, v) + } + } + + rep, err := cli.Do(req) + runtime.Assert(err) + defer rep.Body.Close() + + for key, values := range rep.Header { + for _, v := range values { + w.Header().Add(key, v) + } + } + + w.WriteHeader(rep.StatusCode) + + _, err = io.Copy(w, rep.Body) + runtime.Assert(err) +} + +func ws(w http.ResponseWriter, r *http.Request) { + u := r.URL + u.Scheme = "ws" + u.Host = "unix" + + hdr := make(http.Header) + for key, values := range r.Header { + if strings.HasPrefix(key, "Sec-") { + continue + } + for _, value := range values { + hdr.Add(key, value) + } + } + + hdr.Del("Connection") + hdr.Del("Upgrade") + + remote, resp, err := dialer.Dial(u.String(), hdr) + runtime.Assert(err) + defer resp.Body.Close() + defer remote.Close() + + local, err := upgrader.Upgrade(w, r, nil) + runtime.Assert(err) + defer local.Close() + + cp := func(wg *sync.WaitGroup, dst, src *websocket.Conn) { + defer wg.Done() + defer dst.Close() + defer src.Close() + for { + t, data, err := src.ReadMessage() + if err != nil { + fmt.Println(err) + return + } + err = dst.WriteMessage(t, data) + if err != nil { + fmt.Println(err) + return + } + } + } + + var wg sync.WaitGroup + + wg.Add(2) + go cp(&wg, local, remote) + go cp(&wg, remote, local) + + wg.Wait() +} + +func next(w http.ResponseWriter, r *http.Request) { + upgrade := r.Header.Get("Connection") + + if upgrade == "Upgrade" { + ws(w, r) + return + } + normal(w, r) +}