Mult Port + TCP Calibration (SSH DEAD)

This commit is contained in:
2026-08-17 17:08:57 -03:00
parent 7ea221a99c
commit b997294607
58 changed files with 6033 additions and 497 deletions
+349
View File
@@ -0,0 +1,349 @@
package main
import (
"bufio"
"encoding/binary"
"errors"
"fmt"
"io"
"net"
"sync"
"time"
"dragontcp/internal/protocol"
)
const (
socksVersion5 = 5
socksCmdConnect = 1
socksCmdUDPAssociate = 3
socksAtypIPv4 = 1
socksAtypDomain = 3
socksAtypIPv6 = 4
)
func startSOCKS5Proxy(listenAddr string, manager *sshTunnelManager, maxConnections int, tcpBuffer int) (net.Listener, error) {
ln, err := net.Listen("tcp", listenAddr)
if err != nil {
return nil, err
}
if maxConnections < 1 {
maxConnections = 1
}
slots := make(chan struct{}, maxConnections)
go func() {
for {
conn, err := ln.Accept()
if err != nil {
return
}
select {
case slots <- struct{}{}:
go func(c net.Conn) {
defer func() { <-slots; _ = c.Close() }()
protocol.TuneTCP(c)
protocol.TuneTCPBuffer(c, tcpBuffer)
_ = handleSOCKS5(c, manager)
}(conn)
default:
_ = conn.Close()
}
}
}()
return ln, nil
}
func handleSOCKS5(conn net.Conn, manager *sshTunnelManager) error {
br := bufio.NewReaderSize(conn, 4096)
if err := socksNegotiate(br, conn); err != nil {
return err
}
cmd, host, port, err := socksReadRequest(br)
if err != nil {
_ = socksReply(conn, 1, nil)
return err
}
switch cmd {
case socksCmdConnect:
remote, err := manager.DialTCP(host, port)
if err != nil {
_ = socksReply(conn, 5, nil)
return err
}
defer remote.Close()
if err := socksReply(conn, 0, remote.LocalAddr()); err != nil {
return err
}
protocol.RelayRaw(conn, remote)
return nil
case socksCmdUDPAssociate:
return handleSOCKSUDPAssociate(conn, br, manager)
default:
_ = socksReply(conn, 7, nil)
return fmt.Errorf("SOCKS command %d unsupported", cmd)
}
}
func socksNegotiate(br *bufio.Reader, w io.Writer) error {
header := make([]byte, 2)
if _, err := io.ReadFull(br, header); err != nil {
return err
}
if header[0] != socksVersion5 || header[1] == 0 {
return errors.New("invalid SOCKS5 greeting")
}
methods := make([]byte, int(header[1]))
if _, err := io.ReadFull(br, methods); err != nil {
return err
}
noAuth := false
for _, method := range methods {
if method == 0 {
noAuth = true
break
}
}
if !noAuth {
_, _ = w.Write([]byte{5, 0xff})
return errors.New("SOCKS5 client does not support no-auth")
}
_, err := w.Write([]byte{5, 0})
return err
}
func socksReadRequest(br *bufio.Reader) (cmd byte, host string, port int, err error) {
header := make([]byte, 4)
if _, err = io.ReadFull(br, header); err != nil {
return
}
if header[0] != 5 || header[2] != 0 {
err = errors.New("invalid SOCKS5 request")
return
}
cmd = header[1]
host, err = socksReadHost(br, header[3])
if err != nil {
return
}
var portBuf [2]byte
if _, err = io.ReadFull(br, portBuf[:]); err != nil {
return
}
port = int(binary.BigEndian.Uint16(portBuf[:]))
// CONNECT requires a real destination port. UDP ASSOCIATE commonly uses
// 0.0.0.0:0 to ask the proxy to choose the relay endpoint, which is exactly
// what the Android VPN adapter sends.
if cmd == socksCmdConnect && port < 1 {
err = errors.New("invalid SOCKS5 port")
}
return
}
func socksReadHost(r io.Reader, atyp byte) (string, error) {
switch atyp {
case socksAtypIPv4:
b := make([]byte, 4)
if _, err := io.ReadFull(r, b); err != nil {
return "", err
}
return net.IP(b).String(), nil
case socksAtypIPv6:
b := make([]byte, 16)
if _, err := io.ReadFull(r, b); err != nil {
return "", err
}
return net.IP(b).String(), nil
case socksAtypDomain:
var n [1]byte
if _, err := io.ReadFull(r, n[:]); err != nil {
return "", err
}
if n[0] == 0 {
return "", errors.New("empty SOCKS domain")
}
b := make([]byte, int(n[0]))
if _, err := io.ReadFull(r, b); err != nil {
return "", err
}
return string(b), nil
default:
return "", fmt.Errorf("unsupported SOCKS address type %d", atyp)
}
}
func socksReply(w io.Writer, rep byte, addr net.Addr) error {
ip := net.IPv4zero
port := 0
if tcpAddr, ok := addr.(*net.TCPAddr); ok {
if v4 := tcpAddr.IP.To4(); v4 != nil {
ip = v4
}
port = tcpAddr.Port
} else if udpAddr, ok := addr.(*net.UDPAddr); ok {
if v4 := udpAddr.IP.To4(); v4 != nil {
ip = v4
}
port = udpAddr.Port
}
out := []byte{5, rep, 0, socksAtypIPv4, 0, 0, 0, 0, 0, 0}
copy(out[4:8], ip.To4())
binary.BigEndian.PutUint16(out[8:10], uint16(port))
_, err := w.Write(out)
return err
}
func handleSOCKSUDPAssociate(control net.Conn, br *bufio.Reader, manager *sshTunnelManager) error {
udp, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
if err != nil {
return err
}
defer udp.Close()
if err := socksReply(control, 0, udp.LocalAddr()); err != nil {
return err
}
gw, err := manager.DialUDPGW()
if err != nil {
return err
}
defer gw.Close()
_ = gw.SetDeadline(time.Time{})
done := make(chan struct{})
var closeOnce sync.Once
closeAll := func() { closeOnce.Do(func() { close(done); _ = udp.Close(); _ = gw.Close() }) }
defer closeAll()
var clientMu sync.RWMutex
var clientAddr *net.UDPAddr
writeMu := sync.Mutex{}
go func() {
defer closeAll()
reader := bufio.NewReaderSize(gw, 32*1024)
for {
payload, err := readUDPGWFrame(reader)
if err != nil {
return
}
if len(payload) < 9 {
continue
}
srcIP := payload[3:7]
srcPort := binary.BigEndian.Uint16(payload[7:9])
data := payload[9:]
packet := make([]byte, 10+len(data))
packet[0], packet[1], packet[2], packet[3] = 0, 0, 0, socksAtypIPv4
copy(packet[4:8], srcIP)
binary.BigEndian.PutUint16(packet[8:10], srcPort)
copy(packet[10:], data)
clientMu.RLock()
to := clientAddr
clientMu.RUnlock()
if to != nil {
_, _ = udp.WriteToUDP(packet, to)
}
}
}()
go func() {
defer closeAll()
// The UDP association lifetime is the TCP control connection lifetime.
buf := make([]byte, 1)
for {
if _, err := br.Read(buf); err != nil {
return
}
}
}()
buf := make([]byte, 65535)
for {
n, from, err := udp.ReadFromUDP(buf)
if err != nil {
return nil
}
clientMu.Lock()
clientAddr = from
clientMu.Unlock()
ip, port, payload, err := parseSOCKSUDPDatagram(buf[:n])
if err != nil {
continue
}
frame := buildUDPGWRequest(1, 0, ip, uint16(port), payload)
writeMu.Lock()
_, err = gw.Write(frame)
writeMu.Unlock()
if err != nil {
return err
}
select {
case <-done:
return nil
default:
}
}
}
func parseSOCKSUDPDatagram(packet []byte) ([4]byte, int, []byte, error) {
var out [4]byte
if len(packet) < 10 || packet[0] != 0 || packet[1] != 0 || packet[2] != 0 {
return out, 0, nil, errors.New("invalid SOCKS5 UDP packet")
}
pos := 3
atyp := packet[pos]
pos++
switch atyp {
case socksAtypIPv4:
if len(packet) < pos+4+2 {
return out, 0, nil, io.ErrUnexpectedEOF
}
copy(out[:], packet[pos:pos+4])
pos += 4
case socksAtypDomain:
if len(packet) <= pos {
return out, 0, nil, io.ErrUnexpectedEOF
}
n := int(packet[pos])
pos++
if len(packet) < pos+n+2 {
return out, 0, nil, io.ErrUnexpectedEOF
}
return out, 0, nil, errors.New("SOCKS UDP domain destinations are disabled to avoid local DNS leakage; use an IPv4 destination")
case socksAtypIPv6:
return out, 0, nil, errors.New("UDPGW supports IPv4 only")
default:
return out, 0, nil, errors.New("unsupported SOCKS UDP address type")
}
port := int(binary.BigEndian.Uint16(packet[pos : pos+2]))
pos += 2
return out, port, packet[pos:], nil
}
func buildUDPGWRequest(connID uint16, x byte, ip [4]byte, port uint16, data []byte) []byte {
payloadLen := 9 + len(data)
frame := make([]byte, 2+payloadLen)
binary.LittleEndian.PutUint16(frame[0:2], uint16(payloadLen))
binary.BigEndian.PutUint16(frame[2:4], connID)
frame[4] = x
copy(frame[5:9], ip[:])
binary.BigEndian.PutUint16(frame[9:11], port)
copy(frame[11:], data)
return frame
}
func readUDPGWFrame(r *bufio.Reader) ([]byte, error) {
var lenBuf [2]byte
if _, err := io.ReadFull(r, lenBuf[:]); err != nil {
return nil, err
}
n := int(binary.LittleEndian.Uint16(lenBuf[:]))
if n < 9 || n > 65535 {
return nil, fmt.Errorf("invalid UDPGW frame %d", n)
}
payload := make([]byte, n)
if _, err := io.ReadFull(r, payload); err != nil {
return nil, err
}
return payload, nil
}