Merge remote-tracking branch 'meta/Alpha' into Alpha
This commit is contained in:
commit
a38b2bcb6d
17 changed files with 298 additions and 147 deletions
28
.github/workflows/alpha.yml
vendored
28
.github/workflows/alpha.yml
vendored
|
@ -1,7 +1,7 @@
|
|||
name: Alpha
|
||||
name: alpha
|
||||
on: [push]
|
||||
jobs:
|
||||
Feature-build:
|
||||
Build:
|
||||
if: ${{ !contains(github.event.head_commit.message, '[Skip CI]') }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -57,15 +57,15 @@ jobs:
|
|||
files: bin/*
|
||||
prerelease: true
|
||||
|
||||
- name: send telegram message on push
|
||||
uses: appleboy/telegram-action@master
|
||||
with:
|
||||
to: ${{ secrets.TTELEGRAM_CHAT_ID }}
|
||||
token: ${{ secrets.TELEGRAM_TOKEN }}
|
||||
message: |
|
||||
${{ github.actor }} created commit:
|
||||
Commit message: ${{ github.event.commits[0].message }}
|
||||
|
||||
Repository: ${{ github.repository }}
|
||||
|
||||
See changes: https://github.com/${{ github.repository }}/commit/${{github.sha}}
|
||||
# - name: send telegram message on push
|
||||
# uses: appleboy/telegram-action@master
|
||||
# with:
|
||||
# to: ${{ secrets.TTELEGRAM_CHAT_ID }}
|
||||
# token: ${{ secrets.TELEGRAM_TOKEN }}
|
||||
# message: |
|
||||
# ${{ github.actor }} created commit:
|
||||
# Commit message: ${{ github.event.commits[0].message }}
|
||||
#
|
||||
# Repository: ${{ github.repository }}
|
||||
#
|
||||
# See changes: https://github.com/${{ github.repository }}/commit/${{github.sha}}
|
|
@ -2,9 +2,12 @@ package outbound
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net"
|
||||
"regexp"
|
||||
|
||||
"github.com/Dreamacro/clash/component/dialer"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
|
@ -136,3 +139,28 @@ func (c *packetConn) AppendToChains(a C.ProxyAdapter) {
|
|||
func newPacketConn(pc net.PacketConn, a C.ProxyAdapter) C.PacketConn {
|
||||
return &packetConn{pc, []string{a.Name()}}
|
||||
}
|
||||
|
||||
func uuidMap(str string) string {
|
||||
match, _ := regexp.MatchString(`[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$`, str)
|
||||
if !match {
|
||||
var Nil [16]byte
|
||||
h := sha1.New()
|
||||
h.Write(Nil[:])
|
||||
h.Write([]byte(str))
|
||||
u := h.Sum(nil)[:16]
|
||||
u[6] = (u[6] & 0x0f) | (5 << 4)
|
||||
u[8] = u[8]&(0xff>>2) | (0x02 << 6)
|
||||
buf := make([]byte, 36)
|
||||
hex.Encode(buf[0:8], u[0:4])
|
||||
buf[8] = '-'
|
||||
hex.Encode(buf[9:13], u[4:6])
|
||||
buf[13] = '-'
|
||||
hex.Encode(buf[14:18], u[6:8])
|
||||
buf[18] = '-'
|
||||
hex.Encode(buf[19:23], u[8:10])
|
||||
buf[23] = '-'
|
||||
hex.Encode(buf[24:], u[10:])
|
||||
return string(buf)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
|
|
@ -386,7 +386,7 @@ func NewVless(option VlessOption) (*Vless, error) {
|
|||
}
|
||||
}
|
||||
|
||||
client, err := vless.NewClient(option.UUID, addons, option.FlowShow)
|
||||
client, err := vless.NewClient(uuidMap(option.UUID), addons, option.FlowShow)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -266,7 +266,7 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
|
|||
func NewVmess(option VmessOption) (*Vmess, error) {
|
||||
security := strings.ToLower(option.Cipher)
|
||||
client, err := vmess.NewClient(vmess.Config{
|
||||
UUID: option.UUID,
|
||||
UUID: uuidMap(option.UUID),
|
||||
AlterID: uint16(option.AlterID),
|
||||
Security: security,
|
||||
HostName: option.Server,
|
||||
|
|
30
common/net/relay.go
Normal file
30
common/net/relay.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
package net
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
)
|
||||
|
||||
// Relay copies between left and right bidirectionally.
|
||||
func Relay(leftConn, rightConn net.Conn) {
|
||||
ch := make(chan error)
|
||||
|
||||
go func() {
|
||||
buf := pool.Get(pool.RelayBufferSize)
|
||||
// Wrapping to avoid using *net.TCPConn.(ReadFrom)
|
||||
// See also https://github.com/Dreamacro/clash/pull/1209
|
||||
_, err := io.CopyBuffer(WriteOnlyWriter{Writer: leftConn}, ReadOnlyReader{Reader: rightConn}, buf)
|
||||
pool.Put(buf)
|
||||
leftConn.SetReadDeadline(time.Now())
|
||||
ch <- err
|
||||
}()
|
||||
|
||||
buf := pool.Get(pool.RelayBufferSize)
|
||||
io.CopyBuffer(WriteOnlyWriter{Writer: rightConn}, ReadOnlyReader{Reader: leftConn}, buf)
|
||||
pool.Put(buf)
|
||||
rightConn.SetReadDeadline(time.Now())
|
||||
<-ch
|
||||
}
|
|
@ -31,7 +31,7 @@ func NewDecoder(option Option) *Decoder {
|
|||
// Decode transform a map[string]any to a struct
|
||||
func (d *Decoder) Decode(src map[string]any, dst any) error {
|
||||
if reflect.TypeOf(dst).Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("Decode must recive a ptr struct")
|
||||
return fmt.Errorf("decode must recive a ptr struct")
|
||||
}
|
||||
t := reflect.TypeOf(dst).Elem()
|
||||
v := reflect.ValueOf(dst).Elem()
|
||||
|
@ -291,7 +291,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
|||
field reflect.StructField
|
||||
val reflect.Value
|
||||
}
|
||||
fields := []field{}
|
||||
var fields []field
|
||||
for len(structs) > 0 {
|
||||
structVal := structs[0]
|
||||
structs = structs[1:]
|
||||
|
|
|
@ -4,14 +4,19 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"net"
|
||||
"net/netip"
|
||||
"sync"
|
||||
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
)
|
||||
|
||||
var DisableIPv6 = false
|
||||
var (
|
||||
dialMux sync.Mutex
|
||||
actualSingleDialContext = singleDialContext
|
||||
actualDualStackDialContext = dualStackDialContext
|
||||
DisableIPv6 = false
|
||||
)
|
||||
|
||||
func DialContext(ctx context.Context, network, address string, options ...Option) (net.Conn, error) {
|
||||
opt := &option{
|
||||
|
@ -29,37 +34,9 @@ func DialContext(ctx context.Context, network, address string, options ...Option
|
|||
|
||||
switch network {
|
||||
case "tcp4", "tcp6", "udp4", "udp6":
|
||||
host, port, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ip netip.Addr
|
||||
switch network {
|
||||
case "tcp4", "udp4":
|
||||
if !opt.direct {
|
||||
ip, err = resolver.ResolveIPv4ProxyServerHost(host)
|
||||
} else {
|
||||
ip, err = resolver.ResolveIPv4(host)
|
||||
}
|
||||
default:
|
||||
if !opt.direct {
|
||||
ip, err = resolver.ResolveIPv6ProxyServerHost(host)
|
||||
} else {
|
||||
ip, err = resolver.ResolveIPv6(host)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dialContext(ctx, network, ip, port, opt)
|
||||
return actualSingleDialContext(ctx, network, address, opt)
|
||||
case "tcp", "udp":
|
||||
if TCPConcurrent {
|
||||
return concurrentDialContext(ctx, network, address, opt)
|
||||
} else {
|
||||
return dualStackDialContext(ctx, network, address, opt)
|
||||
}
|
||||
return actualDualStackDialContext(ctx, network, address, opt)
|
||||
default:
|
||||
return nil, errors.New("network invalid")
|
||||
}
|
||||
|
@ -97,6 +74,19 @@ func ListenPacket(ctx context.Context, network, address string, options ...Optio
|
|||
return lc.ListenPacket(ctx, network, address)
|
||||
}
|
||||
|
||||
func SetDial(concurrent bool) {
|
||||
dialMux.Lock()
|
||||
if concurrent {
|
||||
actualSingleDialContext = concurrentSingleDialContext
|
||||
actualDualStackDialContext = concurrentDualStackDialContext
|
||||
} else {
|
||||
actualSingleDialContext = singleDialContext
|
||||
actualDualStackDialContext = concurrentDualStackDialContext
|
||||
}
|
||||
|
||||
dialMux.Unlock()
|
||||
}
|
||||
|
||||
func dialContext(ctx context.Context, network string, destination netip.Addr, port string, opt *option) (net.Conn, error) {
|
||||
dialer := &net.Dialer{}
|
||||
if opt.interfaceName != "" {
|
||||
|
@ -196,12 +186,24 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt
|
|||
return nil, errors.New("never touched")
|
||||
}
|
||||
|
||||
func concurrentDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) {
|
||||
func concurrentDualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) {
|
||||
host, port, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ips []netip.Addr
|
||||
|
||||
if opt.direct {
|
||||
ips, err = resolver.ResolveAllIP(host)
|
||||
} else {
|
||||
ips, err = resolver.ResolveAllIPProxyServerHost(host)
|
||||
}
|
||||
|
||||
return concurrentDialContext(ctx, network, ips, port, opt)
|
||||
}
|
||||
|
||||
func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) {
|
||||
returned := make(chan struct{})
|
||||
defer close(returned)
|
||||
|
||||
|
@ -213,13 +215,6 @@ func concurrentDialContext(ctx context.Context, network, address string, opt *op
|
|||
}
|
||||
|
||||
results := make(chan dialResult)
|
||||
var ips []netip.Addr
|
||||
|
||||
if opt.direct {
|
||||
ips, err = resolver.ResolveAllIP(host)
|
||||
} else {
|
||||
ips, err = resolver.ResolveAllIPProxyServerHost(host)
|
||||
}
|
||||
|
||||
tcpRacer := func(ctx context.Context, ip netip.Addr) {
|
||||
result := dialResult{ip: ip}
|
||||
|
@ -239,7 +234,6 @@ func concurrentDialContext(ctx context.Context, network, address string, opt *op
|
|||
v = "6"
|
||||
}
|
||||
|
||||
log.Debugln("[%s] try use [%s] connected", host, ip.String())
|
||||
result.Conn, result.error = dialContext(ctx, network+v, ip, port, opt)
|
||||
}
|
||||
|
||||
|
@ -251,16 +245,70 @@ func concurrentDialContext(ctx context.Context, network, address string, opt *op
|
|||
for res := range results {
|
||||
connCount--
|
||||
if res.error == nil {
|
||||
log.Debugln("[%s] used [%s] connected", host, res.ip.String())
|
||||
return res.Conn, nil
|
||||
}
|
||||
|
||||
log.Errorln("connect error:%v", res.error)
|
||||
if connCount == 0 {
|
||||
log.Errorln("connect [%s] all ip failed", host)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("all ip tcp shakeHands failed")
|
||||
return nil, errors.New("all ip tcp shake hands failed")
|
||||
}
|
||||
|
||||
func singleDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) {
|
||||
host, port, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ip netip.Addr
|
||||
switch network {
|
||||
case "tcp4", "udp4":
|
||||
if !opt.direct {
|
||||
ip, err = resolver.ResolveIPv4ProxyServerHost(host)
|
||||
} else {
|
||||
ip, err = resolver.ResolveIPv4(host)
|
||||
}
|
||||
default:
|
||||
if !opt.direct {
|
||||
ip, err = resolver.ResolveIPv6ProxyServerHost(host)
|
||||
} else {
|
||||
ip, err = resolver.ResolveIPv6(host)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dialContext(ctx, network, ip, port, opt)
|
||||
}
|
||||
|
||||
func concurrentSingleDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) {
|
||||
host, port, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ips []netip.Addr
|
||||
switch network {
|
||||
case "tcp4", "udp4":
|
||||
if !opt.direct {
|
||||
ips, err = resolver.ResolveAllIPv4ProxyServerHost(host)
|
||||
} else {
|
||||
ips, err = resolver.ResolveAllIPv4(host)
|
||||
}
|
||||
default:
|
||||
if !opt.direct {
|
||||
ips, err = resolver.ResolveAllIPv6ProxyServerHost(host)
|
||||
} else {
|
||||
ips, err = resolver.ResolveAllIPv6(host)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return concurrentDialContext(ctx, network, ips, port, opt)
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ var (
|
|||
DefaultOptions []Option
|
||||
DefaultInterface = atomic.NewString("")
|
||||
DefaultRoutingMark = atomic.NewInt32(0)
|
||||
TCPConcurrent = false
|
||||
)
|
||||
|
||||
type option struct {
|
||||
|
|
|
@ -23,15 +23,17 @@ var (
|
|||
|
||||
var Dispatcher SnifferDispatcher
|
||||
|
||||
type SnifferDispatcher struct {
|
||||
enable bool
|
||||
type (
|
||||
SnifferDispatcher struct {
|
||||
enable bool
|
||||
|
||||
sniffers []C.Sniffer
|
||||
sniffers []C.Sniffer
|
||||
|
||||
foreDomain *trie.DomainTrie[bool]
|
||||
skipSNI *trie.DomainTrie[bool]
|
||||
portRanges *[]utils.Range[uint16]
|
||||
}
|
||||
foreDomain *trie.DomainTrie[bool]
|
||||
skipSNI *trie.DomainTrie[bool]
|
||||
portRanges *[]utils.Range[uint16]
|
||||
}
|
||||
)
|
||||
|
||||
func (sd *SnifferDispatcher) TCPSniff(conn net.Conn, metadata *C.Metadata) {
|
||||
bufConn, ok := conn.(*CN.BufferedConn)
|
||||
|
@ -101,7 +103,6 @@ func (sd *SnifferDispatcher) sniffDomain(conn *CN.BufferedConn, metadata *C.Meta
|
|||
log.Errorln("[Sniffer] [%s] Maybe read timeout, Consider adding skip", metadata.DstIP.String())
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
log.Errorln("[Sniffer] %v", err)
|
||||
return "", err
|
||||
}
|
||||
|
|
|
@ -254,8 +254,8 @@ func updateGeneral(general *config.General, force bool) {
|
|||
resolver.DisableIPv6 = true
|
||||
}
|
||||
|
||||
dialer.TCPConcurrent = general.TCPConcurrent
|
||||
if dialer.TCPConcurrent {
|
||||
if general.TCPConcurrent {
|
||||
dialer.SetDial(general.TCPConcurrent)
|
||||
log.Infoln("Use tcp concurrent")
|
||||
}
|
||||
|
||||
|
|
|
@ -19,12 +19,7 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.Cache[string,
|
|||
client := newClient(c.RemoteAddr(), in)
|
||||
defer client.CloseIdleConnections()
|
||||
|
||||
var conn *N.BufferedConn
|
||||
if bufConn, ok := c.(*N.BufferedConn); ok {
|
||||
conn = bufConn
|
||||
} else {
|
||||
conn = N.NewBufferedConn(c)
|
||||
}
|
||||
conn := N.NewBufferedConn(c)
|
||||
|
||||
keepAlive := true
|
||||
trusted := cache == nil // disable authenticate if cache is nil
|
||||
|
@ -66,6 +61,12 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.Cache[string,
|
|||
|
||||
request.RequestURI = ""
|
||||
|
||||
if isUpgradeRequest(request) {
|
||||
if resp = handleUpgrade(conn, conn.RemoteAddr(), request, in); resp == nil {
|
||||
return // hijack connection
|
||||
}
|
||||
}
|
||||
|
||||
removeHopByHopHeaders(request.Header)
|
||||
removeExtraHTTPHostPort(request)
|
||||
|
||||
|
@ -95,7 +96,7 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.Cache[string,
|
|||
}
|
||||
}
|
||||
|
||||
conn.Close()
|
||||
_ = conn.Close()
|
||||
}
|
||||
|
||||
func authenticate(request *http.Request, cache *cache.Cache[string, bool]) *http.Response {
|
||||
|
|
95
listener/http/upgrade.go
Normal file
95
listener/http/upgrade.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
N "github.com/Dreamacro/clash/common/net"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
func isUpgradeRequest(req *http.Request) bool {
|
||||
return strings.EqualFold(req.Header.Get("Connection"), "Upgrade")
|
||||
}
|
||||
|
||||
func handleUpgrade(localConn net.Conn, source net.Addr, request *http.Request, in chan<- C.ConnContext) (resp *http.Response) {
|
||||
removeProxyHeaders(request.Header)
|
||||
removeExtraHTTPHostPort(request)
|
||||
|
||||
address := request.Host
|
||||
if _, _, err := net.SplitHostPort(address); err != nil {
|
||||
port := "80"
|
||||
if request.TLS != nil {
|
||||
port = "443"
|
||||
}
|
||||
address = net.JoinHostPort(address, port)
|
||||
}
|
||||
|
||||
dstAddr := socks5.ParseAddr(address)
|
||||
if dstAddr == nil {
|
||||
return
|
||||
}
|
||||
|
||||
left, right := net.Pipe()
|
||||
|
||||
in <- inbound.NewHTTP(dstAddr, source, right)
|
||||
|
||||
var remoteServer *N.BufferedConn
|
||||
if request.TLS != nil {
|
||||
tlsConn := tls.Client(left, &tls.Config{
|
||||
ServerName: request.URL.Hostname(),
|
||||
})
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
|
||||
defer cancel()
|
||||
if tlsConn.HandshakeContext(ctx) != nil {
|
||||
_ = localConn.Close()
|
||||
_ = left.Close()
|
||||
return
|
||||
}
|
||||
|
||||
remoteServer = N.NewBufferedConn(tlsConn)
|
||||
} else {
|
||||
remoteServer = N.NewBufferedConn(left)
|
||||
}
|
||||
defer func() {
|
||||
_ = remoteServer.Close()
|
||||
}()
|
||||
|
||||
err := request.Write(remoteServer)
|
||||
if err != nil {
|
||||
_ = localConn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
resp, err = http.ReadResponse(remoteServer.Reader(), request)
|
||||
if err != nil {
|
||||
_ = localConn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusSwitchingProtocols {
|
||||
removeProxyHeaders(resp.Header)
|
||||
|
||||
err = localConn.SetReadDeadline(time.Time{}) // set to not time out
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = resp.Write(localConn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
N.Relay(remoteServer, localConn) // blocking here
|
||||
_ = localConn.Close()
|
||||
resp = nil
|
||||
}
|
||||
return
|
||||
}
|
|
@ -8,15 +8,21 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// removeHopByHopHeaders remove Proxy-* headers
|
||||
func removeProxyHeaders(header http.Header) {
|
||||
header.Del("Proxy-Connection")
|
||||
header.Del("Proxy-Authenticate")
|
||||
header.Del("Proxy-Authorization")
|
||||
}
|
||||
|
||||
// removeHopByHopHeaders remove hop-by-hop header
|
||||
func removeHopByHopHeaders(header http.Header) {
|
||||
// Strip hop-by-hop header based on RFC:
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1
|
||||
// https://www.mnot.net/blog/2011/07/11/what_proxies_must_do
|
||||
|
||||
header.Del("Proxy-Connection")
|
||||
header.Del("Proxy-Authenticate")
|
||||
header.Del("Proxy-Authorization")
|
||||
removeProxyHeaders(header)
|
||||
|
||||
header.Del("TE")
|
||||
header.Del("Trailers")
|
||||
header.Del("Transfer-Encoding")
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package nat
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"io"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
@ -43,7 +44,8 @@ func Start(device io.ReadWriter, gateway, portal, broadcast netip.Addr) (*TCP, *
|
|||
for {
|
||||
n, err := device.Read(buf)
|
||||
if err != nil {
|
||||
return
|
||||
log.Warnln("system error:%s", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
raw := buf[:n]
|
||||
|
|
|
@ -2,7 +2,6 @@ package nat
|
|||
|
||||
import (
|
||||
"net/netip"
|
||||
"sync"
|
||||
|
||||
"github.com/Dreamacro/clash/common/generics/list"
|
||||
)
|
||||
|
@ -25,7 +24,6 @@ type binding struct {
|
|||
}
|
||||
|
||||
type table struct {
|
||||
mu sync.Mutex
|
||||
tuples map[tuple]*list.Element[*binding]
|
||||
ports [portLength]*list.Element[*binding]
|
||||
available *list.List[*binding]
|
||||
|
@ -39,13 +37,13 @@ func (t *table) tupleOf(port uint16) tuple {
|
|||
|
||||
elm := t.ports[offset]
|
||||
|
||||
t.available.MoveToFront(elm)
|
||||
|
||||
return elm.Value.tuple
|
||||
}
|
||||
|
||||
func (t *table) portOf(tuple tuple) uint16 {
|
||||
t.mu.Lock()
|
||||
elm := t.tuples[tuple]
|
||||
t.mu.Unlock()
|
||||
if elm == nil {
|
||||
return 0
|
||||
}
|
||||
|
@ -59,11 +57,8 @@ func (t *table) newConn(tuple tuple) uint16 {
|
|||
elm := t.available.Back()
|
||||
b := elm.Value
|
||||
|
||||
t.mu.Lock()
|
||||
delete(t.tuples, b.tuple)
|
||||
t.tuples[tuple] = elm
|
||||
t.mu.Unlock()
|
||||
|
||||
b.tuple = tuple
|
||||
|
||||
t.available.MoveToFront(elm)
|
||||
|
@ -71,19 +66,6 @@ func (t *table) newConn(tuple tuple) uint16 {
|
|||
return portBegin + b.offset
|
||||
}
|
||||
|
||||
func (t *table) delete(tup tuple) {
|
||||
t.mu.Lock()
|
||||
elm := t.tuples[tup]
|
||||
if elm == nil {
|
||||
t.mu.Unlock()
|
||||
return
|
||||
}
|
||||
delete(t.tuples, tup)
|
||||
t.mu.Unlock()
|
||||
|
||||
t.available.MoveToBack(elm)
|
||||
}
|
||||
|
||||
func newTable() *table {
|
||||
result := &table{
|
||||
tuples: make(map[tuple]*list.Element[*binding], portLength),
|
||||
|
|
|
@ -16,8 +16,6 @@ type conn struct {
|
|||
net.Conn
|
||||
|
||||
tuple tuple
|
||||
|
||||
close func(tuple tuple)
|
||||
}
|
||||
|
||||
func (t *TCP) Accept() (net.Conn, error) {
|
||||
|
@ -39,9 +37,6 @@ func (t *TCP) Accept() (net.Conn, error) {
|
|||
return &conn{
|
||||
Conn: c,
|
||||
tuple: tup,
|
||||
close: func(tuple tuple) {
|
||||
t.table.delete(tuple)
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -57,11 +52,6 @@ func (t *TCP) SetDeadline(time time.Time) error {
|
|||
return t.listener.SetDeadline(time)
|
||||
}
|
||||
|
||||
func (c *conn) Close() error {
|
||||
c.close(c.tuple)
|
||||
return c.Conn.Close()
|
||||
}
|
||||
|
||||
func (c *conn) LocalAddr() net.Addr {
|
||||
return &net.TCPAddr{
|
||||
IP: c.tuple.SourceAddr.Addr().AsSlice(),
|
||||
|
|
|
@ -2,7 +2,6 @@ package tunnel
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
|
@ -63,35 +62,5 @@ func handleUDPToLocal(packet C.UDPPacket, pc net.PacketConn, key string, fAddr n
|
|||
}
|
||||
|
||||
func handleSocket(ctx C.ConnContext, outbound net.Conn) {
|
||||
relay(ctx.Conn(), outbound)
|
||||
}
|
||||
|
||||
// relay copies between left and right bidirectionally.
|
||||
func relay(leftConn, rightConn net.Conn) {
|
||||
ch := make(chan error)
|
||||
|
||||
tcpKeepAlive(leftConn)
|
||||
tcpKeepAlive(rightConn)
|
||||
|
||||
go func() {
|
||||
buf := pool.Get(pool.RelayBufferSize)
|
||||
// Wrapping to avoid using *net.TCPConn.(ReadFrom)
|
||||
// See also https://github.com/Dreamacro/clash/pull/1209
|
||||
_, err := io.CopyBuffer(N.WriteOnlyWriter{Writer: leftConn}, N.ReadOnlyReader{Reader: rightConn}, buf)
|
||||
pool.Put(buf)
|
||||
leftConn.SetReadDeadline(time.Now())
|
||||
ch <- err
|
||||
}()
|
||||
|
||||
buf := pool.Get(pool.RelayBufferSize)
|
||||
io.CopyBuffer(N.WriteOnlyWriter{Writer: rightConn}, N.ReadOnlyReader{Reader: leftConn}, buf)
|
||||
pool.Put(buf)
|
||||
rightConn.SetReadDeadline(time.Now())
|
||||
<-ch
|
||||
}
|
||||
|
||||
func tcpKeepAlive(c net.Conn) {
|
||||
if tcp, ok := c.(*net.TCPConn); ok {
|
||||
tcp.SetKeepAlive(true)
|
||||
}
|
||||
N.Relay(ctx.Conn(), outbound)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue