complete code, need debug

This commit is contained in:
程广 2024-11-15 16:49:01 +08:00
parent edd12ff347
commit c8b2e99003
16 changed files with 492 additions and 11 deletions

24
.vscode/launch.json vendored Normal file
View File

@ -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"
}
]
}

169
client/dataconn.go Normal file
View File

@ -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(),
}
}

100
client/main.go Normal file
View File

@ -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",
}
}

15
client/main_test.go Normal file
View File

@ -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
View File

@ -3,6 +3,7 @@ module git.pyer.club/kingecg/gotunnelserver
go 1.23.1 go 1.23.1
require ( 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/goemitter v0.0.0-20240919084107-533c3d1be082 // indirect
git.pyer.club/kingecg/gologger v1.0.5 // indirect git.pyer.club/kingecg/gologger v1.0.5 // indirect
github.com/gorilla/websocket v1.5.3 // indirect github.com/gorilla/websocket v1.5.3 // indirect

6
go.sum
View File

@ -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 h1:U7Jbet3zObT2qPJ2g408Z9OUvR6phQyHOoHeidM5zUg=
git.pyer.club/kingecg/goemitter v0.0.0-20240919084107-533c3d1be082/go.mod h1:2jbknDqoWH41M3MQ9pQZDKBiNtDmNgPcM3XfkE9YkbQ= 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= git.pyer.club/kingecg/gologger v1.0.5 h1:L/N/bleGHhEiaBYBf9U1z2ni0HfhaU71pk8ik/D11oo=

View File

@ -3,7 +3,6 @@ package server
import ( import (
"net/http" "net/http"
"git.pyer.club/kingecg/gotunnelserver/util"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
) )
@ -11,11 +10,6 @@ func (s *Server) HandleAgent(c *websocket.Conn, r *http.Request) {
agentSession := NewSession(c) agentSession := NewSession(c)
agentSession.Start() agentSession.Start()
s.agentSession[agentSession.Id] = agentSession s.agentSession[agentSession.Id] = agentSession
command := &Command{ command := NcmdSession(agentSession.Id)
Type: util.NewSession,
Payload: map[string]string{
"id": agentSession.Id,
},
}
agentSession.Send(command) agentSession.Send(command)
} }

View File

@ -25,7 +25,9 @@ func (s *Server) HandleClient(conn *websocket.Conn, r *http.Request) {
clientSession.Send(NewErrorResponse("target session not found", command)) clientSession.Send(NewErrorResponse("target session not found", command))
return return
} }
command := NcmdConnectionInited(clientSession.Id) spipe := NewPipe(clientSession.Id, targetSessionId)
s.pipes[spipe.Id] = spipe
command := NcmdConnectionInited(spipe.Id)
for k, v := range orgPlayload { for k, v := range orgPlayload {
command.Payload[k] = v command.Payload[k] = v

View File

@ -3,6 +3,7 @@ package server
import ( import (
"net/http" "net/http"
"git.pyer.club/kingecg/gotunnelserver/util"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
) )
@ -23,6 +24,14 @@ func (p *Pipe) Start() {
p.src.Close() p.src.Close()
p.dst.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) { func (p *Pipe) forward(src, dst *websocket.Conn) {
for { for {
@ -75,7 +84,7 @@ func (s *Server) HandlePipe(conn *websocket.Conn, r *http.Request) {
} }
if pipe.src != nil && pipe.dst != nil { if pipe.src != nil && pipe.dst != nil {
pipe.Start() go pipe.Start()
clientCmdSession := s.findSession(pipe.Src) clientCmdSession := s.findSession(pipe.Src)
clientCmdSession.Send(NcmdConnectionReady(pipe.Id)) // info src endpoint ready and can setup proxy listener clientCmdSession.Send(NcmdConnectionReady(pipe.Id)) // info src endpoint ready and can setup proxy listener
} }

View File

@ -110,7 +110,7 @@ func (s *Server) registHandler() {
s.mux = http.NewServeMux() s.mux = http.NewServeMux()
} }
s.mux.HandleFunc("/hello", s.HandleHello) s.mux.HandleFunc("/hello", s.HandleHello)
s.mux.HandleFunc("/ws", s.HandleWs) s.mux.HandleFunc("/ws/", s.HandleWs)
} }
func (s *Server) Start() { func (s *Server) Start() {
addr := s.Config.Host + ":" + s.Config.Port addr := s.Config.Host + ":" + s.Config.Port

View File

@ -92,7 +92,7 @@ func NewErrorResponse(err string, cmd *Command) *Command {
} }
func NcmdConnectionInited(sessionId string) *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 { func NcmdConnectionReady(sessionId string) *Command {

59
tunnelagent/main.go Normal file
View File

@ -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()
}

87
tunnelclient/main.go Normal file
View File

@ -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.

View File

@ -7,3 +7,13 @@ const (
ConnectionReady ConnectionReady
ErrorCmd ErrorCmd
) )
var (
CmdTypeMap = map[int]string{
NewSession: "new_session",
NewConnection: "new_connection",
ConnectInited: "connect_inited",
ConnectionReady: "connection_ready",
ErrorCmd: "error",
}
)

View File

@ -57,3 +57,8 @@ func GenRandomstring(n int) string {
} }
return string(b) return string(b)
} }
func GenRandomInt() int32 {
// rand.Seed(time.Now().UnixNano())
return int32(rand.Intn(65534) + 1)
}