Compare commits

..

16 Commits

Author SHA1 Message Date
2516169f61 Chore: update dependencies 2022-08-26 21:18:16 +08:00
a3281712e2 Chore: reduce dhcp dns client cost 2022-08-24 21:36:19 +08:00
bf079742cb Clean: use go 1.19 Appendf 2022-08-24 20:21:06 +08:00
6e058f8581 Chore: remove old cache implementation 2022-08-17 11:43:20 +08:00
3946d771e5 Feature: sync missing resolver logic from premium, but still net.IP on opensource 2022-08-13 13:07:35 +08:00
5940f62794 Chore: http2 should use DialTLSContext and some tls handshake should with context 2022-08-13 12:35:39 +08:00
71cad51e8f Fix: satisfy RFC4343 - DNS case insensitivity (#2260) 2022-08-12 13:47:51 +08:00
50105f0559 Migration: go1.19 2022-08-07 21:45:50 +08:00
6648793e40 Chore: reenable latest golangci-lint 2022-08-05 10:52:36 +08:00
95e3a88608 Chore: update bug_report.yml (#2240) 2022-07-28 20:27:53 +08:00
bec4df7b12 Fix: handle parse socks5 udp address properly (#2220) 2022-07-25 12:44:00 +08:00
93400cf44d Fix: ALPN should on DoH instead of DoT (#2232) 2022-07-25 12:41:22 +08:00
a794819869 Chore: upgrade actions and fixed golangci-lint version 2022-07-21 15:15:14 +08:00
be8d63ba8f Fix: macOS udp find process should use unspecified fallback 2022-07-15 17:00:41 +08:00
3b90e18047 Chore: update test dependencies 2022-07-15 16:07:18 +08:00
f0952b55d0 Fix: query string parse on ws-opts (#2213) 2022-07-10 14:56:34 +08:00
39 changed files with 363 additions and 419 deletions

View File

@ -56,8 +56,8 @@ This is an issue of the Clash core *per se*, not to the derivatives of Clash, li
render: yaml
label: "Clash config"
description: "
在下方附上 Clash core 脱敏后配置文件的内容
Paste the Clash core configuration below.
在下方附上 Clash core 配置文件,请确保配置文件中没有敏感信息(比如:服务器地址,密码,端口等)
Paste the Clash core configuration file below, please make sure that there is no sensitive information in the configuration file (e.g., server address/url, password, port)
"
validations:
required: true

View File

@ -19,12 +19,12 @@ jobs:
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v1
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v2

View File

@ -18,24 +18,24 @@ jobs:
fetch-depth: 0
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v2
with:
platforms: all
- name: Set up docker buildx
id: buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2
with:
version: latest
- name: Login to DockerHub
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to Github Package
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
registry: ghcr.io
username: Dreamacro
@ -43,7 +43,7 @@ jobs:
- name: Build dev branch and push
if: github.ref == 'refs/heads/dev'
uses: docker/build-push-action@v2
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
@ -70,7 +70,7 @@ jobs:
- name: Build release and push
if: startsWith(github.ref, 'refs/tags/')
uses: docker/build-push-action@v2
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64

View File

@ -6,15 +6,11 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Get latest go version
id: version
run: |
echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g')
- name: Setup Go
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: ${{ steps.version.outputs.go_version }}
check-latest: true
go-version: '1.19'
- name: golangci-lint
uses: golangci/golangci-lint-action@v3

View File

@ -4,15 +4,11 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Get latest go version
id: version
run: |
echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g')
- name: Setup Go
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: ${{ steps.version.outputs.go_version }}
check-latest: true
go-version: '1.19'
- name: Check out code into the Go module directory
uses: actions/checkout@v3

View File

@ -8,9 +8,10 @@ linters:
linters-settings:
gci:
custom-order: true
sections:
- standard
- prefix(github.com/Dreamacro/clash)
- default
staticcheck:
go: '1.18'
go: '1.19'

View File

@ -40,7 +40,9 @@ type HttpOption struct {
func (h *Http) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
if h.tlsConfig != nil {
cc := tls.Client(c, h.tlsConfig)
err := cc.Handshake()
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
defer cancel()
err := cc.HandshakeContext(ctx)
c = cc
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", h.addr, err)

View File

@ -39,7 +39,9 @@ type Socks5Option struct {
func (ss *Socks5) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
if ss.tls {
cc := tls.Client(c, ss.tlsConfig)
err := cc.Handshake()
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
defer cancel()
err := cc.HandshakeContext(ctx)
c = cc
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
@ -87,7 +89,9 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
if ss.tls {
cc := tls.Client(c, ss.tlsConfig)
err = cc.Handshake()
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
defer cancel()
err = cc.HandshakeContext(ctx)
c = cc
}

106
common/cache/cache.go vendored
View File

@ -1,106 +0,0 @@
package cache
import (
"runtime"
"sync"
"time"
)
// Cache store element with a expired time
type Cache struct {
*cache
}
type cache struct {
mapping sync.Map
janitor *janitor
}
type element struct {
Expired time.Time
Payload any
}
// Put element in Cache with its ttl
func (c *cache) Put(key any, payload any, ttl time.Duration) {
c.mapping.Store(key, &element{
Payload: payload,
Expired: time.Now().Add(ttl),
})
}
// Get element in Cache, and drop when it expired
func (c *cache) Get(key any) any {
item, exist := c.mapping.Load(key)
if !exist {
return nil
}
elm := item.(*element)
// expired
if time.Since(elm.Expired) > 0 {
c.mapping.Delete(key)
return nil
}
return elm.Payload
}
// GetWithExpire element in Cache with Expire Time
func (c *cache) GetWithExpire(key any) (payload any, expired time.Time) {
item, exist := c.mapping.Load(key)
if !exist {
return
}
elm := item.(*element)
// expired
if time.Since(elm.Expired) > 0 {
c.mapping.Delete(key)
return
}
return elm.Payload, elm.Expired
}
func (c *cache) cleanup() {
c.mapping.Range(func(k, v any) bool {
key := k.(string)
elm := v.(*element)
if time.Since(elm.Expired) > 0 {
c.mapping.Delete(key)
}
return true
})
}
type janitor struct {
interval time.Duration
stop chan struct{}
}
func (j *janitor) process(c *cache) {
ticker := time.NewTicker(j.interval)
for {
select {
case <-ticker.C:
c.cleanup()
case <-j.stop:
ticker.Stop()
return
}
}
}
func stopJanitor(c *Cache) {
c.janitor.stop <- struct{}{}
}
// New return *Cache
func New(interval time.Duration) *Cache {
j := &janitor{
interval: interval,
stop: make(chan struct{}),
}
c := &cache{janitor: j}
go j.process(c)
C := &Cache{c}
runtime.SetFinalizer(C, stopJanitor)
return C
}

View File

@ -1,70 +0,0 @@
package cache
import (
"runtime"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestCache_Basic(t *testing.T) {
interval := 200 * time.Millisecond
ttl := 20 * time.Millisecond
c := New(interval)
c.Put("int", 1, ttl)
c.Put("string", "a", ttl)
i := c.Get("int")
assert.Equal(t, i.(int), 1, "should recv 1")
s := c.Get("string")
assert.Equal(t, s.(string), "a", "should recv 'a'")
}
func TestCache_TTL(t *testing.T) {
interval := 200 * time.Millisecond
ttl := 20 * time.Millisecond
now := time.Now()
c := New(interval)
c.Put("int", 1, ttl)
c.Put("int2", 2, ttl)
i := c.Get("int")
_, expired := c.GetWithExpire("int2")
assert.Equal(t, i.(int), 1, "should recv 1")
assert.True(t, now.Before(expired))
time.Sleep(ttl * 2)
i = c.Get("int")
j, _ := c.GetWithExpire("int2")
assert.Nil(t, i, "should recv nil")
assert.Nil(t, j, "should recv nil")
}
func TestCache_AutoCleanup(t *testing.T) {
interval := 10 * time.Millisecond
ttl := 15 * time.Millisecond
c := New(interval)
c.Put("int", 1, ttl)
time.Sleep(ttl * 2)
i := c.Get("int")
j, _ := c.GetWithExpire("int")
assert.Nil(t, i, "should recv nil")
assert.Nil(t, j, "should recv nil")
}
func TestCache_AutoGC(t *testing.T) {
sign := make(chan struct{})
go func() {
interval := 10 * time.Millisecond
ttl := 15 * time.Millisecond
c := New(interval)
c.Put("int", 1, ttl)
sign <- struct{}{}
}()
<-sign
runtime.GC()
}

View File

@ -64,8 +64,8 @@ type LruCache struct {
onEvict EvictCallback
}
// NewLRUCache creates an LruCache
func NewLRUCache(options ...Option) *LruCache {
// New creates an LruCache
func New(options ...Option) *LruCache {
lc := &LruCache{
lru: list.New(),
cache: make(map[any]*list.Element),

View File

@ -19,7 +19,7 @@ var entries = []struct {
}
func TestLRUCache(t *testing.T) {
c := NewLRUCache()
c := New()
for _, e := range entries {
c.Set(e.key, e.value)
@ -45,7 +45,7 @@ func TestLRUCache(t *testing.T) {
}
func TestLRUMaxAge(t *testing.T) {
c := NewLRUCache(WithAge(86400))
c := New(WithAge(86400))
now := time.Now().Unix()
expected := now + 86400
@ -88,7 +88,7 @@ func TestLRUMaxAge(t *testing.T) {
}
func TestLRUpdateOnGet(t *testing.T) {
c := NewLRUCache(WithAge(86400), WithUpdateAgeOnGet())
c := New(WithAge(86400), WithUpdateAgeOnGet())
now := time.Now().Unix()
expires := now + 86400/2
@ -103,7 +103,7 @@ func TestLRUpdateOnGet(t *testing.T) {
}
func TestMaxSize(t *testing.T) {
c := NewLRUCache(WithSize(2))
c := New(WithSize(2))
// Add one expired entry
c.Set("foo", "bar")
_, ok := c.Get("foo")
@ -117,7 +117,7 @@ func TestMaxSize(t *testing.T) {
}
func TestExist(t *testing.T) {
c := NewLRUCache(WithSize(1))
c := New(WithSize(1))
c.Set(1, 2)
assert.True(t, c.Exist(1))
c.Set(2, 3)
@ -130,7 +130,7 @@ func TestEvict(t *testing.T) {
temp = key.(int) + value.(int)
}
c := NewLRUCache(WithEvict(evict), WithSize(1))
c := New(WithEvict(evict), WithSize(1))
c.Set(1, 2)
c.Set(2, 3)
@ -138,7 +138,7 @@ func TestEvict(t *testing.T) {
}
func TestSetWithExpire(t *testing.T) {
c := NewLRUCache(WithAge(1))
c := New(WithAge(1))
now := time.Now().Unix()
tenSecBefore := time.Unix(now-10, 0)
@ -152,7 +152,7 @@ func TestSetWithExpire(t *testing.T) {
}
func TestStale(t *testing.T) {
c := NewLRUCache(WithAge(1), WithStale(true))
c := New(WithAge(1), WithStale(true))
now := time.Now().Unix()
tenSecBefore := time.Unix(now-10, 0)
@ -165,11 +165,11 @@ func TestStale(t *testing.T) {
}
func TestCloneTo(t *testing.T) {
o := NewLRUCache(WithSize(10))
o := New(WithSize(10))
o.Set("1", 1)
o.Set("2", 2)
n := NewLRUCache(WithSize(2))
n := New(WithSize(2))
n.Set("3", 3)
n.Set("4", 4)

View File

@ -3,6 +3,7 @@ package fakeip
import (
"errors"
"net"
"strings"
"sync"
"github.com/Dreamacro/clash/common/cache"
@ -36,6 +37,9 @@ type Pool struct {
func (p *Pool) Lookup(host string) net.IP {
p.mux.Lock()
defer p.mux.Unlock()
// RFC4343: DNS Case Insensitive, we SHOULD return result with all cases.
host = strings.ToLower(host)
if ip, exist := p.store.GetByHost(host); exist {
return ip
}
@ -164,7 +168,7 @@ func New(options Options) (*Pool, error) {
}
} else {
pool.store = &memoryStore{
cache: cache.NewLRUCache(cache.WithSize(options.Size * 2)),
cache: cache.New(cache.WithSize(options.Size * 2)),
}
}

View File

@ -75,6 +75,27 @@ func TestPool_Basic(t *testing.T) {
}
}
func TestPool_Case_Insensitive(t *testing.T) {
_, ipnet, _ := net.ParseCIDR("192.168.0.1/29")
pools, tempfile, err := createPools(Options{
IPNet: ipnet,
Size: 10,
})
assert.Nil(t, err)
defer os.Remove(tempfile)
for _, pool := range pools {
first := pool.Lookup("foo.com")
last := pool.Lookup("Foo.Com")
foo, exist := pool.LookBack(last)
assert.True(t, first.Equal(pool.Lookup("Foo.Com")))
assert.Equal(t, pool.Lookup("fOo.cOM"), first)
assert.True(t, exist)
assert.Equal(t, foo, "foo.com")
}
}
func TestPool_CycleUsed(t *testing.T) {
_, ipnet, _ := net.ParseCIDR("192.168.0.1/29")
pools, tempfile, err := createPools(Options{

View File

@ -44,6 +44,8 @@ func findProcessName(network string, ip net.IP, port int) (string, error) {
// rup8(sizeof(xtcpcb_n))
itemSize += 208
}
var fallbackUDPProcess string
// skip the first xinpgen(24 bytes) block
for i := 24; i+itemSize <= len(buf); i += itemSize {
// offset of xinpcb_n and xsocket_n
@ -57,11 +59,15 @@ func findProcessName(network string, ip net.IP, port int) (string, error) {
// xinpcb_n.inp_vflag
flag := buf[inp+44]
var srcIP net.IP
var (
srcIP net.IP
srcIsIPv4 bool
)
switch {
case flag&0x1 > 0 && isIPv4:
// ipv4
srcIP = net.IP(buf[inp+76 : inp+80])
srcIsIPv4 = true
case flag&0x2 > 0 && !isIPv4:
// ipv6
srcIP = net.IP(buf[inp+64 : inp+80])
@ -69,13 +75,20 @@ func findProcessName(network string, ip net.IP, port int) (string, error) {
continue
}
if !ip.Equal(srcIP) {
continue
if ip.Equal(srcIP) {
// xsocket_n.so_last_pid
pid := readNativeUint32(buf[so+68 : so+72])
return getExecPathFromPID(pid)
}
// xsocket_n.so_last_pid
pid := readNativeUint32(buf[so+68 : so+72])
return getExecPathFromPID(pid)
// udp packet connection may be not equal with srcIP
if network == UDP && srcIP.IsUnspecified() && isIPv4 == srcIsIPv4 {
fallbackUDPProcess, _ = getExecPathFromPID(readNativeUint32(buf[so+68 : so+72]))
}
}
if network == UDP && fallbackUDPProcess != "" {
return fallbackUDPProcess, nil
}
return "", ErrNotFound

View File

@ -166,7 +166,7 @@ func resolveProcessNameByProcSearch(inode, uid int32) (string, error) {
}
buffer := make([]byte, syscall.PathMax)
socket := []byte(fmt.Sprintf("socket:[%d]", inode))
socket := fmt.Appendf(nil, "socket:[%d]", inode)
for _, f := range files {
if !f.IsDir() || !isPid(f.Name()) {

View File

@ -3,6 +3,7 @@ package resolver
import (
"context"
"errors"
"fmt"
"math/rand"
"net"
"strings"
@ -33,29 +34,32 @@ var (
)
type Resolver interface {
LookupIP(ctx context.Context, host string) ([]net.IP, error)
LookupIPv4(ctx context.Context, host string) ([]net.IP, error)
LookupIPv6(ctx context.Context, host string) ([]net.IP, error)
ResolveIP(host string) (ip net.IP, err error)
ResolveIPv4(host string) (ip net.IP, err error)
ResolveIPv6(host string) (ip net.IP, err error)
}
// ResolveIPv4 with a host, return ipv4
func ResolveIPv4(host string) (net.IP, error) {
// LookupIPv4 with a host, return ipv4 list
func LookupIPv4(ctx context.Context, host string) ([]net.IP, error) {
if node := DefaultHosts.Search(host); node != nil {
if ip := node.Data.(net.IP).To4(); ip != nil {
return ip, nil
return []net.IP{ip}, nil
}
}
ip := net.ParseIP(host)
if ip != nil {
if !strings.Contains(host, ":") {
return ip, nil
return []net.IP{ip}, nil
}
return nil, ErrIPVersion
}
if DefaultResolver != nil {
return DefaultResolver.ResolveIPv4(host)
return DefaultResolver.LookupIPv4(ctx, host)
}
ctx, cancel := context.WithTimeout(context.Background(), DefaultDNSTimeout)
@ -67,31 +71,42 @@ func ResolveIPv4(host string) (net.IP, error) {
return nil, ErrIPNotFound
}
return ipAddrs[rand.Intn(len(ipAddrs))], nil
return ipAddrs, nil
}
// ResolveIPv6 with a host, return ipv6
func ResolveIPv6(host string) (net.IP, error) {
// ResolveIPv4 with a host, return ipv4
func ResolveIPv4(host string) (net.IP, error) {
ips, err := LookupIPv4(context.Background(), host)
if err != nil {
return nil, err
} else if len(ips) == 0 {
return nil, fmt.Errorf("%w: %s", ErrIPNotFound, host)
}
return ips[rand.Intn(len(ips))], nil
}
// LookupIPv6 with a host, return ipv6 list
func LookupIPv6(ctx context.Context, host string) ([]net.IP, error) {
if DisableIPv6 {
return nil, ErrIPv6Disabled
}
if node := DefaultHosts.Search(host); node != nil {
if ip := node.Data.(net.IP).To16(); ip != nil {
return ip, nil
return []net.IP{ip}, nil
}
}
ip := net.ParseIP(host)
if ip != nil {
if strings.Contains(host, ":") {
return ip, nil
return []net.IP{ip}, nil
}
return nil, ErrIPVersion
}
if DefaultResolver != nil {
return DefaultResolver.ResolveIPv6(host)
return DefaultResolver.LookupIPv6(ctx, host)
}
ctx, cancel := context.WithTimeout(context.Background(), DefaultDNSTimeout)
@ -103,38 +118,62 @@ func ResolveIPv6(host string) (net.IP, error) {
return nil, ErrIPNotFound
}
return ipAddrs[rand.Intn(len(ipAddrs))], nil
return ipAddrs, nil
}
// ResolveIPWithResolver same as ResolveIP, but with a resolver
func ResolveIPWithResolver(host string, r Resolver) (net.IP, error) {
// ResolveIPv6 with a host, return ipv6
func ResolveIPv6(host string) (net.IP, error) {
ips, err := LookupIPv6(context.Background(), host)
if err != nil {
return nil, err
} else if len(ips) == 0 {
return nil, fmt.Errorf("%w: %s", ErrIPNotFound, host)
}
return ips[rand.Intn(len(ips))], nil
}
// LookupIPWithResolver same as ResolveIP, but with a resolver
func LookupIPWithResolver(ctx context.Context, host string, r Resolver) ([]net.IP, error) {
if node := DefaultHosts.Search(host); node != nil {
return node.Data.(net.IP), nil
return []net.IP{node.Data.(net.IP)}, nil
}
if r != nil {
if DisableIPv6 {
return r.ResolveIPv4(host)
return r.LookupIPv4(ctx, host)
}
return r.ResolveIP(host)
return r.LookupIP(ctx, host)
} else if DisableIPv6 {
return ResolveIPv4(host)
return LookupIPv4(ctx, host)
}
ip := net.ParseIP(host)
if ip != nil {
return ip, nil
return []net.IP{ip}, nil
}
ipAddr, err := net.ResolveIPAddr("ip", host)
ips, err := net.DefaultResolver.LookupIP(ctx, "ip", host)
if err != nil {
return nil, err
} else if len(ips) == 0 {
return nil, ErrIPNotFound
}
return ipAddr.IP, nil
return ips, nil
}
// ResolveIP with a host, return ip
func LookupIP(ctx context.Context, host string) ([]net.IP, error) {
return LookupIPWithResolver(ctx, host, DefaultResolver)
}
// ResolveIP with a host, return ip
func ResolveIP(host string) (net.IP, error) {
return ResolveIPWithResolver(host, DefaultResolver)
ips, err := LookupIP(context.Background(), host)
if err != nil {
return nil, err
} else if len(ips) == 0 {
return nil, fmt.Errorf("%w: %s", ErrIPNotFound, host)
}
return ips[rand.Intn(len(ips))], nil
}

View File

@ -1,16 +0,0 @@
package mime
import (
"mime"
)
var consensusMimes = map[string]string{
// rfc4329: text/javascript is obsolete, so we need to overwrite mime's builtin
".js": "application/javascript; charset=utf-8",
}
func init() {
for ext, typ := range consensusMimes {
mime.AddExtensionType(ext, typ)
}
}

View File

@ -4,6 +4,7 @@ import (
"context"
"crypto/tls"
"fmt"
"math/rand"
"net"
"strings"
@ -36,9 +37,13 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
return nil, fmt.Errorf("dns %s not a valid ip", c.host)
}
} else {
if ip, err = resolver.ResolveIPWithResolver(c.host, c.r); err != nil {
ips, err := resolver.LookupIPWithResolver(ctx, c.host, c.r)
if err != nil {
return nil, fmt.Errorf("use default dns resolve failed: %w", err)
} else if len(ips) == 0 {
return nil, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, c.host)
}
ip = ips[rand.Intn(len(ips))]
}
network := "udp"

View File

@ -29,7 +29,7 @@ type dhcpClient struct {
ifaceAddr *net.IPNet
done chan struct{}
resolver *Resolver
clients []dnsClient
err error
}
@ -41,15 +41,15 @@ func (d *dhcpClient) Exchange(m *D.Msg) (msg *D.Msg, err error) {
}
func (d *dhcpClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {
res, err := d.resolve(ctx)
clients, err := d.resolve(ctx)
if err != nil {
return nil, err
}
return res.ExchangeContext(ctx, m)
return batchExchange(ctx, clients, m)
}
func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) {
func (d *dhcpClient) resolve(ctx context.Context) ([]dnsClient, error) {
d.lock.Lock()
invalidated, err := d.invalidate()
@ -64,8 +64,9 @@ func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) {
ctx, cancel := context.WithTimeout(context.Background(), DHCPTimeout)
defer cancel()
var res *Resolver
var res []dnsClient
dns, err := dhcp.ResolveDNSFromDHCP(ctx, d.ifaceName)
// dns never empty if err is nil
if err == nil {
nameserver := make([]NameServer, 0, len(dns))
for _, item := range dns {
@ -75,9 +76,7 @@ func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) {
})
}
res = NewResolver(Config{
Main: nameserver,
})
res = transform(nameserver, nil)
}
d.lock.Lock()
@ -86,7 +85,7 @@ func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) {
close(done)
d.done = nil
d.resolver = res
d.clients = res
d.err = err
}()
}
@ -96,7 +95,7 @@ func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) {
for {
d.lock.Lock()
res, err, done := d.resolver, d.err, d.done
res, err, done := d.clients, d.err, d.done
d.lock.Unlock()

View File

@ -3,7 +3,10 @@ package dns
import (
"bytes"
"context"
"crypto/tls"
"fmt"
"io"
"math/rand"
"net"
"net/http"
@ -90,10 +93,13 @@ func newDoHClient(url, iface string, r *Resolver) *dohClient {
return nil, err
}
ip, err := resolver.ResolveIPWithResolver(host, r)
ips, err := resolver.LookupIPWithResolver(ctx, host, r)
if err != nil {
return nil, err
} else if len(ips) == 0 {
return nil, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host)
}
ip := ips[rand.Intn(len(ips))]
options := []dialer.Option{}
if iface != "" {
@ -102,6 +108,10 @@ func newDoHClient(url, iface string, r *Resolver) *dohClient {
return dialer.DialContext(ctx, "tcp", net.JoinHostPort(ip.String(), port), options...)
},
TLSClientConfig: &tls.Config{
// alpn identifier, see https://tools.ietf.org/html/draft-hoffman-dprive-dns-tls-alpn-00#page-6
NextProtos: []string{"dns"},
},
},
}
}

View File

@ -78,7 +78,7 @@ func NewEnhancer(cfg Config) *ResolverEnhancer {
if cfg.EnhancedMode != C.DNSNormal {
fakePool = cfg.Pool
mapping = cache.NewLRUCache(cache.WithSize(4096), cache.WithStale(true))
mapping = cache.New(cache.WithSize(4096), cache.WithStale(true))
}
return &ResolverEnhancer{

View File

@ -10,7 +10,6 @@ import (
"time"
"github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/common/picker"
"github.com/Dreamacro/clash/component/fakeip"
"github.com/Dreamacro/clash/component/resolver"
"github.com/Dreamacro/clash/component/trie"
@ -42,19 +41,23 @@ type Resolver struct {
policy *trie.DomainTrie
}
// ResolveIP request with TypeA and TypeAAAA, priority return TypeA
func (r *Resolver) ResolveIP(host string) (ip net.IP, err error) {
ch := make(chan net.IP, 1)
// LookupIP request with TypeA and TypeAAAA, priority return TypeA
func (r *Resolver) LookupIP(ctx context.Context, host string) (ip []net.IP, err error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
ch := make(chan []net.IP, 1)
go func() {
defer close(ch)
ip, err := r.resolveIP(host, D.TypeAAAA)
ip, err := r.lookupIP(ctx, host, D.TypeAAAA)
if err != nil {
return
}
ch <- ip
}()
ip, err = r.resolveIP(host, D.TypeA)
ip, err = r.lookupIP(ctx, host, D.TypeA)
if err == nil {
return
}
@ -67,14 +70,47 @@ func (r *Resolver) ResolveIP(host string) (ip net.IP, err error) {
return ip, nil
}
// ResolveIP request with TypeA and TypeAAAA, priority return TypeA
func (r *Resolver) ResolveIP(host string) (ip net.IP, err error) {
ips, err := r.LookupIP(context.Background(), host)
if err != nil {
return nil, err
} else if len(ips) == 0 {
return nil, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host)
}
return ips[rand.Intn(len(ips))], nil
}
// LookupIPv4 request with TypeA
func (r *Resolver) LookupIPv4(ctx context.Context, host string) ([]net.IP, error) {
return r.lookupIP(ctx, host, D.TypeA)
}
// ResolveIPv4 request with TypeA
func (r *Resolver) ResolveIPv4(host string) (ip net.IP, err error) {
return r.resolveIP(host, D.TypeA)
ips, err := r.lookupIP(context.Background(), host, D.TypeA)
if err != nil {
return nil, err
} else if len(ips) == 0 {
return nil, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host)
}
return ips[rand.Intn(len(ips))], nil
}
// LookupIPv6 request with TypeAAAA
func (r *Resolver) LookupIPv6(ctx context.Context, host string) ([]net.IP, error) {
return r.lookupIP(ctx, host, D.TypeAAAA)
}
// ResolveIPv6 request with TypeAAAA
func (r *Resolver) ResolveIPv6(host string) (ip net.IP, err error) {
return r.resolveIP(host, D.TypeAAAA)
ips, err := r.lookupIP(context.Background(), host, D.TypeAAAA)
if err != nil {
return nil, err
} else if len(ips) == 0 {
return nil, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host)
}
return ips[rand.Intn(len(ips))], nil
}
func (r *Resolver) shouldIPFallback(ip net.IP) bool {
@ -150,31 +186,10 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M
}
func (r *Resolver) batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) {
fast, ctx := picker.WithTimeout(ctx, resolver.DefaultDNSTimeout)
for _, client := range clients {
r := client
fast.Go(func() (any, error) {
m, err := r.ExchangeContext(ctx, m)
if err != nil {
return nil, err
} else if m.Rcode == D.RcodeServerFailure || m.Rcode == D.RcodeRefused {
return nil, errors.New("server failure")
}
return m, nil
})
}
ctx, cancel := context.WithTimeout(ctx, resolver.DefaultDNSTimeout)
defer cancel()
elm := fast.Wait()
if elm == nil {
err := errors.New("all DNS requests failed")
if fErr := fast.Error(); fErr != nil {
err = fmt.Errorf("%w, first error: %s", err, fErr.Error())
}
return nil, err
}
msg = elm.(*D.Msg)
return
return batchExchange(ctx, clients, m)
}
func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient {
@ -253,14 +268,15 @@ func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (msg *D.Msg, err er
return
}
func (r *Resolver) resolveIP(host string, dnsType uint16) (ip net.IP, err error) {
ip = net.ParseIP(host)
func (r *Resolver) lookupIP(ctx context.Context, host string, dnsType uint16) ([]net.IP, error) {
ip := net.ParseIP(host)
if ip != nil {
isIPv4 := ip.To4() != nil
ip4 := ip.To4()
isIPv4 := ip4 != nil
if dnsType == D.TypeAAAA && !isIPv4 {
return ip, nil
return []net.IP{ip}, nil
} else if dnsType == D.TypeA && isIPv4 {
return ip, nil
return []net.IP{ip4}, nil
} else {
return nil, resolver.ErrIPVersion
}
@ -275,13 +291,10 @@ func (r *Resolver) resolveIP(host string, dnsType uint16) (ip net.IP, err error)
}
ips := msgToIP(msg)
ipLength := len(ips)
if ipLength == 0 {
if len(ips) == 0 {
return nil, resolver.ErrIPNotFound
}
ip = ips[rand.Intn(ipLength)]
return
return ips, nil
}
func (r *Resolver) msgToDomain(msg *D.Msg) string {
@ -328,13 +341,13 @@ type Config struct {
func NewResolver(config Config) *Resolver {
defaultResolver := &Resolver{
main: transform(config.Default, nil),
lruCache: cache.NewLRUCache(cache.WithSize(4096), cache.WithStale(true)),
lruCache: cache.New(cache.WithSize(4096), cache.WithStale(true)),
}
r := &Resolver{
ipv6: config.IPv6,
main: transform(config.Main, defaultResolver),
lruCache: cache.NewLRUCache(cache.WithSize(4096), cache.WithStale(true)),
lruCache: cache.New(cache.WithSize(4096), cache.WithStale(true)),
hosts: config.Hosts,
}

View File

@ -1,11 +1,15 @@
package dns
import (
"context"
"crypto/tls"
"errors"
"fmt"
"net"
"time"
"github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/common/picker"
"github.com/Dreamacro/clash/log"
D "github.com/miekg/dns"
@ -63,8 +67,6 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient {
Client: &D.Client{
Net: s.Net,
TLSConfig: &tls.Config{
// alpn identifier, see https://tools.ietf.org/html/draft-hoffman-dprive-dns-tls-alpn-00#page-6
NextProtos: []string{"dns"},
ServerName: host,
},
UDPSize: 4096,
@ -104,3 +106,31 @@ func msgToIP(msg *D.Msg) []net.IP {
return ips
}
func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) {
fast, ctx := picker.WithContext(ctx)
for _, client := range clients {
r := client
fast.Go(func() (any, error) {
m, err := r.ExchangeContext(ctx, m)
if err != nil {
return nil, err
} else if m.Rcode == D.RcodeServerFailure || m.Rcode == D.RcodeRefused {
return nil, errors.New("server failure")
}
return m, nil
})
}
elm := fast.Wait()
if elm == nil {
err := errors.New("all DNS requests failed")
if fErr := fast.Error(); fErr != nil {
err = fmt.Errorf("%w, first error: %s", err, fErr.Error())
}
return nil, err
}
msg = elm.(*D.Msg)
return
}

21
go.mod
View File

@ -5,28 +5,29 @@ go 1.18
require (
github.com/go-chi/chi/v5 v5.0.7
github.com/go-chi/cors v1.2.1
github.com/go-chi/render v1.0.1
github.com/go-chi/render v1.0.2
github.com/gofrs/uuid v4.2.0+incompatible
github.com/gorilla/websocket v1.5.0
github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f
github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84
github.com/miekg/dns v1.1.50
github.com/oschwald/geoip2-golang v1.7.0
github.com/sirupsen/logrus v1.8.1
github.com/oschwald/geoip2-golang v1.8.0
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.0
go.etcd.io/bbolt v1.3.6
go.uber.org/atomic v1.9.0
go.uber.org/atomic v1.10.0
go.uber.org/automaxprocs v1.5.1
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
golang.org/x/net v0.0.0-20220706163947-c90051bbdb60
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e
golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503
golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/ajg/form v1.5.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/oschwald/maxminddb-golang v1.9.0 // indirect
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect
golang.org/x/mod v0.4.2 // indirect

47
go.sum
View File

@ -1,3 +1,5 @@
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -7,8 +9,8 @@ github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@ -20,8 +22,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR
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/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f h1:l1QCwn715k8nYkj4Ql50rzEog3WnMdrd4YYMMwemxEo=
github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84 h1:MJTy6H+EpXLeAn0P5WAWeLk6dJA3V0ik6S3VJfUyQuI=
github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
@ -39,22 +41,21 @@ github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZ
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/oschwald/geoip2-golang v1.7.0 h1:JW1r5AKi+vv2ujSxjKthySK3jo8w8oKWPyXsw+Qs/S8=
github.com/oschwald/geoip2-golang v1.7.0/go.mod h1:mdI/C7iK7NVMcIDDtf4bCKMJ7r0o7UwGeCo9eiitCMQ=
github.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y=
github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm82Cp5HyvYbt8K3zLY=
github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs=
github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
@ -63,15 +64,15 @@ github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk=
go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503 h1:vJ2V3lFLg+bBhgroYuRfyN583UzVveQmIXjc8T/y3to=
golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -86,12 +87,12 @@ 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-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-20220706163947-c90051bbdb60 h1:8NSylCMxLW4JvserAndSgFL7aPli6A68yf0bYFTcWCM=
golang.org/x/net v0.0.0-20220706163947-c90051bbdb60/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c h1:JVAXQ10yGGVbSyoer5VILysz6YKjdNT2bsvlayjqhes=
golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
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-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -100,7 +101,6 @@ golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -112,8 +112,9 @@ 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-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-20220704084225-05e143d24a9e h1:CsOuNlbOuf0mzxJIefr6Q4uAUetRUwZE4qt7VfzP+xo=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=

View File

@ -9,7 +9,6 @@ import (
"time"
C "github.com/Dreamacro/clash/constant"
_ "github.com/Dreamacro/clash/constant/mime"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/tunnel/statistic"

View File

@ -5,7 +5,6 @@ import (
"net"
"net/http"
"strings"
"time"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/cache"
@ -15,7 +14,7 @@ import (
"github.com/Dreamacro/clash/log"
)
func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.Cache) {
func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.LruCache) {
client := newClient(c.RemoteAddr(), in)
defer client.CloseIdleConnections()
@ -99,7 +98,7 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.Cache) {
conn.Close()
}
func authenticate(request *http.Request, cache *cache.Cache) *http.Response {
func authenticate(request *http.Request, cache *cache.LruCache) *http.Response {
authenticator := authStore.Authenticator()
if authenticator != nil {
credential := parseBasicProxyAuthorization(request)
@ -109,11 +108,11 @@ func authenticate(request *http.Request, cache *cache.Cache) *http.Response {
return resp
}
var authed any
if authed = cache.Get(credential); authed == nil {
authed, exist := cache.Get(credential)
if !exist {
user, pass, err := decodeBasicProxyAuthorization(credential)
authed = err == nil && authenticator.Verify(user, pass)
cache.Put(credential, authed, time.Minute)
cache.Set(credential, authed)
}
if !authed.(bool) {
log.Infoln("Auth failed from %s", request.RemoteAddr)

View File

@ -2,7 +2,6 @@ package http
import (
"net"
"time"
"github.com/Dreamacro/clash/common/cache"
C "github.com/Dreamacro/clash/constant"
@ -40,9 +39,9 @@ func NewWithAuthenticate(addr string, in chan<- C.ConnContext, authenticate bool
return nil, err
}
var c *cache.Cache
var c *cache.LruCache
if authenticate {
c = cache.New(time.Second * 30)
c = cache.New(cache.WithAge(30))
}
hl := &Listener{

View File

@ -2,7 +2,6 @@ package mixed
import (
"net"
"time"
"github.com/Dreamacro/clash/common/cache"
N "github.com/Dreamacro/clash/common/net"
@ -16,7 +15,7 @@ import (
type Listener struct {
listener net.Listener
addr string
cache *cache.Cache
cache *cache.LruCache
closed bool
}
@ -45,7 +44,7 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) {
ml := &Listener{
listener: l,
addr: addr,
cache: cache.New(30 * time.Second),
cache: cache.New(cache.WithAge(30)),
}
go func() {
for {
@ -63,7 +62,7 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) {
return ml, nil
}
func handleConn(conn net.Conn, in chan<- C.ConnContext, cache *cache.Cache) {
func handleConn(conn net.Conn, in chan<- C.ConnContext, cache *cache.LruCache) {
conn.(*net.TCPConn).SetKeepAlive(true)
bufConn := N.NewBufferedConn(conn)

View File

@ -13,4 +13,4 @@ linters-settings:
- prefix(github.com/Dreamacro/clash)
- default
staticcheck:
go: '1.18'
go: '1.19'

View File

@ -542,18 +542,16 @@ func testPacketConnTimeout(t *testing.T, pc net.PacketConn) error {
func testSuit(t *testing.T, proxy C.ProxyAdapter) {
conn, err := proxy.DialContext(context.Background(), &C.Metadata{
Host: localIP.String(),
DstPort: "10001",
AddrType: socks5.AtypDomainName,
Host: localIP.String(),
DstPort: "10001",
})
require.NoError(t, err)
defer conn.Close()
assert.NoError(t, testPingPongWithConn(t, conn))
conn, err = proxy.DialContext(context.Background(), &C.Metadata{
Host: localIP.String(),
DstPort: "10001",
AddrType: socks5.AtypDomainName,
Host: localIP.String(),
DstPort: "10001",
})
require.NoError(t, err)
defer conn.Close()
@ -564,10 +562,9 @@ func testSuit(t *testing.T, proxy C.ProxyAdapter) {
}
pc, err := proxy.ListenPacketContext(context.Background(), &C.Metadata{
NetWork: C.UDP,
DstIP: localIP,
DstPort: "10001",
AddrType: socks5.AtypIPv4,
NetWork: C.UDP,
DstIP: localIP,
DstPort: "10001",
})
require.NoError(t, err)
defer pc.Close()
@ -575,10 +572,9 @@ func testSuit(t *testing.T, proxy C.ProxyAdapter) {
assert.NoError(t, testPingPongWithPacketConn(t, pc))
pc, err = proxy.ListenPacketContext(context.Background(), &C.Metadata{
NetWork: C.UDP,
DstIP: localIP,
DstPort: "10001",
AddrType: socks5.AtypIPv4,
NetWork: C.UDP,
DstIP: localIP,
DstPort: "10001",
})
require.NoError(t, err)
defer pc.Close()
@ -586,10 +582,9 @@ func testSuit(t *testing.T, proxy C.ProxyAdapter) {
assert.NoError(t, testLargeDataWithPacketConn(t, pc))
pc, err = proxy.ListenPacketContext(context.Background(), &C.Metadata{
NetWork: C.UDP,
DstIP: localIP,
DstPort: "10001",
AddrType: socks5.AtypIPv4,
NetWork: C.UDP,
DstIP: localIP,
DstPort: "10001",
})
require.NoError(t, err)
defer pc.Close()
@ -625,9 +620,8 @@ func benchmarkProxy(b *testing.B, proxy C.ProxyAdapter) {
}()
conn, err := proxy.DialContext(context.Background(), &C.Metadata{
Host: localIP.String(),
DstPort: "10001",
AddrType: socks5.AtypDomainName,
Host: localIP.String(),
DstPort: "10001",
})
require.NoError(b, err)

View File

@ -3,12 +3,12 @@ module clash-test
go 1.18
require (
github.com/Dreamacro/clash v1.10.6
github.com/Dreamacro/clash v1.11.4
github.com/docker/docker v20.10.17+incompatible
github.com/docker/go-connections v0.4.0
github.com/miekg/dns v1.1.49
github.com/stretchr/testify v1.7.2
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9
github.com/miekg/dns v1.1.50
github.com/stretchr/testify v1.8.0
golang.org/x/net v0.0.0-20220708220712-1185a9018129
)
replace github.com/Dreamacro/clash => ../
@ -35,10 +35,10 @@ require (
github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.uber.org/atomic v1.9.0 // indirect
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
golang.org/x/mod v0.4.2 // indirect
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c // indirect
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect

View File

@ -52,8 +52,8 @@ github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcK
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8=
github.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
@ -78,11 +78,13 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA=
github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -95,8 +97,8 @@ go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
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/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
@ -115,8 +117,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/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-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-20220617184016-355a448f1bc9 h1:Yqz/iviulwKwAREEeUd3nbBFn0XuyJqkoft2IlrvOhc=
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0=
golang.org/x/net v0.0.0-20220708220712-1185a9018129/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-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -146,8 +148,8 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/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-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU=
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e h1:CsOuNlbOuf0mzxJIefr6Q4uAUetRUwZE4qt7VfzP+xo=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=

View File

@ -18,7 +18,6 @@ import (
"time"
"github.com/Dreamacro/clash/common/pool"
C "github.com/Dreamacro/clash/constant"
"go.uber.org/atomic"
"golang.org/x/net/http2"
@ -168,17 +167,13 @@ func (g *Conn) SetDeadline(t time.Time) error {
}
func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config) *http2.Transport {
dialFunc := func(network, addr string, cfg *tls.Config) (net.Conn, error) {
dialFunc := func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
pconn, err := dialFn(network, addr)
if err != nil {
return nil, err
}
cn := tls.Client(pconn, cfg)
// fix tls handshake not timeout
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
defer cancel()
if err := cn.HandshakeContext(ctx); err != nil {
pconn.Close()
return nil, err
@ -192,7 +187,7 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config) *http2.Transport {
}
return &http2.Transport{
DialTLS: dialFunc,
DialTLSContext: dialFunc,
TLSClientConfig: tlsConfig,
AllowHTTP: false,
DisableCompression: true,

View File

@ -241,6 +241,9 @@ func ReadPacket(r io.Reader, payload []byte) (net.Addr, int, error) {
return nil, 0, errors.New("remote address invalid")
}
uAddr := addr.UDPAddr()
if uAddr == nil {
return nil, 0, errors.New("parse addr error")
}
length := len(payload)
if n-headLen < length {

View File

@ -170,6 +170,9 @@ func ReadPacket(r io.Reader, payload []byte) (net.Addr, int, int, error) {
return nil, 0, 0, errors.New("read addr error")
}
uAddr := addr.UDPAddr()
if uAddr == nil {
return nil, 0, 0, errors.New("parse addr error")
}
if _, err = io.ReadFull(r, payload[:2]); err != nil {
return nil, 0, 0, errors.New("read length error")

View File

@ -256,10 +256,16 @@ func streamWebsocketConn(conn net.Conn, c *WebsocketConfig, earlyData *bytes.Buf
dialer.TLSClientConfig = c.TLSConfig
}
u, err := url.Parse(c.Path)
if err != nil {
return nil, fmt.Errorf("parse url %s error: %w", c.Path, err)
}
uri := url.URL{
Scheme: scheme,
Host: net.JoinHostPort(c.Host, c.Port),
Path: c.Path,
Scheme: scheme,
Host: net.JoinHostPort(c.Host, c.Port),
Path: u.Path,
RawQuery: u.RawQuery,
}
headers := http.Header{}

View File

@ -180,11 +180,13 @@ func handleUDPConn(packet *inbound.PacketAdapter) {
// local resolve UDP dns
if !metadata.Resolved() {
ip, err := resolver.ResolveIP(metadata.Host)
ips, err := resolver.LookupIP(context.Background(), metadata.Host)
if err != nil {
return
} else if len(ips) == 0 {
return
}
metadata.DstIP = ip
metadata.DstIP = ips[0]
}
key := packet.LocalAddr().String()