Compare commits
7 Commits
plus_pro
...
v1.12.1_tu
Author | SHA1 | Date | |
---|---|---|---|
c0ea0cfd5d | |||
f700f4b6a3 | |||
f750bc96cb | |||
0002064c07 | |||
9ef850a55b | |||
37ed4a2b94 | |||
26dd6343a1 |
@ -13,11 +13,11 @@
|
|||||||
<img src="https://goreportcard.com/badge/github.com/Dreamacro/clash?style=flat-square">
|
<img src="https://goreportcard.com/badge/github.com/Dreamacro/clash?style=flat-square">
|
||||||
</a>
|
</a>
|
||||||
<img src="https://img.shields.io/github/go-mod/go-version/Dreamacro/clash?style=flat-square">
|
<img src="https://img.shields.io/github/go-mod/go-version/Dreamacro/clash?style=flat-square">
|
||||||
<a href="https://github.com/Dreamacro/clash/releases">
|
<a href="https://github.com/yaling888/clash/releases">
|
||||||
<img src="https://img.shields.io/github/release/Dreamacro/clash/all.svg?style=flat-square">
|
<img src="https://img.shields.io/github/release/yaling888/clash/all.svg?style=flat-square">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/Dreamacro/clash/releases/tag/premium">
|
<a href="https://github.com/yaling888/clash/releases/tag/plus_pro">
|
||||||
<img src="https://img.shields.io/badge/release-Premium-00b4f0?style=flat-square">
|
<img src="https://img.shields.io/badge/release-Plus Pro-00b4f0?style=flat-square">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...
|
|||||||
|
|
||||||
tcpKeepAlive(c)
|
tcpKeepAlive(c)
|
||||||
|
|
||||||
if !metadata.DstIP.IsValid() && c.RemoteAddr() != nil {
|
if !metadata.Resolved() && c.RemoteAddr() != nil {
|
||||||
if h, _, err := net.SplitHostPort(c.RemoteAddr().String()); err == nil {
|
if h, _, err := net.SplitHostPort(c.RemoteAddr().String()); err == nil {
|
||||||
metadata.DstIP = netip.MustParseAddr(h)
|
metadata.DstIP = netip.MustParseAddr(h)
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ func (m *Mitm) DialContext(_ context.Context, metadata *C.Metadata, _ ...dialer.
|
|||||||
|
|
||||||
_ = c.SetKeepAlive(true)
|
_ = c.SetKeepAlive(true)
|
||||||
_ = c.SetKeepAlivePeriod(60 * time.Second)
|
_ = c.SetKeepAlivePeriod(60 * time.Second)
|
||||||
|
_ = c.SetLinger(0)
|
||||||
|
|
||||||
metadata.Type = C.MITM
|
metadata.Type = C.MITM
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ func tcpKeepAlive(c net.Conn) {
|
|||||||
if tcp, ok := c.(*net.TCPConn); ok {
|
if tcp, ok := c.(*net.TCPConn); ok {
|
||||||
_ = tcp.SetKeepAlive(true)
|
_ = tcp.SetKeepAlive(true)
|
||||||
_ = tcp.SetKeepAlivePeriod(30 * time.Second)
|
_ = tcp.SetKeepAlivePeriod(30 * time.Second)
|
||||||
|
_ = tcp.SetLinger(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,17 +91,16 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
|||||||
wsOpts := &vmess.WebsocketConfig{
|
wsOpts := &vmess.WebsocketConfig{
|
||||||
Host: host,
|
Host: host,
|
||||||
Port: port,
|
Port: port,
|
||||||
|
Headers: http.Header{},
|
||||||
Path: v.option.WSOpts.Path,
|
Path: v.option.WSOpts.Path,
|
||||||
MaxEarlyData: v.option.WSOpts.MaxEarlyData,
|
MaxEarlyData: v.option.WSOpts.MaxEarlyData,
|
||||||
EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName,
|
EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName,
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(v.option.WSOpts.Headers) != 0 {
|
if len(v.option.WSOpts.Headers) != 0 {
|
||||||
header := http.Header{}
|
|
||||||
for key, value := range v.option.WSOpts.Headers {
|
for key, value := range v.option.WSOpts.Headers {
|
||||||
header.Add(key, value)
|
wsOpts.Headers.Add(key, value)
|
||||||
}
|
}
|
||||||
wsOpts.Headers = header
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.option.TLS {
|
if v.option.TLS {
|
||||||
@ -117,7 +116,9 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
|||||||
wsOpts.TLSConfig.ServerName = host
|
wsOpts.TLSConfig.ServerName = host
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
wsOpts.Headers.Set("Host", convert.RandHost())
|
if wsOpts.Headers.Get("Host") == "" {
|
||||||
|
wsOpts.Headers.Set("Host", convert.RandHost())
|
||||||
|
}
|
||||||
convert.SetUserAgent(wsOpts.Headers)
|
convert.SetUserAgent(wsOpts.Headers)
|
||||||
}
|
}
|
||||||
c, err = vmess.StreamWebsocketConn(c, wsOpts)
|
c, err = vmess.StreamWebsocketConn(c, wsOpts)
|
||||||
@ -138,9 +139,6 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
http.Header(v.option.HTTPOpts.Headers).Set("Host", convert.RandHost())
|
|
||||||
convert.SetUserAgent(v.option.HTTPOpts.Headers)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
host, _, _ := net.SplitHostPort(v.addr)
|
host, _, _ := net.SplitHostPort(v.addr)
|
||||||
|
@ -48,5 +48,6 @@ func tcpKeepAlive(c net.Conn) {
|
|||||||
if tcp, ok := c.(*net.TCPConn); ok {
|
if tcp, ok := c.(*net.TCPConn); ok {
|
||||||
_ = tcp.SetKeepAlive(true)
|
_ = tcp.SetKeepAlive(true)
|
||||||
_ = tcp.SetKeepAlivePeriod(30 * time.Second)
|
_ = tcp.SetKeepAlivePeriod(30 * time.Second)
|
||||||
|
_ = tcp.SetLinger(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,8 +57,9 @@ func ParseProxy(mapping map[string]any, forceCertVerify bool) (C.Proxy, error) {
|
|||||||
case "vmess":
|
case "vmess":
|
||||||
vmessOption := &outbound.VmessOption{
|
vmessOption := &outbound.VmessOption{
|
||||||
HTTPOpts: outbound.HTTPOptions{
|
HTTPOpts: outbound.HTTPOptions{
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
Path: []string{"/"},
|
Path: []string{"/"},
|
||||||
|
Headers: make(map[string][]string),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err = decoder.Decode(mapping, vmessOption)
|
err = decoder.Decode(mapping, vmessOption)
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
|
|
||||||
"github.com/Dreamacro/clash/common/convert"
|
"github.com/Dreamacro/clash/common/convert"
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
|
||||||
types "github.com/Dreamacro/clash/constant/provider"
|
types "github.com/Dreamacro/clash/constant/provider"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -82,13 +81,11 @@ func (h *HTTPVehicle) Read() ([]byte, error) {
|
|||||||
IdleConnTimeout: 90 * time.Second,
|
IdleConnTimeout: 90 * time.Second,
|
||||||
TLSHandshakeTimeout: 10 * time.Second,
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
ExpectContinueTimeout: 1 * time.Second,
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
DialContext: func(ctx context.Context, network, address string) (conn net.Conn, err error) {
|
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
conn, err = dialer.DialContext(ctx, network, address) // with direct
|
if req.URL.Scheme == "https" {
|
||||||
if err != nil {
|
return (&net.Dialer{}).DialContext(ctx, network, address) // forward to tun if tun enabled
|
||||||
// fallback to tun if tun enabled
|
|
||||||
conn, err = (&net.Dialer{Timeout: C.DefaultTCPTimeout}).Dial(network, address)
|
|
||||||
}
|
}
|
||||||
return
|
return dialer.DialContext(ctx, network, address, dialer.WithDirect()) // with direct
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,21 +21,24 @@ func DecodeBase64(buf []byte) ([]byte, error) {
|
|||||||
return dBuf[:n], nil
|
return dBuf[:n], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBase64StringToString decode base64 string to string
|
func DecodeRawBase64(buf []byte) ([]byte, error) {
|
||||||
func DecodeBase64StringToString(s string) (string, error) {
|
dBuf := make([]byte, base64.RawStdEncoding.DecodedLen(len(buf)))
|
||||||
dBuf, err := enc.DecodeString(s)
|
n, err := base64.RawStdEncoding.Decode(dBuf, buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(dBuf), nil
|
return dBuf[:n], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConvertsV2Ray convert V2Ray subscribe proxies data to clash proxies config
|
// ConvertsV2Ray convert V2Ray subscribe proxies data to clash proxies config
|
||||||
func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
||||||
data, err := DecodeBase64(buf)
|
data, err := DecodeBase64(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
data = buf
|
data, err = DecodeRawBase64(buf)
|
||||||
|
if err != nil {
|
||||||
|
data = buf
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
arr := strings.Split(string(data), "\n")
|
arr := strings.Split(string(data), "\n")
|
||||||
|
@ -4,8 +4,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Relay copies between left and right bidirectionally.
|
// Relay copies between left and right bidirectionally.
|
||||||
@ -16,18 +14,14 @@ func Relay(leftConn, rightConn net.Conn) {
|
|||||||
tcpKeepAlive(rightConn)
|
tcpKeepAlive(rightConn)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
buf := pool.Get(pool.RelayBufferSize)
|
|
||||||
// Wrapping to avoid using *net.TCPConn.(ReadFrom)
|
// Wrapping to avoid using *net.TCPConn.(ReadFrom)
|
||||||
// See also https://github.com/Dreamacro/clash/pull/1209
|
// See also https://github.com/Dreamacro/clash/pull/1209
|
||||||
_, err := io.CopyBuffer(WriteOnlyWriter{Writer: leftConn}, ReadOnlyReader{Reader: rightConn}, buf)
|
_, err := io.Copy(WriteOnlyWriter{Writer: leftConn}, ReadOnlyReader{Reader: rightConn})
|
||||||
_ = pool.Put(buf)
|
|
||||||
_ = leftConn.SetReadDeadline(time.Now())
|
_ = leftConn.SetReadDeadline(time.Now())
|
||||||
ch <- err
|
ch <- err
|
||||||
}()
|
}()
|
||||||
|
|
||||||
buf := pool.Get(pool.RelayBufferSize)
|
_, _ = io.Copy(WriteOnlyWriter{Writer: rightConn}, ReadOnlyReader{Reader: leftConn})
|
||||||
_, _ = io.CopyBuffer(WriteOnlyWriter{Writer: rightConn}, ReadOnlyReader{Reader: leftConn}, buf)
|
|
||||||
_ = pool.Put(buf)
|
|
||||||
_ = rightConn.SetReadDeadline(time.Now())
|
_ = rightConn.SetReadDeadline(time.Now())
|
||||||
<-ch
|
<-ch
|
||||||
}
|
}
|
||||||
|
10
go.mod
10
go.mod
@ -18,17 +18,17 @@ require (
|
|||||||
go.uber.org/atomic v1.9.0
|
go.uber.org/atomic v1.9.0
|
||||||
go.uber.org/automaxprocs v1.5.1
|
go.uber.org/automaxprocs v1.5.1
|
||||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
|
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
|
||||||
golang.org/x/exp v0.0.0-20220602145555-4a0574d9293f
|
golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d
|
||||||
golang.org/x/net v0.0.0-20220531201128-c960675eff93
|
golang.org/x/net v0.0.0-20220614195744-fb05da6f9022
|
||||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
|
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
|
golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098
|
||||||
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab
|
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab
|
||||||
golang.org/x/time v0.0.0-20220411224347-583f2d630306
|
golang.org/x/time v0.0.0-20220609170525-579cf78fd858
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20220601130007-6a08d81f6bc4
|
golang.zx2c4.com/wireguard v0.0.0-20220601130007-6a08d81f6bc4
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.4-0.20220328111914-004c22c5647e
|
golang.zx2c4.com/wireguard/windows v0.5.4-0.20220328111914-004c22c5647e
|
||||||
google.golang.org/protobuf v1.28.0
|
google.golang.org/protobuf v1.28.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
gvisor.dev/gvisor v0.0.0-20220601233344-46e478629075
|
gvisor.dev/gvisor v0.0.0-20220614011939-d89c08b0f364
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
22
go.sum
22
go.sum
@ -20,7 +20,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
@ -80,8 +80,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
|
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
|
||||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/exp v0.0.0-20220602145555-4a0574d9293f h1:KK6mxegmt5hGJRcAnEDjSNLxIRhZxDcgwMbcO/lMCRM=
|
golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d h1:vtUKgx8dahOomfFzLREU8nSv25YHnTgLBn4rDnWZdU0=
|
||||||
golang.org/x/exp v0.0.0-20220602145555-4a0574d9293f/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys=
|
golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
|
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||||
@ -97,8 +97,8 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220531201128-c960675eff93 h1:MYimHLfoXEpOhqd/zgoA/uoXzHB86AEky4LAx5ij9xA=
|
golang.org/x/net v0.0.0-20220614195744-fb05da6f9022 h1:0qjDla5xICC2suMtyRH/QqX3B1btXTfNsIt/i4LFgO0=
|
||||||
golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220614195744-fb05da6f9022/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
|
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
|
||||||
@ -123,16 +123,16 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098 h1:PgOr27OhUx2IRqGJ2RxAWI4dJQ7bi9cSrB82uzFzfUA=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab h1:eHo2TTVBaAPw9lDGK2Gb9GyPMXT6g7O63W6sx3ylbzU=
|
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab h1:eHo2TTVBaAPw9lDGK2Gb9GyPMXT6g7O63W6sx3ylbzU=
|
||||||
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
||||||
golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w=
|
golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U=
|
||||||
golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
@ -158,5 +158,5 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gvisor.dev/gvisor v0.0.0-20220601233344-46e478629075 h1:ucwwit0X39HmMQ1iSeNCIXw4g/B8bi+O6TSxxDbPs9E=
|
gvisor.dev/gvisor v0.0.0-20220614011939-d89c08b0f364 h1:D+X2kUQINrsyxdwX71CUWxxQCGRU4tS6gni5WRYLCjs=
|
||||||
gvisor.dev/gvisor v0.0.0-20220601233344-46e478629075/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
|
gvisor.dev/gvisor v0.0.0-20220614011939-d89c08b0f364/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
|
||||||
|
@ -141,6 +141,11 @@ func updateDNS(c *config.DNS, t config.Tun) {
|
|||||||
ProxyServer: c.ProxyServerNameserver,
|
ProxyServer: c.ProxyServerNameserver,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deprecated warnning
|
||||||
|
if cfg.EnhancedMode == C.DNSMapping {
|
||||||
|
log.Warnln("[DNS] %s is deprecated, please use %s instead", cfg.EnhancedMode.String(), C.DNSFakeIP.String())
|
||||||
|
}
|
||||||
|
|
||||||
r := dns.NewResolver(cfg)
|
r := dns.NewResolver(cfg)
|
||||||
pr := dns.NewProxyServerHostResolver(r)
|
pr := dns.NewProxyServerHostResolver(r)
|
||||||
m := dns.NewEnhancer(cfg)
|
m := dns.NewEnhancer(cfg)
|
||||||
|
@ -2,12 +2,13 @@ package tproxy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
)
|
)
|
||||||
|
|
||||||
type packet struct {
|
type packet struct {
|
||||||
lAddr *net.UDPAddr
|
lAddr netip.AddrPort
|
||||||
buf []byte
|
buf []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,21 +18,21 @@ func (c *packet) Data() []byte {
|
|||||||
|
|
||||||
// WriteBack opens a new socket binding `addr` to write UDP packet back
|
// WriteBack opens a new socket binding `addr` to write UDP packet back
|
||||||
func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
||||||
tc, err := dialUDP("udp", addr.(*net.UDPAddr), c.lAddr)
|
tc, err := dialUDP("udp", addr.(*net.UDPAddr).AddrPort(), c.lAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
n = 0
|
n = 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
n, err = tc.Write(b)
|
n, err = tc.Write(b)
|
||||||
tc.Close()
|
_ = tc.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalAddr returns the source IP/Port of UDP Packet
|
// LocalAddr returns the source IP/Port of UDP Packet
|
||||||
func (c *packet) LocalAddr() net.Addr {
|
func (c *packet) LocalAddr() net.Addr {
|
||||||
return c.lAddr
|
return net.UDPAddrFromAddrPort(c.lAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *packet) Drop() {
|
func (c *packet) Drop() {
|
||||||
pool.Put(c.buf)
|
_ = pool.Put(c.buf)
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ func (l *Listener) Close() error {
|
|||||||
|
|
||||||
func (l *Listener) handleTProxy(conn net.Conn, in chan<- C.ConnContext) {
|
func (l *Listener) handleTProxy(conn net.Conn, in chan<- C.ConnContext) {
|
||||||
target := socks5.ParseAddrToSocksAddr(conn.LocalAddr())
|
target := socks5.ParseAddrToSocksAddr(conn.LocalAddr())
|
||||||
conn.(*net.TCPConn).SetKeepAlive(true)
|
_ = conn.(*net.TCPConn).SetKeepAlive(true)
|
||||||
in <- inbound.NewSocket(target, conn, C.TPROXY)
|
in <- inbound.NewSocket(target, conn, C.TPROXY)
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ package tproxy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/inbound"
|
"github.com/Dreamacro/clash/adapter/inbound"
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
@ -58,28 +59,28 @@ func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error)
|
|||||||
oob := make([]byte, 1024)
|
oob := make([]byte, 1024)
|
||||||
for {
|
for {
|
||||||
buf := pool.Get(pool.UDPBufferSize)
|
buf := pool.Get(pool.UDPBufferSize)
|
||||||
n, oobn, _, lAddr, err := c.ReadMsgUDP(buf, oob)
|
n, oobn, _, lAddr, err := c.ReadMsgUDPAddrPort(buf, oob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pool.Put(buf)
|
_ = pool.Put(buf)
|
||||||
if rl.closed {
|
if rl.closed {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
rAddr, err := getOrigDst(oob, oobn)
|
rAddr, err := getOrigDst(oob[:oobn])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
handlePacketConn(l, in, buf[:n], lAddr, rAddr)
|
handlePacketConn(in, buf[:n], lAddr, rAddr)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return rl, nil
|
return rl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlePacketConn(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, lAddr *net.UDPAddr, rAddr *net.UDPAddr) {
|
func handlePacketConn(in chan<- *inbound.PacketAdapter, buf []byte, lAddr, rAddr netip.AddrPort) {
|
||||||
target := socks5.ParseAddrToSocksAddr(rAddr)
|
target := socks5.AddrFromStdAddrPort(rAddr)
|
||||||
pkt := &packet{
|
pkt := &packet{
|
||||||
lAddr: lAddr,
|
lAddr: lAddr,
|
||||||
buf: buf,
|
buf: buf,
|
||||||
|
@ -3,13 +3,14 @@
|
|||||||
package tproxy
|
package tproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -19,7 +20,7 @@ const (
|
|||||||
|
|
||||||
// dialUDP acts like net.DialUDP for transparent proxy.
|
// dialUDP acts like net.DialUDP for transparent proxy.
|
||||||
// It binds to a non-local address(`lAddr`).
|
// It binds to a non-local address(`lAddr`).
|
||||||
func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
|
func dialUDP(network string, lAddr, rAddr netip.AddrPort) (uc *net.UDPConn, err error) {
|
||||||
rSockAddr, err := udpAddrToSockAddr(rAddr)
|
rSockAddr, err := udpAddrToSockAddr(rAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -35,23 +36,25 @@ func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPCo
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(fd)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
|
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
|
||||||
syscall.Close(fd)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
|
if err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
|
||||||
syscall.Close(fd)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = syscall.Bind(fd, lSockAddr); err != nil {
|
if err = syscall.Bind(fd, lSockAddr); err != nil {
|
||||||
syscall.Close(fd)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = syscall.Connect(fd, rSockAddr); err != nil {
|
if err = syscall.Connect(fd, rSockAddr); err != nil {
|
||||||
syscall.Close(fd)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,35 +63,26 @@ func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPCo
|
|||||||
|
|
||||||
c, err := net.FileConn(fdFile)
|
c, err := net.FileConn(fdFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
syscall.Close(fd)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.(*net.UDPConn), nil
|
return c.(*net.UDPConn), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func udpAddrToSockAddr(addr *net.UDPAddr) (syscall.Sockaddr, error) {
|
func udpAddrToSockAddr(addr netip.AddrPort) (syscall.Sockaddr, error) {
|
||||||
switch {
|
if addr.Addr().Is4() {
|
||||||
case addr.IP.To4() != nil:
|
return &syscall.SockaddrInet4{Addr: addr.Addr().As4(), Port: int(addr.Port())}, nil
|
||||||
ip := [4]byte{}
|
|
||||||
copy(ip[:], addr.IP.To4())
|
|
||||||
|
|
||||||
return &syscall.SockaddrInet4{Addr: ip, Port: addr.Port}, nil
|
|
||||||
|
|
||||||
default:
|
|
||||||
ip := [16]byte{}
|
|
||||||
copy(ip[:], addr.IP.To16())
|
|
||||||
|
|
||||||
zoneID, err := strconv.ParseUint(addr.Zone, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
zoneID = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return &syscall.SockaddrInet6{Addr: ip, Port: addr.Port, ZoneId: uint32(zoneID)}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zoneID, err := strconv.ParseUint(addr.Addr().Zone(), 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
zoneID = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return &syscall.SockaddrInet6{Addr: addr.Addr().As16(), Port: int(addr.Port()), ZoneId: uint32(zoneID)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func udpAddrFamily(net string, lAddr, rAddr *net.UDPAddr) int {
|
func udpAddrFamily(net string, lAddr, rAddr netip.AddrPort) int {
|
||||||
switch net[len(net)-1] {
|
switch net[len(net)-1] {
|
||||||
case '4':
|
case '4':
|
||||||
return syscall.AF_INET
|
return syscall.AF_INET
|
||||||
@ -96,29 +90,35 @@ func udpAddrFamily(net string, lAddr, rAddr *net.UDPAddr) int {
|
|||||||
return syscall.AF_INET6
|
return syscall.AF_INET6
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lAddr == nil || lAddr.IP.To4() != nil) && (rAddr == nil || lAddr.IP.To4() != nil) {
|
if lAddr.Addr().Is4() && rAddr.Addr().Is4() {
|
||||||
return syscall.AF_INET
|
return syscall.AF_INET
|
||||||
}
|
}
|
||||||
return syscall.AF_INET6
|
return syscall.AF_INET6
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOrigDst(oob []byte, oobn int) (*net.UDPAddr, error) {
|
func getOrigDst(oob []byte) (netip.AddrPort, error) {
|
||||||
msgs, err := syscall.ParseSocketControlMessage(oob[:oobn])
|
// oob contains socket control messages which we need to parse.
|
||||||
|
scms, err := unix.ParseSocketControlMessage(oob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return netip.AddrPort{}, fmt.Errorf("parse control message: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, msg := range msgs {
|
// retrieve the destination address from the SCM.
|
||||||
if msg.Header.Level == syscall.SOL_IP && msg.Header.Type == syscall.IP_RECVORIGDSTADDR {
|
sa, err := unix.ParseOrigDstAddr(&scms[0])
|
||||||
ip := net.IP(msg.Data[4:8])
|
if err != nil {
|
||||||
port := binary.BigEndian.Uint16(msg.Data[2:4])
|
return netip.AddrPort{}, fmt.Errorf("retrieve destination: %w", err)
|
||||||
return &net.UDPAddr{IP: ip, Port: int(port)}, nil
|
|
||||||
} else if msg.Header.Level == syscall.SOL_IPV6 && msg.Header.Type == IPV6_RECVORIGDSTADDR {
|
|
||||||
ip := net.IP(msg.Data[8:24])
|
|
||||||
port := binary.BigEndian.Uint16(msg.Data[2:4])
|
|
||||||
return &net.UDPAddr{IP: ip, Port: int(port)}, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, errors.New("cannot find origDst")
|
// encode the destination address into a cmsg.
|
||||||
|
var rAddr netip.AddrPort
|
||||||
|
switch v := sa.(type) {
|
||||||
|
case *unix.SockaddrInet4:
|
||||||
|
rAddr = netip.AddrPortFrom(netip.AddrFrom4(v.Addr), uint16(v.Port))
|
||||||
|
case *unix.SockaddrInet6:
|
||||||
|
rAddr = netip.AddrPortFrom(netip.AddrFrom16(v.Addr), uint16(v.Port))
|
||||||
|
default:
|
||||||
|
return netip.AddrPort{}, fmt.Errorf("unsupported address type: %T", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rAddr, nil
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,13 @@ package tproxy
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getOrigDst(oob []byte, oobn int) (*net.UDPAddr, error) {
|
func getOrigDst(oob []byte) (netip.AddrPort, error) {
|
||||||
return nil, errors.New("UDP redir not supported on current platform")
|
return netip.AddrPort{}, errors.New("UDP redir not supported on current platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
|
func dialUDP(network string, lAddr, rAddr netip.AddrPort) (*net.UDPConn, error) {
|
||||||
return nil, errors.New("UDP redir not supported on current platform")
|
return nil, errors.New("UDP redir not supported on current platform")
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
|
|
||||||
|
"gvisor.dev/gvisor/pkg/buffer"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip"
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
|
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
@ -93,23 +95,29 @@ func (e *Endpoint) dispatchLoop(cancel context.CancelFunc) {
|
|||||||
|
|
||||||
mtu := int(e.mtu)
|
mtu := int(e.mtu)
|
||||||
for {
|
for {
|
||||||
data := make([]byte, mtu)
|
data := pool.Get(mtu)
|
||||||
|
|
||||||
n, err := e.rw.Read(data)
|
n, err := e.rw.Read(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
_ = pool.Put(data)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if n == 0 || n > mtu {
|
if n == 0 || n > mtu {
|
||||||
|
_ = pool.Put(data)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !e.IsAttached() {
|
if !e.IsAttached() {
|
||||||
|
_ = pool.Put(data)
|
||||||
continue /* unattached, drop packet */
|
continue /* unattached, drop packet */
|
||||||
}
|
}
|
||||||
|
|
||||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||||
Data: buffer.View(data[:n]).ToVectorisedView(),
|
Payload: buffer.NewWithData(data[:n]),
|
||||||
|
OnRelease: func() {
|
||||||
|
_ = pool.Put(data)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
switch header.IPVersion(data) {
|
switch header.IPVersion(data) {
|
||||||
@ -117,6 +125,8 @@ func (e *Endpoint) dispatchLoop(cancel context.CancelFunc) {
|
|||||||
e.InjectInbound(header.IPv4ProtocolNumber, pkt)
|
e.InjectInbound(header.IPv4ProtocolNumber, pkt)
|
||||||
case header.IPv6Version:
|
case header.IPv6Version:
|
||||||
e.InjectInbound(header.IPv6ProtocolNumber, pkt)
|
e.InjectInbound(header.IPv6ProtocolNumber, pkt)
|
||||||
|
default:
|
||||||
|
_ = pool.Put(data)
|
||||||
}
|
}
|
||||||
pkt.DecRef()
|
pkt.DecRef()
|
||||||
}
|
}
|
||||||
@ -138,11 +148,8 @@ func (e *Endpoint) outboundLoop(ctx context.Context) {
|
|||||||
func (e *Endpoint) writePacket(pkt *stack.PacketBuffer) tcpip.Error {
|
func (e *Endpoint) writePacket(pkt *stack.PacketBuffer) tcpip.Error {
|
||||||
defer pkt.DecRef()
|
defer pkt.DecRef()
|
||||||
|
|
||||||
size := pkt.Size()
|
buf := pkt.Buffer()
|
||||||
views := pkt.Views()
|
if _, err := e.rw.Write(buf.Flatten()); err != nil {
|
||||||
|
|
||||||
vView := buffer.NewVectorisedView(size, views)
|
|
||||||
if _, err := e.rw.Write(vView.ToView()); err != nil {
|
|
||||||
return &tcpip.ErrInvalidEndpointState{}
|
return &tcpip.ErrInvalidEndpointState{}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -87,7 +87,11 @@ func (t *TUN) Write(packet []byte) (int, error) {
|
|||||||
|
|
||||||
packet = append(t.cache[:t.offset], packet...)
|
packet = append(t.cache[:t.offset], packet...)
|
||||||
|
|
||||||
return t.nt.Write(packet, t.offset)
|
n, err := t.nt.Write(packet, t.offset)
|
||||||
|
if n < t.offset {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return n - t.offset, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TUN) Close() error {
|
func (t *TUN) Close() error {
|
||||||
|
@ -2,23 +2,15 @@ package adapter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TCPConn implements the net.Conn interface.
|
// TCPConn implements the net.Conn interface.
|
||||||
type TCPConn interface {
|
type TCPConn interface {
|
||||||
net.Conn
|
net.Conn
|
||||||
|
|
||||||
// ID returns the transport endpoint id of TCPConn.
|
|
||||||
ID() *stack.TransportEndpointID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UDPConn implements net.Conn and net.PacketConn.
|
// UDPConn implements net.Conn and net.PacketConn.
|
||||||
type UDPConn interface {
|
type UDPConn interface {
|
||||||
net.Conn
|
net.Conn
|
||||||
net.PacketConn
|
net.PacketConn
|
||||||
|
|
||||||
// ID returns the transport endpoint id of UDPConn.
|
|
||||||
ID() *stack.TransportEndpointID
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/inbound"
|
"github.com/Dreamacro/clash/adapter/inbound"
|
||||||
"github.com/Dreamacro/clash/common/nnip"
|
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
D "github.com/Dreamacro/clash/listener/tun/ipstack/commons"
|
D "github.com/Dreamacro/clash/listener/tun/ipstack/commons"
|
||||||
@ -27,15 +26,7 @@ type gvHandler struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gh *gvHandler) HandleTCP(tunConn adapter.TCPConn) {
|
func (gh *gvHandler) HandleTCP(tunConn adapter.TCPConn) {
|
||||||
id := tunConn.ID()
|
rAddrPort := tunConn.LocalAddr().(*net.TCPAddr).AddrPort()
|
||||||
|
|
||||||
rAddr := &net.UDPAddr{
|
|
||||||
IP: net.IP(id.LocalAddress),
|
|
||||||
Port: int(id.LocalPort),
|
|
||||||
Zone: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), id.LocalPort)
|
|
||||||
|
|
||||||
if D.ShouldHijackDns(gh.dnsHijack, rAddrPort, "tcp") {
|
if D.ShouldHijackDns(gh.dnsHijack, rAddrPort, "tcp") {
|
||||||
go func() {
|
go func() {
|
||||||
@ -43,8 +34,8 @@ func (gh *gvHandler) HandleTCP(tunConn adapter.TCPConn) {
|
|||||||
|
|
||||||
buf := pool.Get(pool.UDPBufferSize)
|
buf := pool.Get(pool.UDPBufferSize)
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = pool.Put(buf)
|
|
||||||
_ = tunConn.Close()
|
_ = tunConn.Close()
|
||||||
|
_ = pool.Put(buf)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@ -78,26 +69,18 @@ func (gh *gvHandler) HandleTCP(tunConn adapter.TCPConn) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
gh.tcpIn <- inbound.NewSocket(socks5.ParseAddrToSocksAddr(rAddr), tunConn, C.TUN)
|
gh.tcpIn <- inbound.NewSocket(socks5.AddrFromStdAddrPort(rAddrPort), tunConn, C.TUN)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gh *gvHandler) HandleUDP(tunConn adapter.UDPConn) {
|
func (gh *gvHandler) HandleUDP(tunConn adapter.UDPConn) {
|
||||||
id := tunConn.ID()
|
rAddrPort := tunConn.LocalAddr().(*net.UDPAddr).AddrPort()
|
||||||
|
|
||||||
rAddr := &net.UDPAddr{
|
|
||||||
IP: net.IP(id.LocalAddress),
|
|
||||||
Port: int(id.LocalPort),
|
|
||||||
Zone: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), id.LocalPort)
|
|
||||||
|
|
||||||
if rAddrPort.Addr() == gh.gateway {
|
if rAddrPort.Addr() == gh.gateway {
|
||||||
_ = tunConn.Close()
|
_ = tunConn.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
target := socks5.ParseAddrToSocksAddr(rAddr)
|
target := socks5.AddrFromStdAddrPort(rAddrPort)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
@ -109,22 +92,20 @@ func (gh *gvHandler) HandleUDP(tunConn adapter.UDPConn) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
payload := buf[:n]
|
|
||||||
|
|
||||||
if D.ShouldHijackDns(gh.dnsHijack, rAddrPort, "udp") {
|
if D.ShouldHijackDns(gh.dnsHijack, rAddrPort, "udp") {
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = pool.Put(buf)
|
_ = pool.Put(buf)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
msg, err1 := D.RelayDnsPacket(payload)
|
msg, err1 := D.RelayDnsPacket(buf[:n])
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _ = tunConn.WriteTo(msg, addr)
|
_, _ = tunConn.WriteTo(msg, addr)
|
||||||
|
|
||||||
log.Debugln("[TUN] hijack dns udp: %s", rAddr.String())
|
log.Debugln("[TUN] hijack dns udp: %s", rAddrPort.String())
|
||||||
}()
|
}()
|
||||||
|
|
||||||
continue
|
continue
|
||||||
@ -133,12 +114,14 @@ func (gh *gvHandler) HandleUDP(tunConn adapter.UDPConn) {
|
|||||||
gvPacket := &packet{
|
gvPacket := &packet{
|
||||||
pc: tunConn,
|
pc: tunConn,
|
||||||
rAddr: addr,
|
rAddr: addr,
|
||||||
payload: payload,
|
payload: buf,
|
||||||
|
offset: n,
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case gh.udpIn <- inbound.NewPacket(target, gvPacket, C.TUN):
|
case gh.udpIn <- inbound.NewPacket(target, gvPacket, C.TUN):
|
||||||
default:
|
default:
|
||||||
|
gvPacket.Drop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -70,7 +70,6 @@ func withTCPHandler(handle adapter.TCPHandleFunc) option.Option {
|
|||||||
|
|
||||||
conn := &tcpConn{
|
conn := &tcpConn{
|
||||||
TCPConn: gonet.NewTCPConn(&wq, ep),
|
TCPConn: gonet.NewTCPConn(&wq, ep),
|
||||||
id: id,
|
|
||||||
}
|
}
|
||||||
handle(conn)
|
handle(conn)
|
||||||
})
|
})
|
||||||
@ -113,9 +112,4 @@ func setSocketOptions(s *stack.Stack, ep tcpip.Endpoint) tcpip.Error {
|
|||||||
|
|
||||||
type tcpConn struct {
|
type tcpConn struct {
|
||||||
*gonet.TCPConn
|
*gonet.TCPConn
|
||||||
id stack.TransportEndpointID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *tcpConn) ID() *stack.TransportEndpointID {
|
|
||||||
return &c.id
|
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,6 @@ func withUDPHandler(handle adapter.UDPHandleFunc) option.Option {
|
|||||||
|
|
||||||
conn := &udpConn{
|
conn := &udpConn{
|
||||||
UDPConn: gonet.NewUDPConn(s, &wq, ep),
|
UDPConn: gonet.NewUDPConn(s, &wq, ep),
|
||||||
id: id,
|
|
||||||
}
|
}
|
||||||
handle(conn)
|
handle(conn)
|
||||||
})
|
})
|
||||||
@ -40,21 +39,17 @@ func withUDPHandler(handle adapter.UDPHandleFunc) option.Option {
|
|||||||
|
|
||||||
type udpConn struct {
|
type udpConn struct {
|
||||||
*gonet.UDPConn
|
*gonet.UDPConn
|
||||||
id stack.TransportEndpointID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *udpConn) ID() *stack.TransportEndpointID {
|
|
||||||
return &c.id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type packet struct {
|
type packet struct {
|
||||||
pc adapter.UDPConn
|
pc adapter.UDPConn
|
||||||
rAddr net.Addr
|
rAddr net.Addr
|
||||||
payload []byte
|
payload []byte
|
||||||
|
offset int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *packet) Data() []byte {
|
func (c *packet) Data() []byte {
|
||||||
return c.payload
|
return c.payload[:c.offset]
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteBack write UDP packet with source(ip, port) = `addr`
|
// WriteBack write UDP packet with source(ip, port) = `addr`
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
|
||||||
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/tcpip"
|
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/tcpip"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,7 +21,7 @@ func Start(device io.ReadWriter, gateway, portal, broadcast netip.Addr) (*TCP, *
|
|||||||
tab := newTable()
|
tab := newTable()
|
||||||
udp := &UDP{
|
udp := &UDP{
|
||||||
device: device,
|
device: device,
|
||||||
buf: [pool.UDPBufferSize]byte{},
|
buf: [0xffff]byte{},
|
||||||
}
|
}
|
||||||
tcp := &TCP{
|
tcp := &TCP{
|
||||||
listener: listener,
|
listener: listener,
|
||||||
@ -38,7 +37,7 @@ func Start(device io.ReadWriter, gateway, portal, broadcast netip.Addr) (*TCP, *
|
|||||||
_ = udp.Close()
|
_ = udp.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
buf := make([]byte, pool.RelayBufferSize)
|
buf := make([]byte, 0xffff)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
n, err := device.Read(buf)
|
n, err := device.Read(buf)
|
||||||
@ -152,7 +151,7 @@ func Start(device io.ReadWriter, gateway, portal, broadcast netip.Addr) (*TCP, *
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
udp.handleUDPPacket(ip, u)
|
go udp.handleUDPPacket(ip, u)
|
||||||
case tcpip.ICMP:
|
case tcpip.ICMP:
|
||||||
i := tcpip.ICMPPacket(ip.Payload())
|
i := tcpip.ICMPPacket(ip.Payload())
|
||||||
|
|
||||||
|
@ -2,8 +2,11 @@ package nat
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/generics/list"
|
"github.com/Dreamacro/clash/common/generics/list"
|
||||||
|
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -27,6 +30,7 @@ type table struct {
|
|||||||
tuples map[tuple]*list.Element[*binding]
|
tuples map[tuple]*list.Element[*binding]
|
||||||
ports [portLength]*list.Element[*binding]
|
ports [portLength]*list.Element[*binding]
|
||||||
available *list.List[*binding]
|
available *list.List[*binding]
|
||||||
|
mux sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *table) tupleOf(port uint16) tuple {
|
func (t *table) tupleOf(port uint16) tuple {
|
||||||
@ -43,10 +47,13 @@ func (t *table) tupleOf(port uint16) tuple {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *table) portOf(tuple tuple) uint16 {
|
func (t *table) portOf(tuple tuple) uint16 {
|
||||||
|
t.mux.Lock()
|
||||||
elm := t.tuples[tuple]
|
elm := t.tuples[tuple]
|
||||||
if elm == nil {
|
if elm == nil {
|
||||||
|
t.mux.Unlock()
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
t.mux.Unlock()
|
||||||
|
|
||||||
t.available.MoveToFront(elm)
|
t.available.MoveToFront(elm)
|
||||||
|
|
||||||
@ -54,18 +61,40 @@ func (t *table) portOf(tuple tuple) uint16 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *table) newConn(tuple tuple) uint16 {
|
func (t *table) newConn(tuple tuple) uint16 {
|
||||||
elm := t.available.Back()
|
t.mux.Lock()
|
||||||
|
elm := t.availableConn()
|
||||||
b := elm.Value
|
b := elm.Value
|
||||||
|
|
||||||
delete(t.tuples, b.tuple)
|
|
||||||
t.tuples[tuple] = elm
|
t.tuples[tuple] = elm
|
||||||
b.tuple = tuple
|
b.tuple = tuple
|
||||||
|
t.mux.Unlock()
|
||||||
|
|
||||||
t.available.MoveToFront(elm)
|
t.available.MoveToFront(elm)
|
||||||
|
|
||||||
return portBegin + b.offset
|
return portBegin + b.offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *table) availableConn() *list.Element[*binding] {
|
||||||
|
elm := t.available.Back()
|
||||||
|
offset := elm.Value.offset
|
||||||
|
_, ok := t.tuples[t.ports[offset].Value.tuple]
|
||||||
|
if !ok {
|
||||||
|
if offset != 0 && offset%portLength == 0 { // resize
|
||||||
|
tuples := make(map[tuple]*list.Element[*binding], portLength)
|
||||||
|
maps.Copy(tuples, t.tuples)
|
||||||
|
t.tuples = tuples
|
||||||
|
}
|
||||||
|
return elm
|
||||||
|
}
|
||||||
|
t.available.MoveToFront(elm)
|
||||||
|
return t.availableConn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *table) closeConn(tuple tuple) {
|
||||||
|
t.mux.Lock()
|
||||||
|
delete(t.tuples, tuple)
|
||||||
|
t.mux.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
func newTable() *table {
|
func newTable() *table {
|
||||||
result := &table{
|
result := &table{
|
||||||
tuples: make(map[tuple]*list.Element[*binding], portLength),
|
tuples: make(map[tuple]*list.Element[*binding], portLength),
|
||||||
|
@ -16,6 +16,8 @@ type conn struct {
|
|||||||
net.Conn
|
net.Conn
|
||||||
|
|
||||||
tuple tuple
|
tuple tuple
|
||||||
|
|
||||||
|
close func()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TCP) Accept() (net.Conn, error) {
|
func (t *TCP) Accept() (net.Conn, error) {
|
||||||
@ -24,9 +26,9 @@ func (t *TCP) Accept() (net.Conn, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
addr := c.RemoteAddr().(*net.TCPAddr)
|
addr := c.RemoteAddr().(*net.TCPAddr).AddrPort()
|
||||||
tup := t.table.tupleOf(uint16(addr.Port))
|
tup := t.table.tupleOf(addr.Port())
|
||||||
if !addr.IP.Equal(t.portal.AsSlice()) || tup == zeroTuple {
|
if addr.Addr() != t.portal || tup == zeroTuple {
|
||||||
_ = c.Close()
|
_ = c.Close()
|
||||||
|
|
||||||
return nil, net.InvalidAddrError("unknown remote addr")
|
return nil, net.InvalidAddrError("unknown remote addr")
|
||||||
@ -34,9 +36,14 @@ func (t *TCP) Accept() (net.Conn, error) {
|
|||||||
|
|
||||||
addition(c)
|
addition(c)
|
||||||
|
|
||||||
|
_ = c.SetLinger(0)
|
||||||
|
|
||||||
return &conn{
|
return &conn{
|
||||||
Conn: c,
|
Conn: c,
|
||||||
tuple: tup,
|
tuple: tup,
|
||||||
|
close: func() {
|
||||||
|
t.table.closeConn(tup)
|
||||||
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,16 +59,15 @@ func (t *TCP) SetDeadline(time time.Time) error {
|
|||||||
return t.listener.SetDeadline(time)
|
return t.listener.SetDeadline(time)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *conn) Close() error {
|
||||||
|
c.close()
|
||||||
|
return c.Conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
func (c *conn) LocalAddr() net.Addr {
|
func (c *conn) LocalAddr() net.Addr {
|
||||||
return &net.TCPAddr{
|
return net.TCPAddrFromAddrPort(c.tuple.SourceAddr)
|
||||||
IP: c.tuple.SourceAddr.Addr().AsSlice(),
|
|
||||||
Port: int(c.tuple.SourceAddr.Port()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *conn) RemoteAddr() net.Addr {
|
func (c *conn) RemoteAddr() net.Addr {
|
||||||
return &net.TCPAddr{
|
return net.TCPAddrFromAddrPort(c.tuple.DestinationAddr)
|
||||||
IP: c.tuple.DestinationAddr.Addr().AsSlice(),
|
|
||||||
Port: int(c.tuple.DestinationAddr.Port()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,6 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/nnip"
|
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
|
||||||
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/tcpip"
|
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/tcpip"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,8 +14,8 @@ type call struct {
|
|||||||
cond *sync.Cond
|
cond *sync.Cond
|
||||||
buf []byte
|
buf []byte
|
||||||
n int
|
n int
|
||||||
source net.Addr
|
source netip.AddrPort
|
||||||
destination net.Addr
|
destination netip.AddrPort
|
||||||
}
|
}
|
||||||
|
|
||||||
type UDP struct {
|
type UDP struct {
|
||||||
@ -26,10 +24,10 @@ type UDP struct {
|
|||||||
queueLock sync.Mutex
|
queueLock sync.Mutex
|
||||||
queue []*call
|
queue []*call
|
||||||
bufLock sync.Mutex
|
bufLock sync.Mutex
|
||||||
buf [pool.UDPBufferSize]byte
|
buf [0xffff]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UDP) ReadFrom(buf []byte) (int, net.Addr, net.Addr, error) {
|
func (u *UDP) ReadFrom(buf []byte) (int, netip.AddrPort, netip.AddrPort, error) {
|
||||||
u.queueLock.Lock()
|
u.queueLock.Lock()
|
||||||
defer u.queueLock.Unlock()
|
defer u.queueLock.Unlock()
|
||||||
|
|
||||||
@ -38,8 +36,8 @@ func (u *UDP) ReadFrom(buf []byte) (int, net.Addr, net.Addr, error) {
|
|||||||
cond: sync.NewCond(&u.queueLock),
|
cond: sync.NewCond(&u.queueLock),
|
||||||
buf: buf,
|
buf: buf,
|
||||||
n: -1,
|
n: -1,
|
||||||
source: nil,
|
source: netip.AddrPort{},
|
||||||
destination: nil,
|
destination: netip.AddrPort{},
|
||||||
}
|
}
|
||||||
|
|
||||||
u.queue = append(u.queue, c)
|
u.queue = append(u.queue, c)
|
||||||
@ -51,10 +49,10 @@ func (u *UDP) ReadFrom(buf []byte) (int, net.Addr, net.Addr, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1, nil, nil, net.ErrClosed
|
return -1, netip.AddrPort{}, netip.AddrPort{}, net.ErrClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UDP) WriteTo(buf []byte, local net.Addr, remote net.Addr) (int, error) {
|
func (u *UDP) WriteTo(buf []byte, local netip.AddrPort, remote netip.AddrPort) (int, error) {
|
||||||
if u.closed {
|
if u.closed {
|
||||||
return 0, net.ErrClosed
|
return 0, net.ErrClosed
|
||||||
}
|
}
|
||||||
@ -66,16 +64,7 @@ func (u *UDP) WriteTo(buf []byte, local net.Addr, remote net.Addr) (int, error)
|
|||||||
return 0, net.InvalidAddrError("invalid ip version")
|
return 0, net.InvalidAddrError("invalid ip version")
|
||||||
}
|
}
|
||||||
|
|
||||||
srcAddr, srcOk := local.(*net.UDPAddr)
|
if !local.Addr().Is4() || !remote.Addr().Is4() {
|
||||||
dstAddr, dstOk := remote.(*net.UDPAddr)
|
|
||||||
if !srcOk || !dstOk {
|
|
||||||
return 0, net.InvalidAddrError("invalid addr")
|
|
||||||
}
|
|
||||||
|
|
||||||
srcAddrPort := netip.AddrPortFrom(nnip.IpToAddr(srcAddr.IP), uint16(srcAddr.Port))
|
|
||||||
dstAddrPort := netip.AddrPortFrom(nnip.IpToAddr(dstAddr.IP), uint16(dstAddr.Port))
|
|
||||||
|
|
||||||
if !srcAddrPort.Addr().Is4() || !dstAddrPort.Addr().Is4() {
|
|
||||||
return 0, net.InvalidAddrError("invalid ip version")
|
return 0, net.InvalidAddrError("invalid ip version")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,13 +78,13 @@ func (u *UDP) WriteTo(buf []byte, local net.Addr, remote net.Addr) (int, error)
|
|||||||
ip.SetFragmentOffset(0)
|
ip.SetFragmentOffset(0)
|
||||||
ip.SetTimeToLive(64)
|
ip.SetTimeToLive(64)
|
||||||
ip.SetProtocol(tcpip.UDP)
|
ip.SetProtocol(tcpip.UDP)
|
||||||
ip.SetSourceIP(srcAddrPort.Addr())
|
ip.SetSourceIP(local.Addr())
|
||||||
ip.SetDestinationIP(dstAddrPort.Addr())
|
ip.SetDestinationIP(remote.Addr())
|
||||||
|
|
||||||
udp := tcpip.UDPPacket(ip.Payload())
|
udp := tcpip.UDPPacket(ip.Payload())
|
||||||
udp.SetLength(tcpip.UDPHeaderSize + uint16(len(buf)))
|
udp.SetLength(tcpip.UDPHeaderSize + uint16(len(buf)))
|
||||||
udp.SetSourcePort(srcAddrPort.Port())
|
udp.SetSourcePort(local.Port())
|
||||||
udp.SetDestinationPort(dstAddrPort.Port())
|
udp.SetDestinationPort(remote.Port())
|
||||||
copy(udp.Payload(), buf)
|
copy(udp.Payload(), buf)
|
||||||
|
|
||||||
ip.ResetChecksum()
|
ip.ResetChecksum()
|
||||||
@ -131,14 +120,8 @@ func (u *UDP) handleUDPPacket(ip tcpip.IP, pkt tcpip.UDPPacket) {
|
|||||||
u.queueLock.Unlock()
|
u.queueLock.Unlock()
|
||||||
|
|
||||||
if c != nil {
|
if c != nil {
|
||||||
c.source = &net.UDPAddr{
|
c.source = netip.AddrPortFrom(ip.SourceIP(), pkt.SourcePort())
|
||||||
IP: ip.SourceIP().AsSlice(),
|
c.destination = netip.AddrPortFrom(ip.DestinationIP(), pkt.DestinationPort())
|
||||||
Port: int(pkt.SourcePort()),
|
|
||||||
}
|
|
||||||
c.destination = &net.UDPAddr{
|
|
||||||
IP: ip.DestinationIP().AsSlice(),
|
|
||||||
Port: int(pkt.DestinationPort()),
|
|
||||||
}
|
|
||||||
c.n = copy(c.buf, pkt.Payload())
|
c.n = copy(c.buf, pkt.Payload())
|
||||||
c.cond.Signal()
|
c.cond.Signal()
|
||||||
}
|
}
|
||||||
|
@ -81,26 +81,23 @@ func New(device device.Device, dnsHijack []C.DNSUrl, tunAddress netip.Prefix, tc
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
lAddr := conn.LocalAddr().(*net.TCPAddr)
|
lAddr := conn.LocalAddr().(*net.TCPAddr).AddrPort()
|
||||||
rAddr := conn.RemoteAddr().(*net.TCPAddr)
|
rAddr := conn.RemoteAddr().(*net.TCPAddr).AddrPort()
|
||||||
|
|
||||||
lAddrPort := netip.AddrPortFrom(nnip.IpToAddr(lAddr.IP), uint16(lAddr.Port))
|
if rAddr.Addr().IsLoopback() {
|
||||||
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), uint16(rAddr.Port))
|
|
||||||
|
|
||||||
if rAddrPort.Addr().IsLoopback() {
|
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if D.ShouldHijackDns(dnsAddr, rAddrPort, "tcp") {
|
if D.ShouldHijackDns(dnsAddr, rAddr, "tcp") {
|
||||||
go func() {
|
go func() {
|
||||||
log.Debugln("[TUN] hijack dns tcp: %s", rAddrPort.String())
|
log.Debugln("[TUN] hijack dns tcp: %s", rAddr.String())
|
||||||
|
|
||||||
buf := pool.Get(pool.UDPBufferSize)
|
buf := pool.Get(pool.UDPBufferSize)
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = pool.Put(buf)
|
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
|
_ = pool.Put(buf)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@ -137,10 +134,10 @@ func New(device device.Device, dnsHijack []C.DNSUrl, tunAddress netip.Prefix, tc
|
|||||||
metadata := &C.Metadata{
|
metadata := &C.Metadata{
|
||||||
NetWork: C.TCP,
|
NetWork: C.TCP,
|
||||||
Type: C.TUN,
|
Type: C.TUN,
|
||||||
SrcIP: lAddrPort.Addr(),
|
SrcIP: lAddr.Addr(),
|
||||||
DstIP: rAddrPort.Addr(),
|
DstIP: rAddr.Addr(),
|
||||||
SrcPort: strconv.Itoa(lAddr.Port),
|
SrcPort: strconv.FormatUint(uint64(lAddr.Port()), 10),
|
||||||
DstPort: strconv.Itoa(rAddr.Port),
|
DstPort: strconv.FormatUint(uint64(rAddr.Port()), 10),
|
||||||
AddrType: C.AtypIPv4,
|
AddrType: C.AtypIPv4,
|
||||||
Host: "",
|
Host: "",
|
||||||
}
|
}
|
||||||
@ -159,56 +156,50 @@ func New(device device.Device, dnsHijack []C.DNSUrl, tunAddress netip.Prefix, tc
|
|||||||
for !ipStack.closed {
|
for !ipStack.closed {
|
||||||
buf := pool.Get(pool.UDPBufferSize)
|
buf := pool.Get(pool.UDPBufferSize)
|
||||||
|
|
||||||
n, lRAddr, rRAddr, err := stack.UDP().ReadFrom(buf)
|
n, lAddr, rAddr, err := stack.UDP().ReadFrom(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = pool.Put(buf)
|
_ = pool.Put(buf)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
raw := buf[:n]
|
if rAddr.Addr().IsLoopback() || rAddr.Addr() == gateway {
|
||||||
lAddr := lRAddr.(*net.UDPAddr)
|
|
||||||
rAddr := rRAddr.(*net.UDPAddr)
|
|
||||||
|
|
||||||
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), uint16(rAddr.Port))
|
|
||||||
|
|
||||||
if rAddrPort.Addr().IsLoopback() || rAddrPort.Addr() == gateway {
|
|
||||||
_ = pool.Put(buf)
|
_ = pool.Put(buf)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if D.ShouldHijackDns(dnsAddr, rAddrPort, "udp") {
|
if D.ShouldHijackDns(dnsAddr, rAddr, "udp") {
|
||||||
go func() {
|
go func() {
|
||||||
msg, err := D.RelayDnsPacket(raw)
|
defer func() {
|
||||||
if err != nil {
|
|
||||||
_ = pool.Put(buf)
|
_ = pool.Put(buf)
|
||||||
|
}()
|
||||||
|
|
||||||
|
msg, err := D.RelayDnsPacket(buf[:n])
|
||||||
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _ = stack.UDP().WriteTo(msg, rAddr, lAddr)
|
_, _ = stack.UDP().WriteTo(msg, rAddr, lAddr)
|
||||||
|
|
||||||
_ = pool.Put(buf)
|
log.Debugln("[TUN] hijack dns udp: %s", rAddr.String())
|
||||||
|
|
||||||
log.Debugln("[TUN] hijack dns udp: %s", rAddrPort.String())
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
pkt := &packet{
|
pkt := &packet{
|
||||||
local: lAddr,
|
local: lAddr,
|
||||||
data: raw,
|
data: buf,
|
||||||
|
offset: n,
|
||||||
writeBack: func(b []byte, addr net.Addr) (int, error) {
|
writeBack: func(b []byte, addr net.Addr) (int, error) {
|
||||||
return stack.UDP().WriteTo(b, rAddr, lAddr)
|
return stack.UDP().WriteTo(b, rAddr, lAddr)
|
||||||
},
|
},
|
||||||
drop: func() {
|
|
||||||
_ = pool.Put(buf)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case udpIn <- inbound.NewPacket(socks5.ParseAddrToSocksAddr(rAddr), pkt, C.TUN):
|
case udpIn <- inbound.NewPacket(socks5.AddrFromStdAddrPort(rAddr), pkt, C.TUN):
|
||||||
default:
|
default:
|
||||||
|
pkt.Drop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
package system
|
package system
|
||||||
|
|
||||||
import "net"
|
import (
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
|
)
|
||||||
|
|
||||||
type packet struct {
|
type packet struct {
|
||||||
local *net.UDPAddr
|
local netip.AddrPort
|
||||||
data []byte
|
data []byte
|
||||||
|
offset int
|
||||||
writeBack func(b []byte, addr net.Addr) (int, error)
|
writeBack func(b []byte, addr net.Addr) (int, error)
|
||||||
drop func()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pkt *packet) Data() []byte {
|
func (pkt *packet) Data() []byte {
|
||||||
return pkt.data
|
return pkt.data[:pkt.offset]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pkt *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
func (pkt *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
||||||
@ -18,9 +23,9 @@ func (pkt *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pkt *packet) Drop() {
|
func (pkt *packet) Drop() {
|
||||||
pkt.drop()
|
_ = pool.Put(pkt.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pkt *packet) LocalAddr() net.Addr {
|
func (pkt *packet) LocalAddr() net.Addr {
|
||||||
return pkt.local
|
return net.UDPAddrFromAddrPort(pkt.local)
|
||||||
}
|
}
|
||||||
|
@ -51,11 +51,11 @@ func NewGEOSITE(country string, adapter string) (*GEOSITE, error) {
|
|||||||
return nil, fmt.Errorf("load GeoSite data error, %s", err.Error())
|
return nil, fmt.Errorf("load GeoSite data error, %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
cont := fmt.Sprintf("%d", recordsCount)
|
count := fmt.Sprintf("%d", recordsCount)
|
||||||
if recordsCount == 0 {
|
if recordsCount == 0 {
|
||||||
cont = "from cache"
|
count = "from cache"
|
||||||
}
|
}
|
||||||
log.Infoln("Start initial GeoSite rule %s => %s, records: %s", country, adapter, cont)
|
log.Infoln("Start initial GeoSite rule %s => %s, records: %s", country, adapter, count)
|
||||||
|
|
||||||
geoSite := &GEOSITE{
|
geoSite := &GEOSITE{
|
||||||
Base: &Base{},
|
Base: &Base{},
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/auth"
|
"github.com/Dreamacro/clash/component/auth"
|
||||||
@ -398,6 +399,21 @@ func ParseAddrToSocksAddr(addr net.Addr) Addr {
|
|||||||
return parsed
|
return parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AddrFromStdAddrPort(addrPort netip.AddrPort) Addr {
|
||||||
|
addr := addrPort.Addr()
|
||||||
|
if addr.Is4() {
|
||||||
|
ip4 := addr.As4()
|
||||||
|
return []byte{AtypIPv4, ip4[0], ip4[1], ip4[2], ip4[3], byte(addrPort.Port() >> 8), byte(addrPort.Port())}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 1+net.IPv6len+2)
|
||||||
|
buf[0] = AtypIPv6
|
||||||
|
copy(buf[1:], addr.AsSlice())
|
||||||
|
buf[1+net.IPv6len] = byte(addrPort.Port() >> 8)
|
||||||
|
buf[1+net.IPv6len+1] = byte(addrPort.Port())
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
// DecodeUDPPacket split `packet` to addr payload, and this function is mutable with `packet`
|
// DecodeUDPPacket split `packet` to addr payload, and this function is mutable with `packet`
|
||||||
func DecodeUDPPacket(packet []byte) (addr Addr, payload []byte, err error) {
|
func DecodeUDPPacket(packet []byte) (addr Addr, payload []byte, err error) {
|
||||||
if len(packet) < 5 {
|
if len(packet) < 5 {
|
||||||
|
@ -32,19 +32,21 @@ func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// reset timeout
|
// reset timeout
|
||||||
pc.SetReadDeadline(time.Now().Add(udpTimeout))
|
_ = pc.SetReadDeadline(time.Now().Add(udpTimeout))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleUDPToLocal(packet C.UDPPacket, pc net.PacketConn, key string, fAddr net.Addr) {
|
func handleUDPToLocal(packet C.UDPPacket, pc net.PacketConn, key string, fAddr net.Addr) {
|
||||||
buf := pool.Get(pool.UDPBufferSize)
|
buf := pool.Get(pool.UDPBufferSize)
|
||||||
defer pool.Put(buf)
|
defer func() {
|
||||||
defer natTable.Delete(key)
|
_ = pc.Close()
|
||||||
defer pc.Close()
|
natTable.Delete(key)
|
||||||
|
_ = pool.Put(buf)
|
||||||
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
pc.SetReadDeadline(time.Now().Add(udpTimeout))
|
_ = pc.SetReadDeadline(time.Now().Add(udpTimeout))
|
||||||
n, from, err := pc.ReadFrom(buf)
|
n, from, err := pc.ReadFrom(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
Reference in New Issue
Block a user