complete code, need debug
This commit is contained in:
parent
edd12ff347
commit
c8b2e99003
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
// 使用 IntelliSense 了解相关属性。
|
||||
// 悬停以查看现有属性的描述。
|
||||
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Package client",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}/tunnelclient",
|
||||
"args":["--target-session", "HNCZhU8G86HSfTZv", "--local-port", "2222"]
|
||||
},
|
||||
{
|
||||
"name": "Launch Package",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}/tunnelserver/main.go"
|
||||
}
|
||||
|
||||
]
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"git.pyer.club/kingecg/goemitter"
|
||||
"git.pyer.club/kingecg/gotunnelserver/util"
|
||||
ws "github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type DataEndPoint struct {
|
||||
Host string
|
||||
Port int
|
||||
|
||||
cmdSession *CommandClient
|
||||
dataSession string
|
||||
wsConn *ws.Conn
|
||||
conns map[int32]*DataConn
|
||||
}
|
||||
|
||||
func (d *DataEndPoint) Close() {
|
||||
d.wsConn.Close()
|
||||
}
|
||||
|
||||
func (d *DataEndPoint) onDataConnClose(conn *DataConn) {
|
||||
delete(d.conns, conn.id)
|
||||
}
|
||||
|
||||
func (d *DataEndPoint) Connect() {
|
||||
conn, _, err := ws.DefaultDialer.Dial(d.cmdSession.Address+"/ws/pipe/"+d.dataSession, map[string][]string{
|
||||
"Authorization": {d.cmdSession.makeAuthHeader()},
|
||||
"Session": {d.cmdSession.SessionId},
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
d.wsConn = conn
|
||||
for {
|
||||
// d.wsConn.SetReadDeadline(time.Now().Add(time.Minute * 5))
|
||||
_, data, err := d.wsConn.ReadMessage()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
var packet Packet
|
||||
err = packet.BinaryUnmarshaler(data)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
conn, ok := d.conns[packet.id]
|
||||
if !ok {
|
||||
tcpconn, err := net.Dial("tcp", d.Host+":"+strconv.Itoa(d.Port))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
conn = NewDataConnection(packet.id, &tcpconn, d)
|
||||
conn.Start()
|
||||
conn.Once("close", func(args ...interface{}) {
|
||||
d.onDataConnClose(conn)
|
||||
})
|
||||
}
|
||||
|
||||
conn.Write(packet)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DataEndPoint) Listen() {
|
||||
// listen and accept connection at port
|
||||
listener, err := net.Listen("tcp", d.Host+":"+strconv.Itoa(d.Port))
|
||||
if err != nil {
|
||||
log.Println(err, d)
|
||||
panic(err)
|
||||
}
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dconn := NewDataConnection(0, &conn, d)
|
||||
d.conns[dconn.id] = dconn
|
||||
dconn.Start()
|
||||
dconn.Once("close", func(args ...interface{}) {
|
||||
d.onDataConnClose(dconn)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DataEndPoint) Write(p []byte) (n int, err error) {
|
||||
return len(p), d.wsConn.WriteMessage(ws.BinaryMessage, p)
|
||||
}
|
||||
|
||||
func NewDataEndPoint(cmdSession *CommandClient, dataSession string) *DataEndPoint {
|
||||
return &DataEndPoint{
|
||||
cmdSession: cmdSession,
|
||||
dataSession: dataSession,
|
||||
conns: make(map[int32]*DataConn),
|
||||
}
|
||||
}
|
||||
|
||||
type DataConn struct {
|
||||
id int32
|
||||
conn *net.Conn
|
||||
out io.Writer
|
||||
*goemitter.EventEmitter
|
||||
}
|
||||
|
||||
type Packet struct {
|
||||
id int32
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (p *Packet) MarshalBinary() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, binary.LittleEndian, p)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (p *Packet) BinaryUnmarshaler(data []byte) error {
|
||||
buf := bytes.NewReader(data)
|
||||
return binary.Read(buf, binary.LittleEndian, p)
|
||||
}
|
||||
func (d *DataConn) Close() {
|
||||
(*d.conn).Close()
|
||||
}
|
||||
|
||||
func (d *DataConn) Write(p Packet) (n int, err error) {
|
||||
return (*d.conn).Write(p.data)
|
||||
}
|
||||
|
||||
func (d *DataConn) Start() {
|
||||
go func() {
|
||||
for {
|
||||
buf := make([]byte, 1024)
|
||||
n, err := (*d.conn).Read(buf)
|
||||
if err != nil {
|
||||
// panic(err)
|
||||
break
|
||||
}
|
||||
packet := Packet{
|
||||
id: d.id,
|
||||
data: buf[:n],
|
||||
}
|
||||
data, err := packet.MarshalBinary()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
d.out.Write(data)
|
||||
}
|
||||
d.Emit("close")
|
||||
}()
|
||||
|
||||
}
|
||||
func NewDataConnection(id int32, con *net.Conn, writer io.Writer) *DataConn {
|
||||
cid := id
|
||||
if cid == 0 {
|
||||
cid = util.GenRandomInt()
|
||||
}
|
||||
return &DataConn{
|
||||
id: cid,
|
||||
conn: con,
|
||||
out: writer,
|
||||
EventEmitter: goemitter.NewEmitter(),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"git.pyer.club/kingecg/goemitter"
|
||||
"git.pyer.club/kingecg/gotunnelserver/server"
|
||||
"git.pyer.club/kingecg/gotunnelserver/util"
|
||||
ws "github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type ClientConfig struct {
|
||||
Username string
|
||||
Salt string
|
||||
Address string
|
||||
}
|
||||
|
||||
type CommandClient struct {
|
||||
*ws.Conn
|
||||
*goemitter.EventEmitter
|
||||
*ClientConfig
|
||||
path string
|
||||
SessionId string
|
||||
}
|
||||
|
||||
func (c *CommandClient) Close() {
|
||||
if c.Conn != nil {
|
||||
c.Conn.Close()
|
||||
c.Conn = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CommandClient) Send(cmd *server.Command) (err error) {
|
||||
return c.WriteJSON(cmd)
|
||||
}
|
||||
func (c *CommandClient) Start() {
|
||||
defer c.Close()
|
||||
// cpath:="/client"
|
||||
// if c.IsAgent {
|
||||
// cpath = "/agent"
|
||||
// }
|
||||
authHeader := c.makeAuthHeader()
|
||||
conn, _, err := ws.DefaultDialer.Dial(c.Address+c.path, map[string][]string{
|
||||
"Authorization": {authHeader},
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c.Conn = conn
|
||||
c.On(util.CmdTypeMap[util.NewSession], func(args ...interface{}) {
|
||||
playload, _ := args[0].(map[string]string)
|
||||
sessionId := playload["sessionId"]
|
||||
log.Println("new session:", sessionId)
|
||||
c.SessionId = sessionId
|
||||
})
|
||||
for {
|
||||
var cmd server.Command
|
||||
err := c.ReadJSON(&cmd)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
eventLabel := util.CmdTypeMap[cmd.Type]
|
||||
log.Println("cmd:", cmd.Type, eventLabel)
|
||||
c.Emit(eventLabel, cmd.Payload)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CommandClient) makeAuthHeader() string {
|
||||
a := &util.AuthEntity{
|
||||
Entity: util.Entity{
|
||||
Username: c.Username,
|
||||
},
|
||||
Time: time.Now().Unix(),
|
||||
}
|
||||
a.Authtoken = util.GenAuthToken(a, c.Salt)
|
||||
str, err := json.Marshal(a)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(str)
|
||||
}
|
||||
|
||||
func NewClient(conf *ClientConfig) *CommandClient {
|
||||
return &CommandClient{
|
||||
ClientConfig: conf,
|
||||
EventEmitter: goemitter.NewEmitter(),
|
||||
path: "/ws/client",
|
||||
}
|
||||
}
|
||||
|
||||
func NewAgent(conf *ClientConfig) *CommandClient {
|
||||
return &CommandClient{
|
||||
ClientConfig: conf,
|
||||
EventEmitter: goemitter.NewEmitter(),
|
||||
path: "/ws/agent",
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestClient(t *testing.T) {
|
||||
clientConfig := &ClientConfig{
|
||||
Address: "ws://localhost:8080",
|
||||
Salt: "",
|
||||
Username: "test",
|
||||
}
|
||||
client := NewClient(clientConfig)
|
||||
client.Start()
|
||||
}
|
1
go.mod
1
go.mod
|
@ -3,6 +3,7 @@ module git.pyer.club/kingecg/gotunnelserver
|
|||
go 1.23.1
|
||||
|
||||
require (
|
||||
git.pyer.club/kingecg/command v0.0.0-20241115071005-9f26d1cf2992 // indirect
|
||||
git.pyer.club/kingecg/goemitter v0.0.0-20240919084107-533c3d1be082 // indirect
|
||||
git.pyer.club/kingecg/gologger v1.0.5 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
|
|
6
go.sum
6
go.sum
|
@ -1,3 +1,9 @@
|
|||
git.pyer.club/kingecg/command v0.0.0-20241024110012-525a2ad9662d h1:CIcY8L5FcGy1uO+GvdMeidjdfYSxwTEEEvt3WXBOQAU=
|
||||
git.pyer.club/kingecg/command v0.0.0-20241024110012-525a2ad9662d/go.mod h1:lnSzW19xOIlUwlewxHH0R4SIDO+a4++USjmMjo/jZB0=
|
||||
git.pyer.club/kingecg/command v0.0.0-20241115051719-6fd5111300dc h1:PVvfibld+xRlWIbBlprF0AaIuTpF5QeY3CqthCQP8lg=
|
||||
git.pyer.club/kingecg/command v0.0.0-20241115051719-6fd5111300dc/go.mod h1:7j+/UU5URp7UkTbNtYDvZY3cFywuTRys1TmP3HbNX3A=
|
||||
git.pyer.club/kingecg/command v0.0.0-20241115071005-9f26d1cf2992 h1:QurLvSlNSU2TjTVe9h9+N9QVZEvSXuz9POebExqhfOo=
|
||||
git.pyer.club/kingecg/command v0.0.0-20241115071005-9f26d1cf2992/go.mod h1:7j+/UU5URp7UkTbNtYDvZY3cFywuTRys1TmP3HbNX3A=
|
||||
git.pyer.club/kingecg/goemitter v0.0.0-20240919084107-533c3d1be082 h1:U7Jbet3zObT2qPJ2g408Z9OUvR6phQyHOoHeidM5zUg=
|
||||
git.pyer.club/kingecg/goemitter v0.0.0-20240919084107-533c3d1be082/go.mod h1:2jbknDqoWH41M3MQ9pQZDKBiNtDmNgPcM3XfkE9YkbQ=
|
||||
git.pyer.club/kingecg/gologger v1.0.5 h1:L/N/bleGHhEiaBYBf9U1z2ni0HfhaU71pk8ik/D11oo=
|
||||
|
|
|
@ -3,7 +3,6 @@ package server
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"git.pyer.club/kingecg/gotunnelserver/util"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
|
@ -11,11 +10,6 @@ func (s *Server) HandleAgent(c *websocket.Conn, r *http.Request) {
|
|||
agentSession := NewSession(c)
|
||||
agentSession.Start()
|
||||
s.agentSession[agentSession.Id] = agentSession
|
||||
command := &Command{
|
||||
Type: util.NewSession,
|
||||
Payload: map[string]string{
|
||||
"id": agentSession.Id,
|
||||
},
|
||||
}
|
||||
command := NcmdSession(agentSession.Id)
|
||||
agentSession.Send(command)
|
||||
}
|
||||
|
|
|
@ -25,7 +25,9 @@ func (s *Server) HandleClient(conn *websocket.Conn, r *http.Request) {
|
|||
clientSession.Send(NewErrorResponse("target session not found", command))
|
||||
return
|
||||
}
|
||||
command := NcmdConnectionInited(clientSession.Id)
|
||||
spipe := NewPipe(clientSession.Id, targetSessionId)
|
||||
s.pipes[spipe.Id] = spipe
|
||||
command := NcmdConnectionInited(spipe.Id)
|
||||
|
||||
for k, v := range orgPlayload {
|
||||
command.Payload[k] = v
|
||||
|
|
|
@ -3,6 +3,7 @@ package server
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"git.pyer.club/kingecg/gotunnelserver/util"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
|
@ -23,6 +24,14 @@ func (p *Pipe) Start() {
|
|||
p.src.Close()
|
||||
p.dst.Close()
|
||||
}
|
||||
func NewPipe(src, dst string) *Pipe {
|
||||
return &Pipe{
|
||||
Id: util.GenRandomstring(16),
|
||||
Src: src,
|
||||
Dst: dst,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (p *Pipe) forward(src, dst *websocket.Conn) {
|
||||
for {
|
||||
|
@ -75,7 +84,7 @@ func (s *Server) HandlePipe(conn *websocket.Conn, r *http.Request) {
|
|||
}
|
||||
|
||||
if pipe.src != nil && pipe.dst != nil {
|
||||
pipe.Start()
|
||||
go pipe.Start()
|
||||
clientCmdSession := s.findSession(pipe.Src)
|
||||
clientCmdSession.Send(NcmdConnectionReady(pipe.Id)) // info src endpoint ready and can setup proxy listener
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ func (s *Server) registHandler() {
|
|||
s.mux = http.NewServeMux()
|
||||
}
|
||||
s.mux.HandleFunc("/hello", s.HandleHello)
|
||||
s.mux.HandleFunc("/ws", s.HandleWs)
|
||||
s.mux.HandleFunc("/ws/", s.HandleWs)
|
||||
}
|
||||
func (s *Server) Start() {
|
||||
addr := s.Config.Host + ":" + s.Config.Port
|
||||
|
|
|
@ -92,7 +92,7 @@ func NewErrorResponse(err string, cmd *Command) *Command {
|
|||
}
|
||||
|
||||
func NcmdConnectionInited(sessionId string) *Command {
|
||||
return NewCommand(util.ConnectionReady, map[string]string{"sessionId": sessionId})
|
||||
return NewCommand(util.ConnectInited, map[string]string{"sessionId": sessionId})
|
||||
}
|
||||
|
||||
func NcmdConnectionReady(sessionId string) *Command {
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"git.pyer.club/kingecg/command"
|
||||
"git.pyer.club/kingecg/gotunnelserver/client"
|
||||
"git.pyer.club/kingecg/gotunnelserver/util"
|
||||
)
|
||||
|
||||
type TunnelAgent struct {
|
||||
Username string `flag_default:"tcclient" flag_usage:"username for tunnel server"`
|
||||
Salt string `flag_default:"" flag_usage:"salt for tunnel server"`
|
||||
Address string `flag_default:"ws://127.0.0.1:8080" flag_usage:"address for tunnel server"`
|
||||
}
|
||||
|
||||
var (
|
||||
cmdClient *client.CommandClient
|
||||
dataEp *client.DataEndPoint
|
||||
)
|
||||
|
||||
func main() {
|
||||
cargs := new(TunnelAgent)
|
||||
v, _ := command.NewFVSet(cargs)
|
||||
err := command.Parse(os.Args[1:], v)
|
||||
if err != nil {
|
||||
os.Exit(0)
|
||||
return
|
||||
}
|
||||
clientConfig := &client.ClientConfig{
|
||||
Username: cargs.Username,
|
||||
Salt: cargs.Salt,
|
||||
Address: cargs.Address,
|
||||
}
|
||||
cmdClient = client.NewAgent(clientConfig)
|
||||
cmdClient.On(util.CmdTypeMap[util.ConnectInited], func(args ...interface{}) {
|
||||
playload, _ := args[0].(map[string]string)
|
||||
sessionId := playload["sessionId"]
|
||||
dataEp = client.NewDataEndPoint(cmdClient, sessionId)
|
||||
dataEp.Host = playload["host"]
|
||||
dataEp.Port, _ = strconv.Atoi(playload["port"])
|
||||
log.Println("connect inited:", sessionId, dataEp.Host, dataEp.Port)
|
||||
go dataEp.Connect()
|
||||
})
|
||||
cmdClient.On(util.CmdTypeMap[util.ErrorCmd], func(args ...interface{}) {
|
||||
playload, _ := args[0].(map[string]string)
|
||||
log.Println("error:", playload["error"])
|
||||
os.Exit(1)
|
||||
})
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Println(r)
|
||||
return
|
||||
}
|
||||
}()
|
||||
cmdClient.Start()
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"git.pyer.club/kingecg/command"
|
||||
"git.pyer.club/kingecg/gotunnelserver/client"
|
||||
"git.pyer.club/kingecg/gotunnelserver/server"
|
||||
"git.pyer.club/kingecg/gotunnelserver/util"
|
||||
)
|
||||
|
||||
type TunnelClient struct {
|
||||
TargetSession string `flag_default:"" flag_usage:"target session id"`
|
||||
Username string `flag_default:"tcclient" flag_usage:"username for tunnel server"`
|
||||
Salt string `flag_default:"" flag_usage:"salt for tunnel server"`
|
||||
Address string `flag_default:"ws://127.0.0.1:8080" flag_usage:"address for tunnel server"`
|
||||
Host string `flag_default:"127.0.0.1" flag_usage:"host to proxy to"`
|
||||
Port int `flag_default:"22" flag_usage:"port to proxy to"`
|
||||
LocalPort int `flag_default:"0" flag_usage:"local port to accept connection from"`
|
||||
LocalHost string `flag_default:"127.0.0.1" flag_usage:"local host to accept connection from"`
|
||||
}
|
||||
|
||||
var (
|
||||
cmdClient *client.CommandClient
|
||||
dataEp *client.DataEndPoint
|
||||
)
|
||||
|
||||
func main() {
|
||||
cargs := new(TunnelClient)
|
||||
v, _ := command.NewFVSet(cargs)
|
||||
err := command.Parse(os.Args[1:], v)
|
||||
if err != nil {
|
||||
os.Exit(0)
|
||||
return
|
||||
}
|
||||
|
||||
if cargs.TargetSession == "" {
|
||||
log.Fatal("Must set target session to connect to")
|
||||
return
|
||||
}
|
||||
|
||||
clientConfig := &client.ClientConfig{
|
||||
Username: cargs.Username,
|
||||
Salt: cargs.Salt,
|
||||
Address: cargs.Address,
|
||||
}
|
||||
cmdClient = client.NewClient(clientConfig)
|
||||
cmdClient.Once(util.CmdTypeMap[util.ConnectInited], func(args ...interface{}) {
|
||||
playload, _ := args[0].(map[string]string)
|
||||
sessionId := playload["sessionId"]
|
||||
dataEp = client.NewDataEndPoint(cmdClient, sessionId)
|
||||
dataEp.Host = cargs.LocalHost
|
||||
dataEp.Port = cargs.LocalPort
|
||||
go dataEp.Connect()
|
||||
})
|
||||
cmdClient.On(util.CmdTypeMap[util.NewSession], func(args ...interface{}) {
|
||||
cmdClient.Send(&server.Command{
|
||||
Type: util.NewConnection,
|
||||
Payload: map[string]string{
|
||||
"target": cargs.TargetSession,
|
||||
"host": cargs.Host,
|
||||
"port": strconv.Itoa(cargs.Port),
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
cmdClient.Once(util.CmdTypeMap[util.ConnectionReady], func(args ...interface{}) {
|
||||
go dataEp.Listen()
|
||||
log.Println("connection ready")
|
||||
})
|
||||
|
||||
cmdClient.On(util.CmdTypeMap[util.ErrorCmd], func(args ...interface{}) {
|
||||
playload, _ := args[0].(map[string]string)
|
||||
log.Println("error:", playload["error"])
|
||||
os.Exit(1)
|
||||
})
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Println(r)
|
||||
return
|
||||
}
|
||||
}()
|
||||
cmdClient.Start()
|
||||
}
|
Binary file not shown.
|
@ -7,3 +7,13 @@ const (
|
|||
ConnectionReady
|
||||
ErrorCmd
|
||||
)
|
||||
|
||||
var (
|
||||
CmdTypeMap = map[int]string{
|
||||
NewSession: "new_session",
|
||||
NewConnection: "new_connection",
|
||||
ConnectInited: "connect_inited",
|
||||
ConnectionReady: "connection_ready",
|
||||
ErrorCmd: "error",
|
||||
}
|
||||
)
|
||||
|
|
|
@ -57,3 +57,8 @@ func GenRandomstring(n int) string {
|
|||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func GenRandomInt() int32 {
|
||||
// rand.Seed(time.Now().UnixNano())
|
||||
return int32(rand.Intn(65534) + 1)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue