Feature: socks5 udp associate
This commit is contained in:
@ -6,21 +6,12 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
adapters "github.com/Dreamacro/clash/adapters/inbound"
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
)
|
||||
|
||||
const (
|
||||
// io.Copy default buffer size is 32 KiB
|
||||
// but the maximum packet size of vmess/shadowsocks is about 16 KiB
|
||||
// so define a buffer of 20 KiB to reduce the memory of each TCP relay
|
||||
bufferSize = 20 * 1024
|
||||
)
|
||||
|
||||
var bufPool = sync.Pool{New: func() interface{} { return make([]byte, bufferSize) }}
|
||||
|
||||
func (t *Tunnel) handleHTTP(request *adapters.HTTPAdapter, outbound net.Conn) {
|
||||
conn := newTrafficTrack(outbound, t.traffic)
|
||||
req := request.R
|
||||
@ -50,7 +41,7 @@ func (t *Tunnel) handleHTTP(request *adapters.HTTPAdapter, outbound net.Conn) {
|
||||
} else {
|
||||
resp.Close = true
|
||||
}
|
||||
err = resp.Write(request.Conn())
|
||||
err = resp.Write(request)
|
||||
if err != nil || resp.Close {
|
||||
break
|
||||
}
|
||||
@ -59,7 +50,7 @@ func (t *Tunnel) handleHTTP(request *adapters.HTTPAdapter, outbound net.Conn) {
|
||||
break
|
||||
}
|
||||
|
||||
req, err = http.ReadRequest(bufio.NewReader(request.Conn()))
|
||||
req, err = http.ReadRequest(bufio.NewReader(request))
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
@ -72,9 +63,52 @@ func (t *Tunnel) handleHTTP(request *adapters.HTTPAdapter, outbound net.Conn) {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tunnel) handleSOCKS(request *adapters.SocketAdapter, outbound net.Conn) {
|
||||
func (t *Tunnel) handleSocket(request *adapters.SocketAdapter, outbound net.Conn) {
|
||||
conn := newTrafficTrack(outbound, t.traffic)
|
||||
relay(request.Conn(), conn)
|
||||
relay(request, conn)
|
||||
}
|
||||
|
||||
func (t *Tunnel) handleUDPOverTCP(conn net.Conn, pc net.PacketConn, addr net.Addr) error {
|
||||
ch := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
buf := pool.BufPool.Get().([]byte)
|
||||
defer pool.BufPool.Put(buf)
|
||||
for {
|
||||
n, err := conn.Read(buf)
|
||||
if err != nil {
|
||||
ch <- err
|
||||
return
|
||||
}
|
||||
pc.SetReadDeadline(time.Now().Add(120 * time.Second))
|
||||
if _, err = pc.WriteTo(buf[:n], addr); err != nil {
|
||||
ch <- err
|
||||
return
|
||||
}
|
||||
t.traffic.Up() <- int64(n)
|
||||
ch <- nil
|
||||
}
|
||||
}()
|
||||
|
||||
buf := pool.BufPool.Get().([]byte)
|
||||
defer pool.BufPool.Put(buf)
|
||||
|
||||
for {
|
||||
pc.SetReadDeadline(time.Now().Add(120 * time.Second))
|
||||
n, _, err := pc.ReadFrom(buf)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if _, err := conn.Write(buf[:n]); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
t.traffic.Down() <- int64(n)
|
||||
}
|
||||
|
||||
<-ch
|
||||
return nil
|
||||
}
|
||||
|
||||
// relay copies between left and right bidirectionally.
|
||||
@ -82,16 +116,16 @@ func relay(leftConn, rightConn net.Conn) {
|
||||
ch := make(chan error)
|
||||
|
||||
go func() {
|
||||
buf := bufPool.Get().([]byte)
|
||||
buf := pool.BufPool.Get().([]byte)
|
||||
_, err := io.CopyBuffer(leftConn, rightConn, buf)
|
||||
bufPool.Put(buf[:cap(buf)])
|
||||
pool.BufPool.Put(buf[:cap(buf)])
|
||||
leftConn.SetReadDeadline(time.Now())
|
||||
ch <- err
|
||||
}()
|
||||
|
||||
buf := bufPool.Get().([]byte)
|
||||
buf := pool.BufPool.Get().([]byte)
|
||||
io.CopyBuffer(rightConn, leftConn, buf)
|
||||
bufPool.Put(buf[:cap(buf)])
|
||||
pool.BufPool.Put(buf[:cap(buf)])
|
||||
rightConn.SetReadDeadline(time.Now())
|
||||
<-ch
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ var (
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// Tunnel handle proxy socket and HTTP/SOCKS socket
|
||||
// Tunnel handle relay inbound proxy and outbound proxy
|
||||
type Tunnel struct {
|
||||
queue *channels.InfiniteChannel
|
||||
rules []C.Rule
|
||||
@ -143,6 +143,12 @@ func (t *Tunnel) handleConn(localConn C.ServerAdapter) {
|
||||
}
|
||||
}
|
||||
|
||||
if metadata.NetWork == C.UDP {
|
||||
pc, addr, _ := proxy.DialUDP(metadata)
|
||||
t.handleUDPOverTCP(localConn, pc, addr)
|
||||
return
|
||||
}
|
||||
|
||||
remoConn, err := proxy.Dial(metadata)
|
||||
if err != nil {
|
||||
log.Warnln("Proxy[%s] connect [%s --> %s] error: %s", proxy.Name(), metadata.SourceIP.String(), metadata.String(), err.Error())
|
||||
@ -154,7 +160,7 @@ func (t *Tunnel) handleConn(localConn C.ServerAdapter) {
|
||||
case *InboundAdapter.HTTPAdapter:
|
||||
t.handleHTTP(adapter, remoConn)
|
||||
case *InboundAdapter.SocketAdapter:
|
||||
t.handleSOCKS(adapter, remoConn)
|
||||
t.handleSocket(adapter, remoConn)
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,10 +183,17 @@ func (t *Tunnel) match(metadata *C.Metadata) (C.Proxy, error) {
|
||||
}
|
||||
|
||||
if rule.IsMatch(metadata) {
|
||||
if a, ok := t.proxies[rule.Adapter()]; ok {
|
||||
log.Infoln("%s --> %v match %s using %s", metadata.SourceIP.String(), metadata.String(), rule.RuleType().String(), rule.Adapter())
|
||||
return a, nil
|
||||
adapter, ok := t.proxies[rule.Adapter()]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if metadata.NetWork == C.UDP && !adapter.SupportUDP() {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Infoln("%s --> %v match %s using %s", metadata.SourceIP.String(), metadata.String(), rule.RuleType().String(), rule.Adapter())
|
||||
return adapter, nil
|
||||
}
|
||||
}
|
||||
log.Infoln("%s --> %v doesn't match any rule using DIRECT", metadata.SourceIP.String(), metadata.String())
|
||||
|
Reference in New Issue
Block a user