Compare commits

..

2 Commits

Author SHA1 Message Date
Meta Gowork
584b81e507 [Chore] workflows 2022-04-02 19:02:43 +08:00
Meta Gowork
6596db7257 [Chore] workflows 2022-04-02 19:01:28 +08:00
67 changed files with 841 additions and 1631 deletions

View File

@@ -1,4 +1,4 @@
name: Alpha name: Release
on: [push] on: [push]
jobs: jobs:
Feature-build: Feature-build:
@@ -44,18 +44,18 @@ jobs:
- name: Tag Repo - name: Tag Repo
uses: richardsimko/update-tag@v1 uses: richardsimko/update-tag@v1
with: with:
tag_name: alpha tag_name: v1.10.0
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Alpha - name: Upload Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
if: ${{ env.GIT_BRANCH != 'Meta' && success() }} if: ${{ env.GIT_BRANCH == 'Meta' && success() }}
with: with:
tag: ${{ github.ref }} tag: ${{ github.ref }}
tag_name: alpha tag_name: v1.10.0
files: bin/* files: bin/*
prerelease: true prerelease: false
- name: send telegram message on push - name: send telegram message on push
uses: appleboy/telegram-action@master uses: appleboy/telegram-action@master

View File

@@ -1,7 +1,7 @@
NAME=Clash.Meta NAME=Clash.Meta
BINDIR=bin BINDIR=bin
BRANCH=$(shell git rev-parse --abbrev-ref HEAD) BRANCH=$(shell git rev-parse --abbrev-ref HEAD)
VERSION=alpha-$(shell git rev-parse --short HEAD) VERSION=$(shell git describe --tags || echo "unknown version")
BUILDTIME=$(shell date -u) BUILDTIME=$(shell date -u)
GOBUILD=CGO_ENABLED=0 go build -trimpath -ldflags '-X "github.com/Dreamacro/clash/constant.Version=$(VERSION)" \ GOBUILD=CGO_ENABLED=0 go build -trimpath -ldflags '-X "github.com/Dreamacro/clash/constant.Version=$(VERSION)" \
-X "github.com/Dreamacro/clash/constant.BuildTime=$(BUILDTIME)" \ -X "github.com/Dreamacro/clash/constant.BuildTime=$(BUILDTIME)" \

View File

@@ -21,7 +21,7 @@ var UnifiedDelay = atomic.NewBool(false)
type Proxy struct { type Proxy struct {
C.ProxyAdapter C.ProxyAdapter
history *queue.Queue[C.DelayHistory] history *queue.Queue
alive *atomic.Bool alive *atomic.Bool
} }
@@ -67,7 +67,7 @@ func (p *Proxy) DelayHistory() []C.DelayHistory {
queue := p.history.Copy() queue := p.history.Copy()
histories := []C.DelayHistory{} histories := []C.DelayHistory{}
for _, item := range queue { for _, item := range queue {
histories = append(histories, item) histories = append(histories, item.(C.DelayHistory))
} }
return histories return histories
} }
@@ -80,7 +80,11 @@ func (p *Proxy) LastDelay() (delay uint16) {
return max return max
} }
history := p.history.Last() last := p.history.Last()
if last == nil {
return max
}
history := last.(C.DelayHistory)
if history.Delay == 0 { if history.Delay == 0 {
return max return max
} }
@@ -174,7 +178,7 @@ func (p *Proxy) URLTest(ctx context.Context, url string) (t uint16, err error) {
} }
func NewProxy(adapter C.ProxyAdapter) *Proxy { func NewProxy(adapter C.ProxyAdapter) *Proxy {
return &Proxy{adapter, queue.New[C.DelayHistory](10), atomic.NewBool(true)} return &Proxy{adapter, queue.New(10), atomic.NewBool(true)}
} }
func urlToMetadata(rawURL string) (addr C.Metadata, err error) { func urlToMetadata(rawURL string) (addr C.Metadata, err error) {

View File

@@ -13,13 +13,9 @@ func NewSocket(target socks5.Addr, conn net.Conn, source C.Type) *context.ConnCo
metadata := parseSocksAddr(target) metadata := parseSocksAddr(target)
metadata.NetWork = C.TCP metadata.NetWork = C.TCP
metadata.Type = source metadata.Type = source
remoteAddr := conn.RemoteAddr() if ip, port, err := parseAddr(conn.RemoteAddr().String()); err == nil {
// Filter when net.Addr interface is nil metadata.SrcIP = ip
if remoteAddr != nil { metadata.SrcPort = port
if ip, port, err := parseAddr(remoteAddr.String()); err == nil {
metadata.SrcIP = ip
metadata.SrcPort = port
}
} }
return context.NewConnContext(conn, metadata) return context.NewConnContext(conn, metadata)

View File

@@ -60,7 +60,7 @@ func NewCompatible() *Direct {
func NewPass() *Direct { func NewPass() *Direct {
return &Direct{ return &Direct{
Base: &Base{ Base: &Base{
name: "PASS", name: "Pass",
tp: C.Pass, tp: C.Pass,
udp: true, udp: true,
}, },

View File

@@ -22,20 +22,18 @@ type Http struct {
user string user string
pass string pass string
tlsConfig *tls.Config tlsConfig *tls.Config
option *HttpOption
} }
type HttpOption struct { type HttpOption struct {
BasicOption BasicOption
Name string `proxy:"name"` Name string `proxy:"name"`
Server string `proxy:"server"` Server string `proxy:"server"`
Port int `proxy:"port"` Port int `proxy:"port"`
UserName string `proxy:"username,omitempty"` UserName string `proxy:"username,omitempty"`
Password string `proxy:"password,omitempty"` Password string `proxy:"password,omitempty"`
TLS bool `proxy:"tls,omitempty"` TLS bool `proxy:"tls,omitempty"`
SNI string `proxy:"sni,omitempty"` SNI string `proxy:"sni,omitempty"`
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
Headers map[string]string `proxy:"headers,omitempty"`
} }
// StreamConn implements C.ProxyAdapter // StreamConn implements C.ProxyAdapter
@@ -86,13 +84,6 @@ func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error {
}, },
} }
//增加headers
if len(h.option.Headers) != 0 {
for key, value := range h.option.Headers {
req.Header.Add(key, value)
}
}
if h.user != "" && h.pass != "" { if h.user != "" && h.pass != "" {
auth := h.user + ":" + h.pass auth := h.user + ":" + h.pass
req.Header.Add("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(auth))) req.Header.Add("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(auth)))
@@ -150,6 +141,5 @@ func NewHttp(option HttpOption) *Http {
user: option.UserName, user: option.UserName,
pass: option.Password, pass: option.Password,
tlsConfig: tlsConfig, tlsConfig: tlsConfig,
option: &option,
} }
} }

View File

@@ -58,7 +58,7 @@ func (h *HTTPVehicle) Read() ([]byte, error) {
} }
req, err := http.NewRequest(http.MethodGet, uri.String(), nil) req, err := http.NewRequest(http.MethodGet, uri.String(), nil)
req.Header.Set("User-Agent", netHttp.UA) req.Header.Set("user-agent", netHttp.UA)
if err != nil { if err != nil {
return nil, err return nil, err

48
common/cache/cache.go vendored
View File

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

View File

@@ -11,50 +11,48 @@ import (
func TestCache_Basic(t *testing.T) { func TestCache_Basic(t *testing.T) {
interval := 200 * time.Millisecond interval := 200 * time.Millisecond
ttl := 20 * time.Millisecond ttl := 20 * time.Millisecond
c := New[string, int](interval) c := New(interval)
c.Put("int", 1, ttl) c.Put("int", 1, ttl)
c.Put("string", "a", ttl)
d := New[string, string](interval)
d.Put("string", "a", ttl)
i := c.Get("int") i := c.Get("int")
assert.Equal(t, i, 1, "should recv 1") assert.Equal(t, i.(int), 1, "should recv 1")
s := d.Get("string") s := c.Get("string")
assert.Equal(t, s, "a", "should recv 'a'") assert.Equal(t, s.(string), "a", "should recv 'a'")
} }
func TestCache_TTL(t *testing.T) { func TestCache_TTL(t *testing.T) {
interval := 200 * time.Millisecond interval := 200 * time.Millisecond
ttl := 20 * time.Millisecond ttl := 20 * time.Millisecond
now := time.Now() now := time.Now()
c := New[string, int](interval) c := New(interval)
c.Put("int", 1, ttl) c.Put("int", 1, ttl)
c.Put("int2", 2, ttl) c.Put("int2", 2, ttl)
i := c.Get("int") i := c.Get("int")
_, expired := c.GetWithExpire("int2") _, expired := c.GetWithExpire("int2")
assert.Equal(t, i, 1, "should recv 1") assert.Equal(t, i.(int), 1, "should recv 1")
assert.True(t, now.Before(expired)) assert.True(t, now.Before(expired))
time.Sleep(ttl * 2) time.Sleep(ttl * 2)
i = c.Get("int") i = c.Get("int")
j, _ := c.GetWithExpire("int2") j, _ := c.GetWithExpire("int2")
assert.True(t, i == 0, "should recv 0") assert.Nil(t, i, "should recv nil")
assert.True(t, j == 0, "should recv 0") assert.Nil(t, j, "should recv nil")
} }
func TestCache_AutoCleanup(t *testing.T) { func TestCache_AutoCleanup(t *testing.T) {
interval := 10 * time.Millisecond interval := 10 * time.Millisecond
ttl := 15 * time.Millisecond ttl := 15 * time.Millisecond
c := New[string, int](interval) c := New(interval)
c.Put("int", 1, ttl) c.Put("int", 1, ttl)
time.Sleep(ttl * 2) time.Sleep(ttl * 2)
i := c.Get("int") i := c.Get("int")
j, _ := c.GetWithExpire("int") j, _ := c.GetWithExpire("int")
assert.True(t, i == 0, "should recv 0") assert.Nil(t, i, "should recv nil")
assert.True(t, j == 0, "should recv 0") assert.Nil(t, j, "should recv nil")
} }
func TestCache_AutoGC(t *testing.T) { func TestCache_AutoGC(t *testing.T) {
@@ -62,7 +60,7 @@ func TestCache_AutoGC(t *testing.T) {
go func() { go func() {
interval := 10 * time.Millisecond interval := 10 * time.Millisecond
ttl := 15 * time.Millisecond ttl := 15 * time.Millisecond
c := New[string, int](interval) c := New(interval)
c.Put("int", 1, ttl) c.Put("int", 1, ttl)
sign <- struct{}{} sign <- struct{}{}
}() }()

View File

@@ -9,43 +9,43 @@ import (
) )
// Option is part of Functional Options Pattern // Option is part of Functional Options Pattern
type Option[K comparable, V any] func(*LruCache[K, V]) type Option func(*LruCache)
// EvictCallback is used to get a callback when a cache entry is evicted // EvictCallback is used to get a callback when a cache entry is evicted
type EvictCallback = func(key any, value any) type EvictCallback = func(key any, value any)
// WithEvict set the evict callback // WithEvict set the evict callback
func WithEvict[K comparable, V any](cb EvictCallback) Option[K, V] { func WithEvict(cb EvictCallback) Option {
return func(l *LruCache[K, V]) { return func(l *LruCache) {
l.onEvict = cb l.onEvict = cb
} }
} }
// WithUpdateAgeOnGet update expires when Get element // WithUpdateAgeOnGet update expires when Get element
func WithUpdateAgeOnGet[K comparable, V any]() Option[K, V] { func WithUpdateAgeOnGet() Option {
return func(l *LruCache[K, V]) { return func(l *LruCache) {
l.updateAgeOnGet = true l.updateAgeOnGet = true
} }
} }
// WithAge defined element max age (second) // WithAge defined element max age (second)
func WithAge[K comparable, V any](maxAge int64) Option[K, V] { func WithAge(maxAge int64) Option {
return func(l *LruCache[K, V]) { return func(l *LruCache) {
l.maxAge = maxAge l.maxAge = maxAge
} }
} }
// WithSize defined max length of LruCache // WithSize defined max length of LruCache
func WithSize[K comparable, V any](maxSize int) Option[K, V] { func WithSize(maxSize int) Option {
return func(l *LruCache[K, V]) { return func(l *LruCache) {
l.maxSize = maxSize l.maxSize = maxSize
} }
} }
// WithStale decide whether Stale return is enabled. // WithStale decide whether Stale return is enabled.
// If this feature is enabled, element will not get Evicted according to `WithAge`. // If this feature is enabled, element will not get Evicted according to `WithAge`.
func WithStale[K comparable, V any](stale bool) Option[K, V] { func WithStale(stale bool) Option {
return func(l *LruCache[K, V]) { return func(l *LruCache) {
l.staleReturn = stale l.staleReturn = stale
} }
} }
@@ -53,7 +53,7 @@ func WithStale[K comparable, V any](stale bool) Option[K, V] {
// LruCache is a thread-safe, in-memory lru-cache that evicts the // LruCache is a thread-safe, in-memory lru-cache that evicts the
// least recently used entries from memory when (if set) the entries are // least recently used entries from memory when (if set) the entries are
// older than maxAge (in seconds). Use the New constructor to create one. // older than maxAge (in seconds). Use the New constructor to create one.
type LruCache[K comparable, V any] struct { type LruCache struct {
maxAge int64 maxAge int64
maxSize int maxSize int
mu sync.Mutex mu sync.Mutex
@@ -65,8 +65,8 @@ type LruCache[K comparable, V any] struct {
} }
// NewLRUCache creates an LruCache // NewLRUCache creates an LruCache
func NewLRUCache[K comparable, V any](options ...Option[K, V]) *LruCache[K, V] { func NewLRUCache(options ...Option) *LruCache {
lc := &LruCache[K, V]{ lc := &LruCache{
lru: list.New(), lru: list.New(),
cache: make(map[any]*list.Element), cache: make(map[any]*list.Element),
} }
@@ -80,12 +80,12 @@ func NewLRUCache[K comparable, V any](options ...Option[K, V]) *LruCache[K, V] {
// Get returns the any representation of a cached response and a bool // Get returns the any representation of a cached response and a bool
// set to true if the key was found. // set to true if the key was found.
func (c *LruCache[K, V]) Get(key K) (V, bool) { func (c *LruCache) Get(key any) (any, bool) {
el := c.get(key) entry := c.get(key)
if el == nil { if entry == nil {
return getZero[V](), false return nil, false
} }
value := el.value value := entry.value
return value, true return value, true
} }
@@ -94,17 +94,17 @@ func (c *LruCache[K, V]) Get(key K) (V, bool) {
// a time.Time Give expected expires, // a time.Time Give expected expires,
// and a bool set to true if the key was found. // and a bool set to true if the key was found.
// This method will NOT check the maxAge of element and will NOT update the expires. // This method will NOT check the maxAge of element and will NOT update the expires.
func (c *LruCache[K, V]) GetWithExpire(key K) (V, time.Time, bool) { func (c *LruCache) GetWithExpire(key any) (any, time.Time, bool) {
el := c.get(key) entry := c.get(key)
if el == nil { if entry == nil {
return getZero[V](), time.Time{}, false return nil, time.Time{}, false
} }
return el.value, time.Unix(el.expires, 0), true return entry.value, time.Unix(entry.expires, 0), true
} }
// Exist returns if key exist in cache but not put item to the head of linked list // Exist returns if key exist in cache but not put item to the head of linked list
func (c *LruCache[K, V]) Exist(key K) bool { func (c *LruCache) Exist(key any) bool {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
@@ -113,7 +113,7 @@ func (c *LruCache[K, V]) Exist(key K) bool {
} }
// Set stores the any representation of a response for a given key. // Set stores the any representation of a response for a given key.
func (c *LruCache[K, V]) Set(key K, value V) { func (c *LruCache) Set(key any, value any) {
expires := int64(0) expires := int64(0)
if c.maxAge > 0 { if c.maxAge > 0 {
expires = time.Now().Unix() + c.maxAge expires = time.Now().Unix() + c.maxAge
@@ -123,21 +123,21 @@ func (c *LruCache[K, V]) Set(key K, value V) {
// SetWithExpire stores the any representation of a response for a given key and given expires. // SetWithExpire stores the any representation of a response for a given key and given expires.
// The expires time will round to second. // The expires time will round to second.
func (c *LruCache[K, V]) SetWithExpire(key K, value V, expires time.Time) { func (c *LruCache) SetWithExpire(key any, value any, expires time.Time) {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
if le, ok := c.cache[key]; ok { if le, ok := c.cache[key]; ok {
c.lru.MoveToBack(le) c.lru.MoveToBack(le)
e := le.Value.(*entry[K, V]) e := le.Value.(*entry)
e.value = value e.value = value
e.expires = expires.Unix() e.expires = expires.Unix()
} else { } else {
e := &entry[K, V]{key: key, value: value, expires: expires.Unix()} e := &entry{key: key, value: value, expires: expires.Unix()}
c.cache[key] = c.lru.PushBack(e) c.cache[key] = c.lru.PushBack(e)
if c.maxSize > 0 { if c.maxSize > 0 {
if elLen := c.lru.Len(); elLen > c.maxSize { if len := c.lru.Len(); len > c.maxSize {
c.deleteElement(c.lru.Front()) c.deleteElement(c.lru.Front())
} }
} }
@@ -147,7 +147,7 @@ func (c *LruCache[K, V]) SetWithExpire(key K, value V, expires time.Time) {
} }
// CloneTo clone and overwrite elements to another LruCache // CloneTo clone and overwrite elements to another LruCache
func (c *LruCache[K, V]) CloneTo(n *LruCache[K, V]) { func (c *LruCache) CloneTo(n *LruCache) {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
@@ -158,12 +158,12 @@ func (c *LruCache[K, V]) CloneTo(n *LruCache[K, V]) {
n.cache = make(map[any]*list.Element) n.cache = make(map[any]*list.Element)
for e := c.lru.Front(); e != nil; e = e.Next() { for e := c.lru.Front(); e != nil; e = e.Next() {
elm := e.Value.(*entry[K, V]) elm := e.Value.(*entry)
n.cache[elm.key] = n.lru.PushBack(elm) n.cache[elm.key] = n.lru.PushBack(elm)
} }
} }
func (c *LruCache[K, V]) get(key K) *entry[K, V] { func (c *LruCache) get(key any) *entry {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
@@ -172,7 +172,7 @@ func (c *LruCache[K, V]) get(key K) *entry[K, V] {
return nil return nil
} }
if !c.staleReturn && c.maxAge > 0 && le.Value.(*entry[K, V]).expires <= time.Now().Unix() { if !c.staleReturn && c.maxAge > 0 && le.Value.(*entry).expires <= time.Now().Unix() {
c.deleteElement(le) c.deleteElement(le)
c.maybeDeleteOldest() c.maybeDeleteOldest()
@@ -180,15 +180,15 @@ func (c *LruCache[K, V]) get(key K) *entry[K, V] {
} }
c.lru.MoveToBack(le) c.lru.MoveToBack(le)
el := le.Value.(*entry[K, V]) entry := le.Value.(*entry)
if c.maxAge > 0 && c.updateAgeOnGet { if c.maxAge > 0 && c.updateAgeOnGet {
el.expires = time.Now().Unix() + c.maxAge entry.expires = time.Now().Unix() + c.maxAge
} }
return el return entry
} }
// Delete removes the value associated with a key. // Delete removes the value associated with a key.
func (c *LruCache[K, V]) Delete(key K) { func (c *LruCache) Delete(key any) {
c.mu.Lock() c.mu.Lock()
if le, ok := c.cache[key]; ok { if le, ok := c.cache[key]; ok {
@@ -198,25 +198,25 @@ func (c *LruCache[K, V]) Delete(key K) {
c.mu.Unlock() c.mu.Unlock()
} }
func (c *LruCache[K, V]) maybeDeleteOldest() { func (c *LruCache) maybeDeleteOldest() {
if !c.staleReturn && c.maxAge > 0 { if !c.staleReturn && c.maxAge > 0 {
now := time.Now().Unix() now := time.Now().Unix()
for le := c.lru.Front(); le != nil && le.Value.(*entry[K, V]).expires <= now; le = c.lru.Front() { for le := c.lru.Front(); le != nil && le.Value.(*entry).expires <= now; le = c.lru.Front() {
c.deleteElement(le) c.deleteElement(le)
} }
} }
} }
func (c *LruCache[K, V]) deleteElement(le *list.Element) { func (c *LruCache) deleteElement(le *list.Element) {
c.lru.Remove(le) c.lru.Remove(le)
e := le.Value.(*entry[K, V]) e := le.Value.(*entry)
delete(c.cache, e.key) delete(c.cache, e.key)
if c.onEvict != nil { if c.onEvict != nil {
c.onEvict(e.key, e.value) c.onEvict(e.key, e.value)
} }
} }
func (c *LruCache[K, V]) Clear() error { func (c *LruCache) Clear() error {
c.mu.Lock() c.mu.Lock()
c.cache = make(map[any]*list.Element) c.cache = make(map[any]*list.Element)
@@ -225,13 +225,8 @@ func (c *LruCache[K, V]) Clear() error {
return nil return nil
} }
type entry[K comparable, V any] struct { type entry struct {
key K key any
value V value any
expires int64 expires int64
} }
func getZero[T any]() T {
var result T
return result
}

View File

@@ -19,7 +19,7 @@ var entries = []struct {
} }
func TestLRUCache(t *testing.T) { func TestLRUCache(t *testing.T) {
c := NewLRUCache[string, string]() c := NewLRUCache()
for _, e := range entries { for _, e := range entries {
c.Set(e.key, e.value) c.Set(e.key, e.value)
@@ -32,7 +32,7 @@ func TestLRUCache(t *testing.T) {
for _, e := range entries { for _, e := range entries {
value, ok := c.Get(e.key) value, ok := c.Get(e.key)
if assert.True(t, ok) { if assert.True(t, ok) {
assert.Equal(t, e.value, value) assert.Equal(t, e.value, value.(string))
} }
} }
@@ -45,25 +45,25 @@ func TestLRUCache(t *testing.T) {
} }
func TestLRUMaxAge(t *testing.T) { func TestLRUMaxAge(t *testing.T) {
c := NewLRUCache[string, string](WithAge[string, string](86400)) c := NewLRUCache(WithAge(86400))
now := time.Now().Unix() now := time.Now().Unix()
expected := now + 86400 expected := now + 86400
// Add one expired entry // Add one expired entry
c.Set("foo", "bar") c.Set("foo", "bar")
c.lru.Back().Value.(*entry[string, string]).expires = now c.lru.Back().Value.(*entry).expires = now
// Reset // Reset
c.Set("foo", "bar") c.Set("foo", "bar")
e := c.lru.Back().Value.(*entry[string, string]) e := c.lru.Back().Value.(*entry)
assert.True(t, e.expires >= now) assert.True(t, e.expires >= now)
c.lru.Back().Value.(*entry[string, string]).expires = now c.lru.Back().Value.(*entry).expires = now
// Set a few and verify expiration times // Set a few and verify expiration times
for _, s := range entries { for _, s := range entries {
c.Set(s.key, s.value) c.Set(s.key, s.value)
e := c.lru.Back().Value.(*entry[string, string]) e := c.lru.Back().Value.(*entry)
assert.True(t, e.expires >= expected && e.expires <= expected+10) assert.True(t, e.expires >= expected && e.expires <= expected+10)
} }
@@ -77,7 +77,7 @@ func TestLRUMaxAge(t *testing.T) {
for _, s := range entries { for _, s := range entries {
le, ok := c.cache[s.key] le, ok := c.cache[s.key]
if assert.True(t, ok) { if assert.True(t, ok) {
le.Value.(*entry[string, string]).expires = now le.Value.(*entry).expires = now
} }
} }
@@ -88,22 +88,22 @@ func TestLRUMaxAge(t *testing.T) {
} }
func TestLRUpdateOnGet(t *testing.T) { func TestLRUpdateOnGet(t *testing.T) {
c := NewLRUCache[string, string](WithAge[string, string](86400), WithUpdateAgeOnGet[string, string]()) c := NewLRUCache(WithAge(86400), WithUpdateAgeOnGet())
now := time.Now().Unix() now := time.Now().Unix()
expires := now + 86400/2 expires := now + 86400/2
// Add one expired entry // Add one expired entry
c.Set("foo", "bar") c.Set("foo", "bar")
c.lru.Back().Value.(*entry[string, string]).expires = expires c.lru.Back().Value.(*entry).expires = expires
_, ok := c.Get("foo") _, ok := c.Get("foo")
assert.True(t, ok) assert.True(t, ok)
assert.True(t, c.lru.Back().Value.(*entry[string, string]).expires > expires) assert.True(t, c.lru.Back().Value.(*entry).expires > expires)
} }
func TestMaxSize(t *testing.T) { func TestMaxSize(t *testing.T) {
c := NewLRUCache[string, string](WithSize[string, string](2)) c := NewLRUCache(WithSize(2))
// Add one expired entry // Add one expired entry
c.Set("foo", "bar") c.Set("foo", "bar")
_, ok := c.Get("foo") _, ok := c.Get("foo")
@@ -117,7 +117,7 @@ func TestMaxSize(t *testing.T) {
} }
func TestExist(t *testing.T) { func TestExist(t *testing.T) {
c := NewLRUCache[int, int](WithSize[int, int](1)) c := NewLRUCache(WithSize(1))
c.Set(1, 2) c.Set(1, 2)
assert.True(t, c.Exist(1)) assert.True(t, c.Exist(1))
c.Set(2, 3) c.Set(2, 3)
@@ -130,7 +130,7 @@ func TestEvict(t *testing.T) {
temp = key.(int) + value.(int) temp = key.(int) + value.(int)
} }
c := NewLRUCache[int, int](WithEvict[int, int](evict), WithSize[int, int](1)) c := NewLRUCache(WithEvict(evict), WithSize(1))
c.Set(1, 2) c.Set(1, 2)
c.Set(2, 3) c.Set(2, 3)
@@ -138,22 +138,21 @@ func TestEvict(t *testing.T) {
} }
func TestSetWithExpire(t *testing.T) { func TestSetWithExpire(t *testing.T) {
c := NewLRUCache[int, *struct{}](WithAge[int, *struct{}](1)) c := NewLRUCache(WithAge(1))
now := time.Now().Unix() now := time.Now().Unix()
tenSecBefore := time.Unix(now-10, 0) tenSecBefore := time.Unix(now-10, 0)
c.SetWithExpire(1, &struct{}{}, tenSecBefore) c.SetWithExpire(1, 2, tenSecBefore)
// res is expected not to exist, and expires should be empty time.Time // res is expected not to exist, and expires should be empty time.Time
res, expires, exist := c.GetWithExpire(1) res, expires, exist := c.GetWithExpire(1)
assert.Equal(t, nil, res)
assert.True(t, nil == res)
assert.Equal(t, time.Time{}, expires) assert.Equal(t, time.Time{}, expires)
assert.Equal(t, false, exist) assert.Equal(t, false, exist)
} }
func TestStale(t *testing.T) { func TestStale(t *testing.T) {
c := NewLRUCache[int, int](WithAge[int, int](1), WithStale[int, int](true)) c := NewLRUCache(WithAge(1), WithStale(true))
now := time.Now().Unix() now := time.Now().Unix()
tenSecBefore := time.Unix(now-10, 0) tenSecBefore := time.Unix(now-10, 0)
@@ -166,11 +165,11 @@ func TestStale(t *testing.T) {
} }
func TestCloneTo(t *testing.T) { func TestCloneTo(t *testing.T) {
o := NewLRUCache[string, int](WithSize[string, int](10)) o := NewLRUCache(WithSize(10))
o.Set("1", 1) o.Set("1", 1)
o.Set("2", 2) o.Set("2", 2)
n := NewLRUCache[string, int](WithSize[string, int](2)) n := NewLRUCache(WithSize(2))
n.Set("3", 3) n.Set("3", 3)
n.Set("4", 4) n.Set("4", 4)

View File

@@ -5,13 +5,13 @@ import (
) )
// Queue is a simple concurrent safe queue // Queue is a simple concurrent safe queue
type Queue[T any] struct { type Queue struct {
items []T items []any
lock sync.RWMutex lock sync.RWMutex
} }
// Put add the item to the queue. // Put add the item to the queue.
func (q *Queue[T]) Put(items ...T) { func (q *Queue) Put(items ...any) {
if len(items) == 0 { if len(items) == 0 {
return return
} }
@@ -22,9 +22,9 @@ func (q *Queue[T]) Put(items ...T) {
} }
// Pop returns the head of items. // Pop returns the head of items.
func (q *Queue[T]) Pop() T { func (q *Queue) Pop() any {
if len(q.items) == 0 { if len(q.items) == 0 {
return GetZero[T]() return nil
} }
q.lock.Lock() q.lock.Lock()
@@ -35,9 +35,9 @@ func (q *Queue[T]) Pop() T {
} }
// Last returns the last of item. // Last returns the last of item.
func (q *Queue[T]) Last() T { func (q *Queue) Last() any {
if len(q.items) == 0 { if len(q.items) == 0 {
return GetZero[T]() return nil
} }
q.lock.RLock() q.lock.RLock()
@@ -47,8 +47,8 @@ func (q *Queue[T]) Last() T {
} }
// Copy get the copy of queue. // Copy get the copy of queue.
func (q *Queue[T]) Copy() []T { func (q *Queue) Copy() []any {
items := []T{} items := []any{}
q.lock.RLock() q.lock.RLock()
items = append(items, q.items...) items = append(items, q.items...)
q.lock.RUnlock() q.lock.RUnlock()
@@ -56,7 +56,7 @@ func (q *Queue[T]) Copy() []T {
} }
// Len returns the number of items in this queue. // Len returns the number of items in this queue.
func (q *Queue[T]) Len() int64 { func (q *Queue) Len() int64 {
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()
@@ -64,13 +64,8 @@ func (q *Queue[T]) Len() int64 {
} }
// New is a constructor for a new concurrent safe queue. // New is a constructor for a new concurrent safe queue.
func New[T any](hint int64) *Queue[T] { func New(hint int64) *Queue {
return &Queue[T]{ return &Queue{
items: make([]T, 0, hint), items: make([]any, 0, hint),
} }
} }
func GetZero[T any]() T {
var result T
return result
}

View File

@@ -1,44 +0,0 @@
package utils
import (
"golang.org/x/exp/constraints"
)
type Range[T constraints.Ordered] struct {
start T
end T
}
func NewRange[T constraints.Ordered](start, end T) *Range[T] {
if start > end {
return &Range[T]{
start: end,
end: start,
}
}
return &Range[T]{
start: start,
end: end,
}
}
func (r *Range[T]) Contains(t T) bool {
return t >= r.start && t <= r.end
}
func (r *Range[T]) LeftContains(t T) bool {
return t >= r.start && t < r.end
}
func (r *Range[T]) RightContains(t T) bool {
return t > r.start && t <= r.end
}
func (r *Range[T]) Start() T {
return r.start
}
func (r *Range[T]) End() T {
return r.end
}

View File

@@ -1,7 +1,7 @@
package fakeip package fakeip
import ( import (
"net/netip" "net"
"github.com/Dreamacro/clash/component/profile/cachefile" "github.com/Dreamacro/clash/component/profile/cachefile"
) )
@@ -11,27 +11,22 @@ type cachefileStore struct {
} }
// GetByHost implements store.GetByHost // GetByHost implements store.GetByHost
func (c *cachefileStore) GetByHost(host string) (netip.Addr, bool) { func (c *cachefileStore) GetByHost(host string) (net.IP, bool) {
elm := c.cache.GetFakeip([]byte(host)) elm := c.cache.GetFakeip([]byte(host))
if elm == nil { if elm == nil {
return netip.Addr{}, false return nil, false
}
if len(elm) == 4 {
return netip.AddrFrom4(*(*[4]byte)(elm)), true
} else {
return netip.AddrFrom16(*(*[16]byte)(elm)), true
} }
return net.IP(elm), true
} }
// PutByHost implements store.PutByHost // PutByHost implements store.PutByHost
func (c *cachefileStore) PutByHost(host string, ip netip.Addr) { func (c *cachefileStore) PutByHost(host string, ip net.IP) {
c.cache.PutFakeip([]byte(host), ip.AsSlice()) c.cache.PutFakeip([]byte(host), ip)
} }
// GetByIP implements store.GetByIP // GetByIP implements store.GetByIP
func (c *cachefileStore) GetByIP(ip netip.Addr) (string, bool) { func (c *cachefileStore) GetByIP(ip net.IP) (string, bool) {
elm := c.cache.GetFakeip(ip.AsSlice()) elm := c.cache.GetFakeip(ip.To4())
if elm == nil { if elm == nil {
return "", false return "", false
} }
@@ -39,18 +34,18 @@ func (c *cachefileStore) GetByIP(ip netip.Addr) (string, bool) {
} }
// PutByIP implements store.PutByIP // PutByIP implements store.PutByIP
func (c *cachefileStore) PutByIP(ip netip.Addr, host string) { func (c *cachefileStore) PutByIP(ip net.IP, host string) {
c.cache.PutFakeip(ip.AsSlice(), []byte(host)) c.cache.PutFakeip(ip.To4(), []byte(host))
} }
// DelByIP implements store.DelByIP // DelByIP implements store.DelByIP
func (c *cachefileStore) DelByIP(ip netip.Addr) { func (c *cachefileStore) DelByIP(ip net.IP) {
addr := ip.AsSlice() ip = ip.To4()
c.cache.DelFakeipPair(addr, c.cache.GetFakeip(addr)) c.cache.DelFakeipPair(ip, c.cache.GetFakeip(ip.To4()))
} }
// Exist implements store.Exist // Exist implements store.Exist
func (c *cachefileStore) Exist(ip netip.Addr) bool { func (c *cachefileStore) Exist(ip net.IP) bool {
_, exist := c.GetByIP(ip) _, exist := c.GetByIP(ip)
return exist return exist
} }

View File

@@ -1,37 +1,40 @@
package fakeip package fakeip
import ( import (
"net/netip" "net"
"github.com/Dreamacro/clash/common/cache" "github.com/Dreamacro/clash/common/cache"
) )
type memoryStore struct { type memoryStore struct {
cacheIP *cache.LruCache[string, netip.Addr] cache *cache.LruCache
cacheHost *cache.LruCache[netip.Addr, string]
} }
// GetByHost implements store.GetByHost // GetByHost implements store.GetByHost
func (m *memoryStore) GetByHost(host string) (netip.Addr, bool) { func (m *memoryStore) GetByHost(host string) (net.IP, bool) {
if ip, exist := m.cacheIP.Get(host); exist { if elm, exist := m.cache.Get(host); exist {
ip := elm.(net.IP)
// ensure ip --> host on head of linked list // ensure ip --> host on head of linked list
m.cacheHost.Get(ip) m.cache.Get(ipToUint(ip.To4()))
return ip, true return ip, true
} }
return netip.Addr{}, false return nil, false
} }
// PutByHost implements store.PutByHost // PutByHost implements store.PutByHost
func (m *memoryStore) PutByHost(host string, ip netip.Addr) { func (m *memoryStore) PutByHost(host string, ip net.IP) {
m.cacheIP.Set(host, ip) m.cache.Set(host, ip)
} }
// GetByIP implements store.GetByIP // GetByIP implements store.GetByIP
func (m *memoryStore) GetByIP(ip netip.Addr) (string, bool) { func (m *memoryStore) GetByIP(ip net.IP) (string, bool) {
if host, exist := m.cacheHost.Get(ip); exist { if elm, exist := m.cache.Get(ipToUint(ip.To4())); exist {
host := elm.(string)
// ensure host --> ip on head of linked list // ensure host --> ip on head of linked list
m.cacheIP.Get(host) m.cache.Get(host)
return host, true return host, true
} }
@@ -39,41 +42,33 @@ func (m *memoryStore) GetByIP(ip netip.Addr) (string, bool) {
} }
// PutByIP implements store.PutByIP // PutByIP implements store.PutByIP
func (m *memoryStore) PutByIP(ip netip.Addr, host string) { func (m *memoryStore) PutByIP(ip net.IP, host string) {
m.cacheHost.Set(ip, host) m.cache.Set(ipToUint(ip.To4()), host)
} }
// DelByIP implements store.DelByIP // DelByIP implements store.DelByIP
func (m *memoryStore) DelByIP(ip netip.Addr) { func (m *memoryStore) DelByIP(ip net.IP) {
if host, exist := m.cacheHost.Get(ip); exist { ipNum := ipToUint(ip.To4())
m.cacheIP.Delete(host) if elm, exist := m.cache.Get(ipNum); exist {
m.cache.Delete(elm.(string))
} }
m.cacheHost.Delete(ip) m.cache.Delete(ipNum)
} }
// Exist implements store.Exist // Exist implements store.Exist
func (m *memoryStore) Exist(ip netip.Addr) bool { func (m *memoryStore) Exist(ip net.IP) bool {
return m.cacheHost.Exist(ip) return m.cache.Exist(ipToUint(ip.To4()))
} }
// CloneTo implements store.CloneTo // CloneTo implements store.CloneTo
// only for memoryStore to memoryStore // only for memoryStore to memoryStore
func (m *memoryStore) CloneTo(store store) { func (m *memoryStore) CloneTo(store store) {
if ms, ok := store.(*memoryStore); ok { if ms, ok := store.(*memoryStore); ok {
m.cacheIP.CloneTo(ms.cacheIP) m.cache.CloneTo(ms.cache)
m.cacheHost.CloneTo(ms.cacheHost)
} }
} }
// FlushFakeIP implements store.FlushFakeIP // FlushFakeIP implements store.FlushFakeIP
func (m *memoryStore) FlushFakeIP() error { func (m *memoryStore) FlushFakeIP() error {
_ = m.cacheIP.Clear() return m.cache.Clear()
return m.cacheHost.Clear()
}
func newMemoryStore(size int) *memoryStore {
return &memoryStore{
cacheIP: cache.NewLRUCache[string, netip.Addr](cache.WithSize[string, netip.Addr](size)),
cacheHost: cache.NewLRUCache[netip.Addr, string](cache.WithSize[netip.Addr, string](size)),
}
} }

View File

@@ -1,47 +1,41 @@
package fakeip package fakeip
import ( import (
"encoding/binary"
"errors" "errors"
"math/bits" "net"
"net/netip"
"sync" "sync"
"github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/component/profile/cachefile" "github.com/Dreamacro/clash/component/profile/cachefile"
"github.com/Dreamacro/clash/component/trie" "github.com/Dreamacro/clash/component/trie"
) )
type uint128 struct {
hi uint64
lo uint64
}
type store interface { type store interface {
GetByHost(host string) (netip.Addr, bool) GetByHost(host string) (net.IP, bool)
PutByHost(host string, ip netip.Addr) PutByHost(host string, ip net.IP)
GetByIP(ip netip.Addr) (string, bool) GetByIP(ip net.IP) (string, bool)
PutByIP(ip netip.Addr, host string) PutByIP(ip net.IP, host string)
DelByIP(ip netip.Addr) DelByIP(ip net.IP)
Exist(ip netip.Addr) bool Exist(ip net.IP) bool
CloneTo(store) CloneTo(store)
FlushFakeIP() error FlushFakeIP() error
} }
// Pool is a implementation about fake ip generator without storage // Pool is a implementation about fake ip generator without storage
type Pool struct { type Pool struct {
gateway netip.Addr max uint32
first netip.Addr min uint32
last netip.Addr gateway uint32
offset netip.Addr broadcast uint32
cycle bool offset uint32
mux sync.Mutex mux sync.Mutex
host *trie.DomainTrie[bool] host *trie.DomainTrie
ipnet *netip.Prefix ipnet *net.IPNet
store store store store
} }
// Lookup return a fake ip with host // Lookup return a fake ip with host
func (p *Pool) Lookup(host string) netip.Addr { func (p *Pool) Lookup(host string) net.IP {
p.mux.Lock() p.mux.Lock()
defer p.mux.Unlock() defer p.mux.Unlock()
if ip, exist := p.store.GetByHost(host); exist { if ip, exist := p.store.GetByHost(host); exist {
@@ -54,10 +48,14 @@ func (p *Pool) Lookup(host string) netip.Addr {
} }
// LookBack return host with the fake ip // LookBack return host with the fake ip
func (p *Pool) LookBack(ip netip.Addr) (string, bool) { func (p *Pool) LookBack(ip net.IP) (string, bool) {
p.mux.Lock() p.mux.Lock()
defer p.mux.Unlock() defer p.mux.Unlock()
if ip = ip.To4(); ip == nil {
return "", false
}
return p.store.GetByIP(ip) return p.store.GetByIP(ip)
} }
@@ -70,25 +68,29 @@ func (p *Pool) ShouldSkipped(domain string) bool {
} }
// Exist returns if given ip exists in fake-ip pool // Exist returns if given ip exists in fake-ip pool
func (p *Pool) Exist(ip netip.Addr) bool { func (p *Pool) Exist(ip net.IP) bool {
p.mux.Lock() p.mux.Lock()
defer p.mux.Unlock() defer p.mux.Unlock()
if ip = ip.To4(); ip == nil {
return false
}
return p.store.Exist(ip) return p.store.Exist(ip)
} }
// Gateway return gateway ip // Gateway return gateway ip
func (p *Pool) Gateway() netip.Addr { func (p *Pool) Gateway() net.IP {
return p.gateway return uintToIP(p.gateway)
} }
// Broadcast return the last ip // Broadcast return broadcast ip
func (p *Pool) Broadcast() netip.Addr { func (p *Pool) Broadcast() net.IP {
return p.last return uintToIP(p.broadcast)
} }
// IPNet return raw ipnet // IPNet return raw ipnet
func (p *Pool) IPNet() *netip.Prefix { func (p *Pool) IPNet() *net.IPNet {
return p.ipnet return p.ipnet
} }
@@ -97,36 +99,47 @@ func (p *Pool) CloneFrom(o *Pool) {
o.store.CloneTo(p.store) o.store.CloneTo(p.store)
} }
func (p *Pool) get(host string) netip.Addr { func (p *Pool) get(host string) net.IP {
current := p.offset
for { for {
p.offset = p.offset.Next() p.offset = (p.offset + 1) % (p.max - p.min)
// Avoid infinite loops
if !p.offset.Less(p.last) { if p.offset == current {
p.cycle = true p.offset = (p.offset + 1) % (p.max - p.min)
p.offset = p.first ip := uintToIP(p.min + p.offset - 1)
} p.store.DelByIP(ip)
if p.cycle {
p.store.DelByIP(p.offset)
break break
} }
if !p.store.Exist(p.offset) { ip := uintToIP(p.min + p.offset - 1)
if !p.store.Exist(ip) {
break break
} }
} }
ip := uintToIP(p.min + p.offset - 1)
p.store.PutByIP(p.offset, host) p.store.PutByIP(ip, host)
return p.offset return ip
} }
func (p *Pool) FlushFakeIP() error { func (p *Pool) FlushFakeIP() error {
return p.store.FlushFakeIP() return p.store.FlushFakeIP()
} }
func ipToUint(ip net.IP) uint32 {
v := uint32(ip[0]) << 24
v += uint32(ip[1]) << 16
v += uint32(ip[2]) << 8
v += uint32(ip[3])
return v
}
func uintToIP(v uint32) net.IP {
return net.IP{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}
}
type Options struct { type Options struct {
IPNet *netip.Prefix IPNet *net.IPNet
Host *trie.DomainTrie[bool] Host *trie.DomainTrie
// Size sets the maximum number of entries in memory // Size sets the maximum number of entries in memory
// and does not work if Persistence is true // and does not work if Persistence is true
@@ -139,59 +152,33 @@ type Options struct {
// New return Pool instance // New return Pool instance
func New(options Options) (*Pool, error) { func New(options Options) (*Pool, error) {
var ( min := ipToUint(options.IPNet.IP) + 3
hostAddr = options.IPNet.Masked().Addr()
gateway = hostAddr.Next()
first = gateway.Next().Next()
last = add(hostAddr, 1<<uint64(hostAddr.BitLen()-options.IPNet.Bits())-1)
)
if !options.IPNet.IsValid() || !first.Less(last) || !options.IPNet.Contains(last) { ones, bits := options.IPNet.Mask.Size()
total := 1<<uint(bits-ones) - 4
if total <= 0 {
return nil, errors.New("ipnet don't have valid ip") return nil, errors.New("ipnet don't have valid ip")
} }
max := min + uint32(total) - 1
pool := &Pool{ pool := &Pool{
gateway: gateway, min: min,
first: first, max: max,
last: last, gateway: min - 2,
offset: first.Prev(), broadcast: max + 1,
cycle: false, host: options.Host,
host: options.Host, ipnet: options.IPNet,
ipnet: options.IPNet,
} }
if options.Persistence { if options.Persistence {
pool.store = &cachefileStore{ pool.store = &cachefileStore{
cache: cachefile.Cache(), cache: cachefile.Cache(),
} }
} else { } else {
pool.store = newMemoryStore(options.Size) pool.store = &memoryStore{
cache: cache.NewLRUCache(cache.WithSize(options.Size * 2)),
}
} }
return pool, nil return pool, nil
} }
// add returns addr + n.
func add(addr netip.Addr, n uint64) netip.Addr {
buf := addr.As16()
u := uint128{
binary.BigEndian.Uint64(buf[:8]),
binary.BigEndian.Uint64(buf[8:]),
}
lo, carry := bits.Add64(u.lo, n, 0)
u.hi = u.hi + carry
u.lo = lo
binary.BigEndian.PutUint64(buf[:8], u.hi)
binary.BigEndian.PutUint64(buf[8:], u.lo)
a := netip.AddrFrom16(buf)
if addr.Is4() {
return a.Unmap()
}
return a
}

View File

@@ -2,7 +2,7 @@ package fakeip
import ( import (
"fmt" "fmt"
"net/netip" "net"
"os" "os"
"testing" "testing"
"time" "time"
@@ -49,9 +49,9 @@ func createCachefileStore(options Options) (*Pool, string, error) {
} }
func TestPool_Basic(t *testing.T) { func TestPool_Basic(t *testing.T) {
ipnet := netip.MustParsePrefix("192.168.0.0/28") _, ipnet, _ := net.ParseCIDR("192.168.0.0/28")
pools, tempfile, err := createPools(Options{ pools, tempfile, err := createPools(Options{
IPNet: &ipnet, IPNet: ipnet,
Size: 10, Size: 10,
}) })
assert.Nil(t, err) assert.Nil(t, err)
@@ -62,52 +62,24 @@ func TestPool_Basic(t *testing.T) {
last := pool.Lookup("bar.com") last := pool.Lookup("bar.com")
bar, exist := pool.LookBack(last) bar, exist := pool.LookBack(last)
assert.True(t, first == netip.AddrFrom4([4]byte{192, 168, 0, 3})) assert.True(t, first.Equal(net.IP{192, 168, 0, 3}))
assert.True(t, pool.Lookup("foo.com") == netip.AddrFrom4([4]byte{192, 168, 0, 3})) assert.Equal(t, pool.Lookup("foo.com"), net.IP{192, 168, 0, 3})
assert.True(t, last == netip.AddrFrom4([4]byte{192, 168, 0, 4})) assert.True(t, last.Equal(net.IP{192, 168, 0, 4}))
assert.True(t, exist) assert.True(t, exist)
assert.Equal(t, bar, "bar.com") assert.Equal(t, bar, "bar.com")
assert.True(t, pool.Gateway() == netip.AddrFrom4([4]byte{192, 168, 0, 1})) assert.Equal(t, pool.Gateway(), net.IP{192, 168, 0, 1})
assert.True(t, pool.Broadcast() == netip.AddrFrom4([4]byte{192, 168, 0, 15})) assert.Equal(t, pool.Broadcast(), net.IP{192, 168, 0, 15})
assert.Equal(t, pool.IPNet().String(), ipnet.String()) assert.Equal(t, pool.IPNet().String(), ipnet.String())
assert.True(t, pool.Exist(netip.AddrFrom4([4]byte{192, 168, 0, 4}))) assert.True(t, pool.Exist(net.IP{192, 168, 0, 4}))
assert.False(t, pool.Exist(netip.AddrFrom4([4]byte{192, 168, 0, 5}))) assert.False(t, pool.Exist(net.IP{192, 168, 0, 5}))
assert.False(t, pool.Exist(netip.MustParseAddr("::1"))) assert.False(t, pool.Exist(net.ParseIP("::1")))
}
}
func TestPool_BasicV6(t *testing.T) {
ipnet := netip.MustParsePrefix("2001:4860:4860::8888/118")
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("bar.com")
bar, exist := pool.LookBack(last)
assert.True(t, first == netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8803"))
assert.True(t, pool.Lookup("foo.com") == netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8803"))
assert.True(t, last == netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8804"))
assert.True(t, exist)
assert.Equal(t, bar, "bar.com")
assert.True(t, pool.Gateway() == netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8801"))
assert.True(t, pool.Broadcast() == netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8bff"))
assert.Equal(t, pool.IPNet().String(), ipnet.String())
assert.True(t, pool.Exist(netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8804")))
assert.False(t, pool.Exist(netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8805")))
assert.False(t, pool.Exist(netip.MustParseAddr("127.0.0.1")))
} }
} }
func TestPool_CycleUsed(t *testing.T) { func TestPool_CycleUsed(t *testing.T) {
ipnet := netip.MustParsePrefix("192.168.0.16/28") _, ipnet, _ := net.ParseCIDR("192.168.0.16/28")
pools, tempfile, err := createPools(Options{ pools, tempfile, err := createPools(Options{
IPNet: &ipnet, IPNet: ipnet,
Size: 10, Size: 10,
}) })
assert.Nil(t, err) assert.Nil(t, err)
@@ -116,22 +88,22 @@ func TestPool_CycleUsed(t *testing.T) {
for _, pool := range pools { for _, pool := range pools {
foo := pool.Lookup("foo.com") foo := pool.Lookup("foo.com")
bar := pool.Lookup("bar.com") bar := pool.Lookup("bar.com")
for i := 0; i < 10; i++ { for i := 0; i < 9; i++ {
pool.Lookup(fmt.Sprintf("%d.com", i)) pool.Lookup(fmt.Sprintf("%d.com", i))
} }
baz := pool.Lookup("baz.com") baz := pool.Lookup("baz.com")
next := pool.Lookup("foo.com") next := pool.Lookup("foo.com")
assert.True(t, foo == baz) assert.True(t, foo.Equal(baz))
assert.True(t, next == bar) assert.True(t, next.Equal(bar))
} }
} }
func TestPool_Skip(t *testing.T) { func TestPool_Skip(t *testing.T) {
ipnet := netip.MustParsePrefix("192.168.0.1/29") _, ipnet, _ := net.ParseCIDR("192.168.0.1/29")
tree := trie.New[bool]() tree := trie.New()
tree.Insert("example.com", true) tree.Insert("example.com", tree)
pools, tempfile, err := createPools(Options{ pools, tempfile, err := createPools(Options{
IPNet: &ipnet, IPNet: ipnet,
Size: 10, Size: 10,
Host: tree, Host: tree,
}) })
@@ -145,9 +117,9 @@ func TestPool_Skip(t *testing.T) {
} }
func TestPool_MaxCacheSize(t *testing.T) { func TestPool_MaxCacheSize(t *testing.T) {
ipnet := netip.MustParsePrefix("192.168.0.1/24") _, ipnet, _ := net.ParseCIDR("192.168.0.1/24")
pool, _ := New(Options{ pool, _ := New(Options{
IPNet: &ipnet, IPNet: ipnet,
Size: 2, Size: 2,
}) })
@@ -156,13 +128,13 @@ func TestPool_MaxCacheSize(t *testing.T) {
pool.Lookup("baz.com") pool.Lookup("baz.com")
next := pool.Lookup("foo.com") next := pool.Lookup("foo.com")
assert.False(t, first == next) assert.False(t, first.Equal(next))
} }
func TestPool_DoubleMapping(t *testing.T) { func TestPool_DoubleMapping(t *testing.T) {
ipnet := netip.MustParsePrefix("192.168.0.1/24") _, ipnet, _ := net.ParseCIDR("192.168.0.1/24")
pool, _ := New(Options{ pool, _ := New(Options{
IPNet: &ipnet, IPNet: ipnet,
Size: 2, Size: 2,
}) })
@@ -186,23 +158,23 @@ func TestPool_DoubleMapping(t *testing.T) {
assert.False(t, bazExist) assert.False(t, bazExist)
assert.True(t, barExist) assert.True(t, barExist)
assert.False(t, bazIP == newBazIP) assert.False(t, bazIP.Equal(newBazIP))
} }
func TestPool_Clone(t *testing.T) { func TestPool_Clone(t *testing.T) {
ipnet := netip.MustParsePrefix("192.168.0.1/24") _, ipnet, _ := net.ParseCIDR("192.168.0.1/24")
pool, _ := New(Options{ pool, _ := New(Options{
IPNet: &ipnet, IPNet: ipnet,
Size: 2, Size: 2,
}) })
first := pool.Lookup("foo.com") first := pool.Lookup("foo.com")
last := pool.Lookup("bar.com") last := pool.Lookup("bar.com")
assert.True(t, first == netip.AddrFrom4([4]byte{192, 168, 0, 3})) assert.True(t, first.Equal(net.IP{192, 168, 0, 3}))
assert.True(t, last == netip.AddrFrom4([4]byte{192, 168, 0, 4})) assert.True(t, last.Equal(net.IP{192, 168, 0, 4}))
newPool, _ := New(Options{ newPool, _ := New(Options{
IPNet: &ipnet, IPNet: ipnet,
Size: 2, Size: 2,
}) })
newPool.CloneFrom(pool) newPool.CloneFrom(pool)
@@ -213,9 +185,9 @@ func TestPool_Clone(t *testing.T) {
} }
func TestPool_Error(t *testing.T) { func TestPool_Error(t *testing.T) {
ipnet := netip.MustParsePrefix("192.168.0.1/31") _, ipnet, _ := net.ParseCIDR("192.168.0.1/31")
_, err := New(Options{ _, err := New(Options{
IPNet: &ipnet, IPNet: ipnet,
Size: 10, Size: 10,
}) })
@@ -223,9 +195,9 @@ func TestPool_Error(t *testing.T) {
} }
func TestPool_FlushFileCache(t *testing.T) { func TestPool_FlushFileCache(t *testing.T) {
ipnet := netip.MustParsePrefix("192.168.0.1/28") _, ipnet, _ := net.ParseCIDR("192.168.0.1/28")
pools, tempfile, err := createPools(Options{ pools, tempfile, err := createPools(Options{
IPNet: &ipnet, IPNet: ipnet,
Size: 10, Size: 10,
}) })
assert.Nil(t, err) assert.Nil(t, err)
@@ -244,18 +216,18 @@ func TestPool_FlushFileCache(t *testing.T) {
next := pool.Lookup("baz.com") next := pool.Lookup("baz.com")
nero := pool.Lookup("foo.com") nero := pool.Lookup("foo.com")
assert.True(t, foo == fox) assert.Equal(t, foo, fox)
assert.False(t, foo == baz) assert.NotEqual(t, foo, baz)
assert.True(t, bar == bax) assert.Equal(t, bar, bax)
assert.False(t, bar == next) assert.NotEqual(t, bar, next)
assert.True(t, baz == nero) assert.Equal(t, baz, nero)
} }
} }
func TestPool_FlushMemoryCache(t *testing.T) { func TestPool_FlushMemoryCache(t *testing.T) {
ipnet := netip.MustParsePrefix("192.168.0.1/28") _, ipnet, _ := net.ParseCIDR("192.168.0.1/28")
pool, _ := New(Options{ pool, _ := New(Options{
IPNet: &ipnet, IPNet: ipnet,
Size: 10, Size: 10,
}) })
@@ -271,9 +243,9 @@ func TestPool_FlushMemoryCache(t *testing.T) {
next := pool.Lookup("baz.com") next := pool.Lookup("baz.com")
nero := pool.Lookup("foo.com") nero := pool.Lookup("foo.com")
assert.True(t, foo == fox) assert.Equal(t, foo, fox)
assert.False(t, foo == baz) assert.NotEqual(t, foo, baz)
assert.True(t, bar == bax) assert.Equal(t, bar, bax)
assert.False(t, bar == next) assert.NotEqual(t, bar, next)
assert.True(t, baz == nero) assert.Equal(t, baz, nero)
} }

View File

@@ -15,9 +15,6 @@ func LoaderName() string {
} }
func SetLoader(newLoader string) { func SetLoader(newLoader string) {
if newLoader == "memc" {
newLoader = "memconservative"
}
geoLoaderName = newLoader geoLoaderName = newLoader
} }

View File

@@ -3,7 +3,6 @@ package process
import ( import (
"errors" "errors"
"net" "net"
"runtime"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
) )
@@ -24,9 +23,6 @@ func FindProcessName(network string, srcIP net.IP, srcPort int) (string, error)
} }
func ShouldFindProcess(metadata *C.Metadata) bool { func ShouldFindProcess(metadata *C.Metadata) bool {
if runtime.GOOS == "android" {
return false
}
if metadata.Process != "" { if metadata.Process != "" {
return false return false
} }

View File

@@ -0,0 +1,230 @@
package process
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"os"
"path"
"path/filepath"
"strings"
"syscall"
"unicode"
"unsafe"
"github.com/Dreamacro/clash/common/pool"
)
// from https://github.com/vishvananda/netlink/blob/bca67dfc8220b44ef582c9da4e9172bf1c9ec973/nl/nl_linux.go#L52-L62
var nativeEndian = func() binary.ByteOrder {
var x uint32 = 0x01020304
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
return binary.BigEndian
}
return binary.LittleEndian
}()
const (
sizeOfSocketDiagRequest = syscall.SizeofNlMsghdr + 8 + 48
socketDiagByFamily = 20
pathProc = "/proc"
)
func findProcessName(network string, ip net.IP, srcPort int) (string, error) {
inode, uid, err := resolveSocketByNetlink(network, ip, srcPort)
if err != nil {
return "", err
}
return resolveProcessNameByProcSearch(inode, uid)
}
func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int32, int32, error) {
var family byte
var protocol byte
switch network {
case TCP:
protocol = syscall.IPPROTO_TCP
case UDP:
protocol = syscall.IPPROTO_UDP
default:
return 0, 0, ErrInvalidNetwork
}
if ip.To4() != nil {
family = syscall.AF_INET
} else {
family = syscall.AF_INET6
}
req := packSocketDiagRequest(family, protocol, ip, uint16(srcPort))
socket, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM, syscall.NETLINK_INET_DIAG)
if err != nil {
return 0, 0, fmt.Errorf("dial netlink: %w", err)
}
defer syscall.Close(socket)
syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &syscall.Timeval{Usec: 100})
syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &syscall.Timeval{Usec: 100})
if err := syscall.Connect(socket, &syscall.SockaddrNetlink{
Family: syscall.AF_NETLINK,
Pad: 0,
Pid: 0,
Groups: 0,
}); err != nil {
return 0, 0, err
}
if _, err := syscall.Write(socket, req); err != nil {
return 0, 0, fmt.Errorf("write request: %w", err)
}
rb := pool.Get(pool.RelayBufferSize)
defer pool.Put(rb)
n, err := syscall.Read(socket, rb)
if err != nil {
return 0, 0, fmt.Errorf("read response: %w", err)
}
messages, err := syscall.ParseNetlinkMessage(rb[:n])
if err != nil {
return 0, 0, fmt.Errorf("parse netlink message: %w", err)
} else if len(messages) == 0 {
return 0, 0, fmt.Errorf("unexcepted netlink response")
}
message := messages[0]
if message.Header.Type&syscall.NLMSG_ERROR != 0 {
return 0, 0, fmt.Errorf("netlink message: NLMSG_ERROR")
}
uid, inode := unpackSocketDiagResponse(&messages[0])
if uid < 0 || inode < 0 {
return 0, 0, fmt.Errorf("invalid uid(%d) or inode(%d)", uid, inode)
}
return uid, inode, nil
}
func packSocketDiagRequest(family, protocol byte, source net.IP, sourcePort uint16) []byte {
s := make([]byte, 16)
if v4 := source.To4(); v4 != nil {
copy(s, v4)
} else {
copy(s, source)
}
buf := make([]byte, sizeOfSocketDiagRequest)
nativeEndian.PutUint32(buf[0:4], sizeOfSocketDiagRequest)
nativeEndian.PutUint16(buf[4:6], socketDiagByFamily)
nativeEndian.PutUint16(buf[6:8], syscall.NLM_F_REQUEST|syscall.NLM_F_DUMP)
nativeEndian.PutUint32(buf[8:12], 0)
nativeEndian.PutUint32(buf[12:16], 0)
buf[16] = family
buf[17] = protocol
buf[18] = 0
buf[19] = 0
nativeEndian.PutUint32(buf[20:24], 0xFFFFFFFF)
binary.BigEndian.PutUint16(buf[24:26], sourcePort)
binary.BigEndian.PutUint16(buf[26:28], 0)
copy(buf[28:44], s)
copy(buf[44:60], net.IPv6zero)
nativeEndian.PutUint32(buf[60:64], 0)
nativeEndian.PutUint64(buf[64:72], 0xFFFFFFFFFFFFFFFF)
return buf
}
func unpackSocketDiagResponse(msg *syscall.NetlinkMessage) (inode, uid int32) {
if len(msg.Data) < 72 {
return 0, 0
}
data := msg.Data
uid = int32(nativeEndian.Uint32(data[64:68]))
inode = int32(nativeEndian.Uint32(data[68:72]))
return
}
func resolveProcessNameByProcSearch(inode, uid int32) (string, error) {
files, err := os.ReadDir(pathProc)
if err != nil {
return "", err
}
buffer := make([]byte, syscall.PathMax)
socket := []byte(fmt.Sprintf("socket:[%d]", inode))
for _, f := range files {
if !f.IsDir() || !isPid(f.Name()) {
continue
}
info, err := f.Info()
if err != nil {
return "", err
}
if info.Sys().(*syscall.Stat_t).Uid != uint32(uid) {
continue
}
processPath := path.Join(pathProc, f.Name())
fdPath := path.Join(processPath, "fd")
fds, err := os.ReadDir(fdPath)
if err != nil {
continue
}
for _, fd := range fds {
n, err := syscall.Readlink(path.Join(fdPath, fd.Name()), buffer)
if err != nil {
continue
}
if bytes.Equal(buffer[:n], socket) {
cmdline, err := os.ReadFile(path.Join(processPath, "cmdline"))
if err != nil {
return "", err
}
return splitCmdline(cmdline), nil
}
}
}
return "", fmt.Errorf("process of uid(%d),inode(%d) not found", uid, inode)
}
func splitCmdline(cmdline []byte) string {
cmdline = bytes.Trim(cmdline, " ")
idx := bytes.IndexFunc(cmdline, func(r rune) bool {
return unicode.IsControl(r) || unicode.IsSpace(r)
})
if idx == -1 {
return filepath.Base(string(cmdline))
}
return filepath.Base(string(cmdline[:idx]))
}
func isPid(s string) bool {
return strings.IndexFunc(s, func(r rune) bool {
return !unicode.IsDigit(r)
}) == -1
}

View File

@@ -1,3 +1,5 @@
//go:build !android
package process package process
import ( import (

View File

@@ -14,7 +14,6 @@ type Enhancer interface {
IsExistFakeIP(net.IP) bool IsExistFakeIP(net.IP) bool
FindHostByIP(net.IP) (string, bool) FindHostByIP(net.IP) (string, bool)
FlushFakeIP() error FlushFakeIP() error
InsertHostByIP(net.IP, string)
} }
func FakeIPEnabled() bool { func FakeIPEnabled() bool {
@@ -57,12 +56,6 @@ func IsExistFakeIP(ip net.IP) bool {
return false return false
} }
func InsertHostByIP(ip net.IP, host string) {
if mapper := DefaultHostMapper; mapper != nil {
mapper.InsertHostByIP(ip, host)
}
}
func FindHostByIP(ip net.IP) (string, bool) { func FindHostByIP(ip net.IP) (string, bool) {
if mapper := DefaultHostMapper; mapper != nil { if mapper := DefaultHostMapper; mapper != nil {
return mapper.FindHostByIP(ip) return mapper.FindHostByIP(ip)

View File

@@ -5,7 +5,6 @@ import (
"errors" "errors"
"math/rand" "math/rand"
"net" "net"
"net/netip"
"strings" "strings"
"time" "time"
@@ -24,7 +23,7 @@ var (
DisableIPv6 = true DisableIPv6 = true
// DefaultHosts aim to resolve hosts // DefaultHosts aim to resolve hosts
DefaultHosts = trie.New[netip.Addr]() DefaultHosts = trie.New()
// DefaultDNSTimeout defined the default dns request timeout // DefaultDNSTimeout defined the default dns request timeout
DefaultDNSTimeout = time.Second * 5 DefaultDNSTimeout = time.Second * 5
@@ -49,8 +48,8 @@ func ResolveIPv4(host string) (net.IP, error) {
func ResolveIPv4WithResolver(host string, r Resolver) (net.IP, error) { func ResolveIPv4WithResolver(host string, r Resolver) (net.IP, error) {
if node := DefaultHosts.Search(host); node != nil { if node := DefaultHosts.Search(host); node != nil {
if ip := node.Data; ip.Is4() { if ip := node.Data.(net.IP).To4(); ip != nil {
return ip.AsSlice(), nil return ip, nil
} }
} }
@@ -93,8 +92,8 @@ func ResolveIPv6WithResolver(host string, r Resolver) (net.IP, error) {
} }
if node := DefaultHosts.Search(host); node != nil { if node := DefaultHosts.Search(host); node != nil {
if ip := node.Data; ip.Is6() { if ip := node.Data.(net.IP).To16(); ip != nil {
return ip.AsSlice(), nil return ip, nil
} }
} }
@@ -129,8 +128,7 @@ func ResolveIPv6WithResolver(host string, r Resolver) (net.IP, error) {
// ResolveIPWithResolver same as ResolveIP, but with a resolver // ResolveIPWithResolver same as ResolveIP, but with a resolver
func ResolveIPWithResolver(host string, r Resolver) (net.IP, error) { func ResolveIPWithResolver(host string, r Resolver) (net.IP, error) {
if node := DefaultHosts.Search(host); node != nil { if node := DefaultHosts.Search(host); node != nil {
ip := node.Data return node.Data.(net.IP), nil
return ip.Unmap().AsSlice(), nil
} }
if r != nil { if r != nil {

View File

@@ -1,149 +0,0 @@
package sniffer
import (
"errors"
"net"
"strconv"
"github.com/Dreamacro/clash/component/trie"
CN "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/utils"
"github.com/Dreamacro/clash/component/resolver"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
)
var (
ErrorUnsupportedSniffer = errors.New("unsupported sniffer")
ErrorSniffFailed = errors.New("all sniffer failed")
)
var Dispatcher SnifferDispatcher
type SnifferDispatcher struct {
enable bool
sniffers []C.Sniffer
foreDomain *trie.DomainTrie[bool]
skipSNI *trie.DomainTrie[bool]
portRanges *[]utils.Range[uint16]
}
func (sd *SnifferDispatcher) TCPSniff(conn net.Conn, metadata *C.Metadata) {
bufConn, ok := conn.(*CN.BufferedConn)
if !ok {
return
}
if metadata.Host == "" || sd.foreDomain.Search(metadata.Host) != nil {
port, err := strconv.ParseUint(metadata.DstPort, 10, 16)
if err != nil {
log.Debugln("[Sniffer] Dst port is error")
return
}
for _, portRange := range *sd.portRanges {
if !portRange.Contains(uint16(port)) {
return
}
}
if host, err := sd.sniffDomain(bufConn, metadata); err != nil {
log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%s] to [%s:%s]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort)
return
} else {
if sd.skipSNI.Search(host) != nil {
log.Debugln("[Sniffer] Skip sni[%s]", host)
return
}
sd.replaceDomain(metadata, host)
}
}
}
func (sd *SnifferDispatcher) replaceDomain(metadata *C.Metadata, host string) {
log.Debugln("[Sniffer] Sniff TCP [%s:%s]-->[%s:%s] success, replace domain [%s]-->[%s]",
metadata.SrcIP, metadata.SrcPort,
metadata.DstIP, metadata.DstPort,
metadata.Host, host)
metadata.AddrType = C.AtypDomainName
metadata.Host = host
metadata.DNSMode = C.DNSMapping
resolver.InsertHostByIP(metadata.DstIP, host)
metadata.DstIP = nil
}
func (sd *SnifferDispatcher) Enable() bool {
return sd.enable
}
func (sd *SnifferDispatcher) sniffDomain(conn *CN.BufferedConn, metadata *C.Metadata) (string, error) {
for _, sniffer := range sd.sniffers {
if sniffer.SupportNetwork() == C.TCP {
_, err := conn.Peek(1)
if err != nil {
return "", err
}
bufferedLen := conn.Buffered()
bytes, err := conn.Peek(bufferedLen)
if err != nil {
log.Debugln("[Sniffer] the data length not enough")
continue
}
host, err := sniffer.SniffTCP(bytes)
if err != nil {
log.Debugln("[Sniffer] [%s] Sniff data failed %s", sniffer.Protocol(), metadata.DstIP)
continue
}
return host, nil
}
}
return "", ErrorSniffFailed
}
func NewCloseSnifferDispatcher() (*SnifferDispatcher, error) {
dispatcher := SnifferDispatcher{
enable: false,
}
return &dispatcher, nil
}
func NewSnifferDispatcher(needSniffer []C.SnifferType, forceDomain *trie.DomainTrie[bool],
skipSNI *trie.DomainTrie[bool], ports *[]utils.Range[uint16]) (*SnifferDispatcher, error) {
dispatcher := SnifferDispatcher{
enable: true,
foreDomain: forceDomain,
skipSNI: skipSNI,
portRanges: ports,
}
for _, snifferName := range needSniffer {
sniffer, err := NewSniffer(snifferName)
if err != nil {
log.Errorln("Sniffer name[%s] is error", snifferName)
return &SnifferDispatcher{enable: false}, err
}
dispatcher.sniffers = append(dispatcher.sniffers, sniffer)
}
return &dispatcher, nil
}
func NewSniffer(name C.SnifferType) (C.Sniffer, error) {
switch name {
case C.TLS:
return &TLSSniffer{}, nil
default:
return nil, ErrorUnsupportedSniffer
}
}

View File

@@ -1,159 +0,0 @@
package sniffer
import (
"testing"
)
func TestTLSHeaders(t *testing.T) {
cases := []struct {
input []byte
domain string
err bool
}{
{
input: []byte{
0x16, 0x03, 0x01, 0x00, 0xc8, 0x01, 0x00, 0x00,
0xc4, 0x03, 0x03, 0x1a, 0xac, 0xb2, 0xa8, 0xfe,
0xb4, 0x96, 0x04, 0x5b, 0xca, 0xf7, 0xc1, 0xf4,
0x2e, 0x53, 0x24, 0x6e, 0x34, 0x0c, 0x58, 0x36,
0x71, 0x97, 0x59, 0xe9, 0x41, 0x66, 0xe2, 0x43,
0xa0, 0x13, 0xb6, 0x00, 0x00, 0x20, 0x1a, 0x1a,
0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30,
0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0x14, 0xcc, 0x13,
0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d,
0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00,
0x00, 0x7b, 0xba, 0xba, 0x00, 0x00, 0xff, 0x01,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00,
0x14, 0x00, 0x00, 0x11, 0x63, 0x2e, 0x73, 0x2d,
0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66,
0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x17, 0x00,
0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d, 0x00,
0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04,
0x01, 0x05, 0x03, 0x08, 0x05, 0x05, 0x01, 0x08,
0x06, 0x06, 0x01, 0x02, 0x01, 0x00, 0x05, 0x00,
0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x0c,
0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70,
0x2f, 0x31, 0x2e, 0x31, 0x00, 0x0b, 0x00, 0x02,
0x01, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08,
0xaa, 0xaa, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18,
0xaa, 0xaa, 0x00, 0x01, 0x00,
},
domain: "c.s-microsoft.com",
err: false,
},
{
input: []byte{
0x16, 0x03, 0x01, 0x00, 0xee, 0x01, 0x00, 0x00,
0xea, 0x03, 0x03, 0xe7, 0x91, 0x9e, 0x93, 0xca,
0x78, 0x1b, 0x3c, 0xe0, 0x65, 0x25, 0x58, 0xb5,
0x93, 0xe1, 0x0f, 0x85, 0xec, 0x9a, 0x66, 0x8e,
0x61, 0x82, 0x88, 0xc8, 0xfc, 0xae, 0x1e, 0xca,
0xd7, 0xa5, 0x63, 0x20, 0xbd, 0x1c, 0x00, 0x00,
0x8b, 0xee, 0x09, 0xe3, 0x47, 0x6a, 0x0e, 0x74,
0xb0, 0xbc, 0xa3, 0x02, 0xa7, 0x35, 0xe8, 0x85,
0x70, 0x7c, 0x7a, 0xf0, 0x00, 0xdf, 0x4a, 0xea,
0x87, 0x01, 0x14, 0x91, 0x00, 0x20, 0xea, 0xea,
0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30,
0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0x14, 0xcc, 0x13,
0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d,
0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00,
0x00, 0x81, 0x9a, 0x9a, 0x00, 0x00, 0xff, 0x01,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
0x16, 0x00, 0x00, 0x13, 0x77, 0x77, 0x77, 0x30,
0x37, 0x2e, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x74,
0x61, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x00,
0x17, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x0d, 0x00, 0x14, 0x00, 0x12, 0x04, 0x03, 0x08,
0x04, 0x04, 0x01, 0x05, 0x03, 0x08, 0x05, 0x05,
0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, 0x00,
0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e,
0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74,
0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x75, 0x50,
0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00,
0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x9a, 0x9a,
0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x8a, 0x8a,
0x00, 0x01, 0x00,
},
domain: "www07.clicktale.net",
err: false,
},
{
input: []byte{
0x16, 0x03, 0x01, 0x00, 0xe6, 0x01, 0x00, 0x00, 0xe2, 0x03, 0x03, 0x81, 0x47, 0xc1,
0x66, 0xd5, 0x1b, 0xfa, 0x4b, 0xb5, 0xe0, 0x2a, 0xe1, 0xa7, 0x87, 0x13, 0x1d, 0x11, 0xaa, 0xc6,
0xce, 0xfc, 0x7f, 0xab, 0x94, 0xc8, 0x62, 0xad, 0xc8, 0xab, 0x0c, 0xdd, 0xcb, 0x20, 0x6f, 0x9d,
0x07, 0xf1, 0x95, 0x3e, 0x99, 0xd8, 0xf3, 0x6d, 0x97, 0xee, 0x19, 0x0b, 0x06, 0x1b, 0xf4, 0x84,
0x0b, 0xb6, 0x8f, 0xcc, 0xde, 0xe2, 0xd0, 0x2d, 0x6b, 0x0c, 0x1f, 0x52, 0x53, 0x13, 0x00, 0x08,
0x13, 0x02, 0x13, 0x03, 0x13, 0x01, 0x00, 0xff, 0x01, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x0c,
0x00, 0x0a, 0x00, 0x00, 0x07, 0x64, 0x6f, 0x67, 0x66, 0x69, 0x73, 0x68, 0x00, 0x0b, 0x00, 0x04,
0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x0a, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x1e,
0x00, 0x19, 0x00, 0x18, 0x00, 0x23, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00,
0x00, 0x0d, 0x00, 0x1e, 0x00, 0x1c, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x07, 0x08, 0x08,
0x08, 0x09, 0x08, 0x0a, 0x08, 0x0b, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01,
0x06, 0x01, 0x00, 0x2b, 0x00, 0x07, 0x06, 0x7f, 0x1c, 0x7f, 0x1b, 0x7f, 0x1a, 0x00, 0x2d, 0x00,
0x02, 0x01, 0x01, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x2f, 0x35, 0x0c,
0xb6, 0x90, 0x0a, 0xb7, 0xd5, 0xc4, 0x1b, 0x2f, 0x60, 0xaa, 0x56, 0x7b, 0x3f, 0x71, 0xc8, 0x01,
0x7e, 0x86, 0xd3, 0xb7, 0x0c, 0x29, 0x1a, 0x9e, 0x5b, 0x38, 0x3f, 0x01, 0x72,
},
domain: "dogfish",
err: false,
},
{
input: []byte{
0x16, 0x03, 0x01, 0x01, 0x03, 0x01, 0x00, 0x00,
0xff, 0x03, 0x03, 0x3d, 0x89, 0x52, 0x9e, 0xee,
0xbe, 0x17, 0x63, 0x75, 0xef, 0x29, 0xbd, 0x14,
0x6a, 0x49, 0xe0, 0x2c, 0x37, 0x57, 0x71, 0x62,
0x82, 0x44, 0x94, 0x8f, 0x6e, 0x94, 0x08, 0x45,
0x7f, 0xdb, 0xc1, 0x00, 0x00, 0x3e, 0xc0, 0x2c,
0xc0, 0x30, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8,
0xcc, 0xaa, 0xc0, 0x2b, 0xc0, 0x2f, 0x00, 0x9e,
0xc0, 0x24, 0xc0, 0x28, 0x00, 0x6b, 0xc0, 0x23,
0xc0, 0x27, 0x00, 0x67, 0xc0, 0x0a, 0xc0, 0x14,
0x00, 0x39, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33,
0x00, 0x9d, 0x00, 0x9c, 0x13, 0x02, 0x13, 0x03,
0x13, 0x01, 0x00, 0x3d, 0x00, 0x3c, 0x00, 0x35,
0x00, 0x2f, 0x00, 0xff, 0x01, 0x00, 0x00, 0x98,
0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x00,
0x0b, 0x31, 0x30, 0x2e, 0x34, 0x32, 0x2e, 0x30,
0x2e, 0x32, 0x34, 0x33, 0x00, 0x0b, 0x00, 0x04,
0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x0a,
0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x19,
0x00, 0x18, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d,
0x00, 0x20, 0x00, 0x1e, 0x04, 0x03, 0x05, 0x03,
0x06, 0x03, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06,
0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03,
0x02, 0x01, 0x02, 0x02, 0x04, 0x02, 0x05, 0x02,
0x06, 0x02, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17,
0x00, 0x00, 0x00, 0x2b, 0x00, 0x09, 0x08, 0x7f,
0x14, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00,
0x2d, 0x00, 0x03, 0x02, 0x01, 0x00, 0x00, 0x28,
0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20,
0x13, 0x7c, 0x6e, 0x97, 0xc4, 0xfd, 0x09, 0x2e,
0x70, 0x2f, 0x73, 0x5a, 0x9b, 0x57, 0x4d, 0x5f,
0x2b, 0x73, 0x2c, 0xa5, 0x4a, 0x98, 0x40, 0x3d,
0x75, 0x6e, 0xb4, 0x76, 0xf9, 0x48, 0x8f, 0x36,
},
domain: "10.42.0.243",
err: false,
},
}
for _, test := range cases {
domain, err := SniffTLS(test.input)
if test.err {
if err == nil {
t.Errorf("Exepct error but nil in test %v", test)
}
} else {
if err != nil {
t.Errorf("Expect no error but actually %s in test %v", err.Error(), test)
}
if *domain != test.domain {
t.Error("expect domain ", test.domain, " but got ", domain)
}
}
}
}

View File

@@ -1,157 +0,0 @@
package sniffer
import (
"encoding/binary"
"errors"
"strings"
C "github.com/Dreamacro/clash/constant"
)
var (
errNotTLS = errors.New("not TLS header")
errNotClientHello = errors.New("not client hello")
ErrNoClue = errors.New("not enough information for making a decision")
)
type TLSSniffer struct {
}
func (tls *TLSSniffer) Protocol() string {
return "tls"
}
func (tls *TLSSniffer) SupportNetwork() C.NetWork {
return C.TCP
}
func (tls *TLSSniffer) SniffTCP(bytes []byte) (string, error) {
domain, err := SniffTLS(bytes)
if err == nil {
return *domain, nil
} else {
return "", err
}
}
func IsValidTLSVersion(major, minor byte) bool {
return major == 3
}
// ReadClientHello returns server name (if any) from TLS client hello message.
// https://github.com/golang/go/blob/master/src/crypto/tls/handshake_messages.go#L300
func ReadClientHello(data []byte) (*string, error) {
if len(data) < 42 {
return nil, ErrNoClue
}
sessionIDLen := int(data[38])
if sessionIDLen > 32 || len(data) < 39+sessionIDLen {
return nil, ErrNoClue
}
data = data[39+sessionIDLen:]
if len(data) < 2 {
return nil, ErrNoClue
}
// cipherSuiteLen is the number of bytes of cipher suite numbers. Since
// they are uint16s, the number must be even.
cipherSuiteLen := int(data[0])<<8 | int(data[1])
if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen {
return nil, errNotClientHello
}
data = data[2+cipherSuiteLen:]
if len(data) < 1 {
return nil, ErrNoClue
}
compressionMethodsLen := int(data[0])
if len(data) < 1+compressionMethodsLen {
return nil, ErrNoClue
}
data = data[1+compressionMethodsLen:]
if len(data) == 0 {
return nil, errNotClientHello
}
if len(data) < 2 {
return nil, errNotClientHello
}
extensionsLength := int(data[0])<<8 | int(data[1])
data = data[2:]
if extensionsLength != len(data) {
return nil, errNotClientHello
}
for len(data) != 0 {
if len(data) < 4 {
return nil, errNotClientHello
}
extension := uint16(data[0])<<8 | uint16(data[1])
length := int(data[2])<<8 | int(data[3])
data = data[4:]
if len(data) < length {
return nil, errNotClientHello
}
if extension == 0x00 { /* extensionServerName */
d := data[:length]
if len(d) < 2 {
return nil, errNotClientHello
}
namesLen := int(d[0])<<8 | int(d[1])
d = d[2:]
if len(d) != namesLen {
return nil, errNotClientHello
}
for len(d) > 0 {
if len(d) < 3 {
return nil, errNotClientHello
}
nameType := d[0]
nameLen := int(d[1])<<8 | int(d[2])
d = d[3:]
if len(d) < nameLen {
return nil, errNotClientHello
}
if nameType == 0 {
serverName := string(d[:nameLen])
// An SNI value may not include a
// trailing dot. See
// https://tools.ietf.org/html/rfc6066#section-3.
if strings.HasSuffix(serverName, ".") {
return nil, errNotClientHello
}
return &serverName, nil
}
d = d[nameLen:]
}
}
data = data[length:]
}
return nil, errNotTLS
}
func SniffTLS(b []byte) (*string, error) {
if len(b) < 5 {
return nil, ErrNoClue
}
if b[0] != 0x16 /* TLS Handshake */ {
return nil, errNotTLS
}
if !IsValidTLSVersion(b[1], b[2]) {
return nil, errNotTLS
}
headerLen := int(binary.BigEndian.Uint16(b[3:5]))
if 5+headerLen > len(b) {
return nil, ErrNoClue
}
domain, err := ReadClientHello(b[5 : 5+headerLen])
if err == nil {
return domain, nil
}
return nil, err
}

View File

@@ -17,8 +17,8 @@ var ErrInvalidDomain = errors.New("invalid domain")
// DomainTrie contains the main logic for adding and searching nodes for domain segments. // DomainTrie contains the main logic for adding and searching nodes for domain segments.
// support wildcard domain (e.g *.google.com) // support wildcard domain (e.g *.google.com)
type DomainTrie[T comparable] struct { type DomainTrie struct {
root *Node[T] root *Node
} }
func ValidAndSplitDomain(domain string) ([]string, bool) { func ValidAndSplitDomain(domain string) ([]string, bool) {
@@ -51,7 +51,7 @@ func ValidAndSplitDomain(domain string) ([]string, bool) {
// 3. subdomain.*.example.com // 3. subdomain.*.example.com
// 4. .example.com // 4. .example.com
// 5. +.example.com // 5. +.example.com
func (t *DomainTrie[T]) Insert(domain string, data T) error { func (t *DomainTrie) Insert(domain string, data any) error {
parts, valid := ValidAndSplitDomain(domain) parts, valid := ValidAndSplitDomain(domain)
if !valid { if !valid {
return ErrInvalidDomain return ErrInvalidDomain
@@ -68,13 +68,13 @@ func (t *DomainTrie[T]) Insert(domain string, data T) error {
return nil return nil
} }
func (t *DomainTrie[T]) insert(parts []string, data T) { func (t *DomainTrie) insert(parts []string, data any) {
node := t.root node := t.root
// reverse storage domain part to save space // reverse storage domain part to save space
for i := len(parts) - 1; i >= 0; i-- { for i := len(parts) - 1; i >= 0; i-- {
part := parts[i] part := parts[i]
if !node.hasChild(part) { if !node.hasChild(part) {
node.addChild(part, newNode(getZero[T]())) node.addChild(part, newNode(nil))
} }
node = node.getChild(part) node = node.getChild(part)
@@ -88,7 +88,7 @@ func (t *DomainTrie[T]) insert(parts []string, data T) {
// 1. static part // 1. static part
// 2. wildcard domain // 2. wildcard domain
// 2. dot wildcard domain // 2. dot wildcard domain
func (t *DomainTrie[T]) Search(domain string) *Node[T] { func (t *DomainTrie) Search(domain string) *Node {
parts, valid := ValidAndSplitDomain(domain) parts, valid := ValidAndSplitDomain(domain)
if !valid || parts[0] == "" { if !valid || parts[0] == "" {
return nil return nil
@@ -96,26 +96,26 @@ func (t *DomainTrie[T]) Search(domain string) *Node[T] {
n := t.search(t.root, parts) n := t.search(t.root, parts)
if n == nil || n.Data == getZero[T]() { if n == nil || n.Data == nil {
return nil return nil
} }
return n return n
} }
func (t *DomainTrie[T]) search(node *Node[T], parts []string) *Node[T] { func (t *DomainTrie) search(node *Node, parts []string) *Node {
if len(parts) == 0 { if len(parts) == 0 {
return node return node
} }
if c := node.getChild(parts[len(parts)-1]); c != nil { if c := node.getChild(parts[len(parts)-1]); c != nil {
if n := t.search(c, parts[:len(parts)-1]); n != nil && n.Data != getZero[T]() { if n := t.search(c, parts[:len(parts)-1]); n != nil && n.Data != nil {
return n return n
} }
} }
if c := node.getChild(wildcard); c != nil { if c := node.getChild(wildcard); c != nil {
if n := t.search(c, parts[:len(parts)-1]); n != nil && n.Data != getZero[T]() { if n := t.search(c, parts[:len(parts)-1]); n != nil && n.Data != nil {
return n return n
} }
} }
@@ -124,6 +124,6 @@ func (t *DomainTrie[T]) search(node *Node[T], parts []string) *Node[T] {
} }
// New returns a new, empty Trie. // New returns a new, empty Trie.
func New[T comparable]() *DomainTrie[T] { func New() *DomainTrie {
return &DomainTrie[T]{root: newNode[T](getZero[T]())} return &DomainTrie{root: newNode(nil)}
} }

View File

@@ -1,16 +1,16 @@
package trie package trie
import ( import (
"net/netip" "net"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
var localIP = netip.AddrFrom4([4]byte{127, 0, 0, 1}) var localIP = net.IP{127, 0, 0, 1}
func TestTrie_Basic(t *testing.T) { func TestTrie_Basic(t *testing.T) {
tree := New[netip.Addr]() tree := New()
domains := []string{ domains := []string{
"example.com", "example.com",
"google.com", "google.com",
@@ -23,7 +23,7 @@ func TestTrie_Basic(t *testing.T) {
node := tree.Search("example.com") node := tree.Search("example.com")
assert.NotNil(t, node) assert.NotNil(t, node)
assert.True(t, node.Data == localIP) assert.True(t, node.Data.(net.IP).Equal(localIP))
assert.NotNil(t, tree.Insert("", localIP)) assert.NotNil(t, tree.Insert("", localIP))
assert.Nil(t, tree.Search("")) assert.Nil(t, tree.Search(""))
assert.NotNil(t, tree.Search("localhost")) assert.NotNil(t, tree.Search("localhost"))
@@ -31,7 +31,7 @@ func TestTrie_Basic(t *testing.T) {
} }
func TestTrie_Wildcard(t *testing.T) { func TestTrie_Wildcard(t *testing.T) {
tree := New[netip.Addr]() tree := New()
domains := []string{ domains := []string{
"*.example.com", "*.example.com",
"sub.*.example.com", "sub.*.example.com",
@@ -64,7 +64,7 @@ func TestTrie_Wildcard(t *testing.T) {
} }
func TestTrie_Priority(t *testing.T) { func TestTrie_Priority(t *testing.T) {
tree := New[int]() tree := New()
domains := []string{ domains := []string{
".dev", ".dev",
"example.dev", "example.dev",
@@ -79,18 +79,18 @@ func TestTrie_Priority(t *testing.T) {
} }
for idx, domain := range domains { for idx, domain := range domains {
tree.Insert(domain, idx+1) tree.Insert(domain, idx)
} }
assertFn("test.dev", 1) assertFn("test.dev", 0)
assertFn("foo.bar.dev", 1) assertFn("foo.bar.dev", 0)
assertFn("example.dev", 2) assertFn("example.dev", 1)
assertFn("foo.example.dev", 3) assertFn("foo.example.dev", 2)
assertFn("test.example.dev", 4) assertFn("test.example.dev", 3)
} }
func TestTrie_Boundary(t *testing.T) { func TestTrie_Boundary(t *testing.T) {
tree := New[netip.Addr]() tree := New()
tree.Insert("*.dev", localIP) tree.Insert("*.dev", localIP)
assert.NotNil(t, tree.Insert(".", localIP)) assert.NotNil(t, tree.Insert(".", localIP))
@@ -99,7 +99,7 @@ func TestTrie_Boundary(t *testing.T) {
} }
func TestTrie_WildcardBoundary(t *testing.T) { func TestTrie_WildcardBoundary(t *testing.T) {
tree := New[netip.Addr]() tree := New()
tree.Insert("+.*", localIP) tree.Insert("+.*", localIP)
tree.Insert("stun.*.*.*", localIP) tree.Insert("stun.*.*.*", localIP)

View File

@@ -1,31 +1,26 @@
package trie package trie
// Node is the trie's node // Node is the trie's node
type Node[T comparable] struct { type Node struct {
children map[string]*Node[T] children map[string]*Node
Data T Data any
} }
func (n *Node[T]) getChild(s string) *Node[T] { func (n *Node) getChild(s string) *Node {
return n.children[s] return n.children[s]
} }
func (n *Node[T]) hasChild(s string) bool { func (n *Node) hasChild(s string) bool {
return n.getChild(s) != nil return n.getChild(s) != nil
} }
func (n *Node[T]) addChild(s string, child *Node[T]) { func (n *Node) addChild(s string, child *Node) {
n.children[s] = child n.children[s] = child
} }
func newNode[T comparable](data T) *Node[T] { func newNode(data any) *Node {
return &Node[T]{ return &Node{
Data: data, Data: data,
children: map[string]*Node[T]{}, children: map[string]*Node{},
} }
} }
func getZero[T comparable]() T {
var result T
return result
}

View File

@@ -4,19 +4,16 @@ import (
"container/list" "container/list"
"errors" "errors"
"fmt" "fmt"
R "github.com/Dreamacro/clash/rule"
RP "github.com/Dreamacro/clash/rule/provider"
"net" "net"
"net/netip" "net/netip"
"net/url" "net/url"
"os" "os"
"runtime" "runtime"
"strconv"
"strings" "strings"
"time" "time"
"github.com/Dreamacro/clash/common/utils"
R "github.com/Dreamacro/clash/rule"
RP "github.com/Dreamacro/clash/rule/provider"
"github.com/Dreamacro/clash/adapter" "github.com/Dreamacro/clash/adapter"
"github.com/Dreamacro/clash/adapter/outbound" "github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/adapter/outboundgroup" "github.com/Dreamacro/clash/adapter/outboundgroup"
@@ -81,7 +78,7 @@ type DNS struct {
EnhancedMode C.DNSMode `yaml:"enhanced-mode"` EnhancedMode C.DNSMode `yaml:"enhanced-mode"`
DefaultNameserver []dns.NameServer `yaml:"default-nameserver"` DefaultNameserver []dns.NameServer `yaml:"default-nameserver"`
FakeIPRange *fakeip.Pool FakeIPRange *fakeip.Pool
Hosts *trie.DomainTrie[netip.Addr] Hosts *trie.DomainTrie
NameServerPolicy map[string]dns.NameServer NameServerPolicy map[string]dns.NameServer
ProxyServerNameserver []dns.NameServer ProxyServerNameserver []dns.NameServer
} }
@@ -116,6 +113,12 @@ type Tun struct {
AutoRoute bool `yaml:"auto-route" json:"auto-route"` AutoRoute bool `yaml:"auto-route" json:"auto-route"`
} }
// Script config
type Script struct {
MainCode string `yaml:"code" json:"code"`
ShortcutsCode map[string]string `yaml:"shortcuts" json:"shortcuts"`
}
// IPTables config // IPTables config
type IPTables struct { type IPTables struct {
Enable bool `yaml:"enable" json:"enable"` Enable bool `yaml:"enable" json:"enable"`
@@ -123,16 +126,6 @@ type IPTables struct {
Bypass []string `yaml:"bypass" json:"bypass"` Bypass []string `yaml:"bypass" json:"bypass"`
} }
type Sniffer struct {
Enable bool
Force bool
Sniffers []C.SnifferType
Reverses *trie.DomainTrie[bool]
ForceDomain *trie.DomainTrie[bool]
SkipSNI *trie.DomainTrie[bool]
Ports *[]utils.Range[uint16]
}
// Experimental config // Experimental config
type Experimental struct{} type Experimental struct{}
@@ -143,14 +136,13 @@ type Config struct {
IPTables *IPTables IPTables *IPTables
DNS *DNS DNS *DNS
Experimental *Experimental Experimental *Experimental
Hosts *trie.DomainTrie[netip.Addr] Hosts *trie.DomainTrie
Profile *Profile Profile *Profile
Rules []C.Rule Rules []C.Rule
Users []auth.AuthUser Users []auth.AuthUser
Proxies map[string]C.Proxy Proxies map[string]C.Proxy
Providers map[string]providerTypes.ProxyProvider Providers map[string]providerTypes.ProxyProvider
RuleProviders map[string]*providerTypes.RuleProvider RuleProviders map[string]*providerTypes.RuleProvider
Sniffer *Sniffer
} }
type RawDNS struct { type RawDNS struct {
@@ -178,12 +170,11 @@ type RawFallbackFilter struct {
} }
type RawTun struct { type RawTun struct {
Enable bool `yaml:"enable" json:"enable"` Enable bool `yaml:"enable" json:"enable"`
Device string `yaml:"device" json:"device"` Device string `yaml:"device" json:"device"`
Stack C.TUNStack `yaml:"stack" json:"stack"` Stack C.TUNStack `yaml:"stack" json:"stack"`
DNSHijack []string `yaml:"dns-hijack" json:"dns-hijack"` DNSHijack []string `yaml:"dns-hijack" json:"dns-hijack"`
AutoRoute bool `yaml:"auto-route" json:"auto-route"` AutoRoute bool `yaml:"auto-route" json:"auto-route"`
AutoDetectInterface bool `yaml:"auto-detect-interface"`
} }
type RawConfig struct { type RawConfig struct {
@@ -207,7 +198,6 @@ type RawConfig struct {
GeodataMode bool `yaml:"geodata-mode"` GeodataMode bool `yaml:"geodata-mode"`
GeodataLoader string `yaml:"geodata-loader"` GeodataLoader string `yaml:"geodata-loader"`
Sniffer SnifferRaw `yaml:"sniffer"`
ProxyProvider map[string]map[string]any `yaml:"proxy-providers"` ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
RuleProvider map[string]map[string]any `yaml:"rule-providers"` RuleProvider map[string]map[string]any `yaml:"rule-providers"`
Hosts map[string]string `yaml:"hosts"` Hosts map[string]string `yaml:"hosts"`
@@ -221,16 +211,6 @@ type RawConfig struct {
Rule []string `yaml:"rules"` Rule []string `yaml:"rules"`
} }
type SnifferRaw struct {
Enable bool `yaml:"enable" json:"enable"`
Sniffing []string `yaml:"sniffing" json:"sniffing"`
Force bool `yaml:"force" json:"force"`
Reverse []string `yaml:"reverses" json:"reverses"`
ForceDomain []string `yaml:"force-domain" json:"force-domain"`
SkipSNI []string `yaml:"skip-sni" json:"skip-sni"`
Ports []string `yaml:"port-whitelist" json:"port-whitelist"`
}
// Parse config // Parse config
func Parse(buf []byte) (*Config, error) { func Parse(buf []byte) (*Config, error) {
rawCfg, err := UnmarshalRawConfig(buf) rawCfg, err := UnmarshalRawConfig(buf)
@@ -257,12 +237,11 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
Proxy: []map[string]any{}, Proxy: []map[string]any{},
ProxyGroup: []map[string]any{}, ProxyGroup: []map[string]any{},
Tun: RawTun{ Tun: RawTun{
Enable: false, Enable: false,
Device: "", Device: "",
AutoDetectInterface: true, Stack: C.TunGvisor,
Stack: C.TunGvisor, DNSHijack: []string{"0.0.0.0:53"}, // default hijack all dns query
DNSHijack: []string{"0.0.0.0:53"}, // default hijack all dns query AutoRoute: true,
AutoRoute: true,
}, },
IPTables: IPTables{ IPTables: IPTables{
Enable: false, Enable: false,
@@ -296,15 +275,6 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
"www.msftconnecttest.com", "www.msftconnecttest.com",
}, },
}, },
Sniffer: SnifferRaw{
Enable: false,
Force: false,
Sniffing: []string{},
Reverse: []string{},
ForceDomain: []string{},
SkipSNI: []string{},
Ports: []string{},
},
Profile: Profile{ Profile: Profile{
StoreSelected: true, StoreSelected: true,
}, },
@@ -367,11 +337,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
config.Users = parseAuthentication(rawCfg.Authentication) config.Users = parseAuthentication(rawCfg.Authentication)
config.Sniffer, err = parseSniffer(rawCfg.Sniffer)
if err != nil {
return nil, err
}
elapsedTime := time.Since(startTime) / time.Millisecond // duration in ms elapsedTime := time.Since(startTime) / time.Millisecond // duration in ms
log.Infoln("Initial configuration complete, total time: %dms", elapsedTime) //Segment finished in xxm log.Infoln("Initial configuration complete, total time: %dms", elapsedTime) //Segment finished in xxm
return config, nil return config, nil
@@ -573,7 +538,7 @@ func parseRules(cfg *RawConfig, proxies map[string]C.Proxy) ([]C.Rule, map[strin
params = rule[l:] params = rule[l:]
} }
if _, ok := proxies[target]; !ok { if _, ok := proxies[target]; mode != T.Script && !ok {
return nil, nil, fmt.Errorf("rules[%d] [%s] error: proxy [%s] not found", idx, line, target) return nil, nil, fmt.Errorf("rules[%d] [%s] error: proxy [%s] not found", idx, line, target)
} }
@@ -590,7 +555,9 @@ func parseRules(cfg *RawConfig, proxies map[string]C.Proxy) ([]C.Rule, map[strin
return nil, nil, fmt.Errorf("rules[%d] [%s] error: %s", idx, line, parseErr.Error()) return nil, nil, fmt.Errorf("rules[%d] [%s] error: %s", idx, line, parseErr.Error())
} }
rules = append(rules, parsed) if mode != T.Script {
rules = append(rules, parsed)
}
} }
runtime.GC() runtime.GC()
@@ -598,18 +565,18 @@ func parseRules(cfg *RawConfig, proxies map[string]C.Proxy) ([]C.Rule, map[strin
return rules, ruleProviders, nil return rules, ruleProviders, nil
} }
func parseHosts(cfg *RawConfig) (*trie.DomainTrie[netip.Addr], error) { func parseHosts(cfg *RawConfig) (*trie.DomainTrie, error) {
tree := trie.New[netip.Addr]() tree := trie.New()
// add default hosts // add default hosts
if err := tree.Insert("localhost", netip.AddrFrom4([4]byte{127, 0, 0, 1})); err != nil { if err := tree.Insert("localhost", net.IP{127, 0, 0, 1}); err != nil {
log.Errorln("insert localhost to host error: %s", err.Error()) log.Errorln("insert localhost to host error: %s", err.Error())
} }
if len(cfg.Hosts) != 0 { if len(cfg.Hosts) != 0 {
for domain, ipStr := range cfg.Hosts { for domain, ipStr := range cfg.Hosts {
ip, err := netip.ParseAddr(ipStr) ip := net.ParseIP(ipStr)
if err != nil { if ip == nil {
return nil, fmt.Errorf("%s is not a valid IP", ipStr) return nil, fmt.Errorf("%s is not a valid IP", ipStr)
} }
_ = tree.Insert(domain, ip) _ = tree.Insert(domain, ip)
@@ -757,7 +724,7 @@ func parseFallbackGeoSite(countries []string, rules []C.Rule) ([]*router.DomainM
return sites, nil return sites, nil
} }
func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr], rules []C.Rule) (*DNS, error) { func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie, rules []C.Rule) (*DNS, error) {
cfg := rawCfg.DNS cfg := rawCfg.DNS
if cfg.Enable && len(cfg.NameServer) == 0 { if cfg.Enable && len(cfg.NameServer) == 0 {
return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty") return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty")
@@ -800,23 +767,20 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr], rules []C.R
for _, ns := range dnsCfg.DefaultNameserver { for _, ns := range dnsCfg.DefaultNameserver {
host, _, err := net.SplitHostPort(ns.Addr) host, _, err := net.SplitHostPort(ns.Addr)
if err != nil || net.ParseIP(host) == nil { if err != nil || net.ParseIP(host) == nil {
u, err := url.Parse(ns.Addr) return nil, errors.New("default nameserver should be pure IP")
if err != nil || net.ParseIP(u.Host) == nil {
return nil, errors.New("default nameserver should be pure IP")
}
} }
} }
if cfg.EnhancedMode == C.DNSFakeIP { if cfg.EnhancedMode == C.DNSFakeIP {
ipnet, err := netip.ParsePrefix(cfg.FakeIPRange) _, ipnet, err := net.ParseCIDR(cfg.FakeIPRange)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var host *trie.DomainTrie[bool] var host *trie.DomainTrie
// fake ip skip host filter // fake ip skip host filter
if len(cfg.FakeIPFilter) != 0 { if len(cfg.FakeIPFilter) != 0 {
host = trie.New[bool]() host = trie.New()
for _, domain := range cfg.FakeIPFilter { for _, domain := range cfg.FakeIPFilter {
_ = host.Insert(domain, true) _ = host.Insert(domain, true)
} }
@@ -824,7 +788,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr], rules []C.R
if len(dnsCfg.Fallback) != 0 { if len(dnsCfg.Fallback) != 0 {
if host == nil { if host == nil {
host = trie.New[bool]() host = trie.New()
} }
for _, fb := range dnsCfg.Fallback { for _, fb := range dnsCfg.Fallback {
if net.ParseIP(fb.Addr) != nil { if net.ParseIP(fb.Addr) != nil {
@@ -835,7 +799,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr], rules []C.R
} }
pool, err := fakeip.New(fakeip.Options{ pool, err := fakeip.New(fakeip.Options{
IPNet: &ipnet, IPNet: ipnet,
Size: 1000, Size: 1000,
Host: host, Host: host,
Persistence: rawCfg.Profile.StoreFakeIP, Persistence: rawCfg.Profile.StoreFakeIP,
@@ -879,7 +843,7 @@ func parseAuthentication(rawRecords []string) []auth.AuthUser {
} }
func parseTun(rawTun RawTun, general *General) (*Tun, error) { func parseTun(rawTun RawTun, general *General) (*Tun, error) {
if rawTun.Enable && rawTun.AutoDetectInterface { if (rawTun.Enable || general.TProxyPort != 0) && general.Interface == "" {
autoDetectInterfaceName, err := commons.GetAutoDetectInterface() autoDetectInterfaceName, err := commons.GetAutoDetectInterface()
if err != nil || autoDetectInterfaceName == "" { if err != nil || autoDetectInterfaceName == "" {
log.Warnln("Can not find auto detect interface.[%s]", err) log.Warnln("Can not find auto detect interface.[%s]", err)
@@ -913,97 +877,3 @@ func parseTun(rawTun RawTun, general *General) (*Tun, error) {
AutoRoute: rawTun.AutoRoute, AutoRoute: rawTun.AutoRoute,
}, nil }, nil
} }
func parseSniffer(snifferRaw SnifferRaw) (*Sniffer, error) {
sniffer := &Sniffer{
Enable: snifferRaw.Enable,
Force: snifferRaw.Force,
}
ports := []utils.Range[uint16]{}
if len(snifferRaw.Ports) == 0 {
ports = append(ports, *utils.NewRange[uint16](0, 65535))
} else {
for _, portRange := range snifferRaw.Ports {
portRaws := strings.Split(portRange, "-")
if len(portRaws) > 1 {
p, err := strconv.ParseUint(portRaws[0], 10, 16)
if err != nil {
return nil, fmt.Errorf("%s format error", portRange)
}
start := uint16(p)
p, err = strconv.ParseUint(portRaws[0], 10, 16)
if err != nil {
return nil, fmt.Errorf("%s format error", portRange)
}
end := uint16(p)
ports = append(ports, *utils.NewRange(start, end))
}
}
}
sniffer.Ports = &ports
loadSniffer := make(map[C.SnifferType]struct{})
for _, snifferName := range snifferRaw.Sniffing {
find := false
for _, snifferType := range C.SnifferList {
if snifferType.String() == strings.ToUpper(snifferName) {
find = true
loadSniffer[snifferType] = struct{}{}
}
}
if !find {
return nil, fmt.Errorf("not find the sniffer[%s]", snifferName)
}
}
for st := range loadSniffer {
sniffer.Sniffers = append(sniffer.Sniffers, st)
}
sniffer.ForceDomain = trie.New[bool]()
for _, domain := range snifferRaw.ForceDomain {
err := sniffer.ForceDomain.Insert(domain, true)
if err != nil {
return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
}
}
sniffer.SkipSNI = trie.New[bool]()
for _, domain := range snifferRaw.SkipSNI {
err := sniffer.SkipSNI.Insert(domain, true)
if err != nil {
return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
}
}
// Compatibility, remove it when release
if strings.Contains(C.Version, "alpha") || strings.Contains(C.Version, "develop") || strings.Contains(C.Version, "1.10.0") {
log.Warnln("Sniffer param force and reverses deprecated, will be removed in the release version, see https://github.com/MetaCubeX/Clash.Meta/commit/48a01adb7a4f38974b9d9639f931d0d245aebf28")
if snifferRaw.Force {
// match all domain
sniffer.ForceDomain.Insert("+", true)
for _, domain := range snifferRaw.Reverse {
err := sniffer.SkipSNI.Insert(domain, true)
if err != nil {
return nil, fmt.Errorf("error domian[%s], error:%v", domain, err)
}
}
} else {
for _, domain := range snifferRaw.Reverse {
err := sniffer.ForceDomain.Insert(domain, true)
if err != nil {
return nil, fmt.Errorf("error domian[%s], error:%v", domain, err)
}
}
}
}
return sniffer, nil
}

View File

@@ -87,8 +87,7 @@ type Metadata struct {
} }
func (m *Metadata) RemoteAddress() string { func (m *Metadata) RemoteAddress() string {
return net.JoinHostPort(m.String(), m.DstPort) return net.JoinHostPort(m.String(), m.DstPort)
} }
func (m *Metadata) SourceAddress() string { func (m *Metadata) SourceAddress() string {

View File

@@ -1,26 +0,0 @@
package constant
type Sniffer interface {
SupportNetwork() NetWork
SniffTCP(bytes []byte) (string, error)
Protocol() string
}
const (
TLS SnifferType = iota
)
var (
SnifferList = []SnifferType{TLS}
)
type SnifferType int
func (rt SnifferType) String() string {
switch rt {
case TLS:
return "TLS"
default:
return "Unknown"
}
}

View File

@@ -3,8 +3,8 @@ package context
import ( import (
"net" "net"
CN "github.com/Dreamacro/clash/common/net"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
) )
@@ -19,7 +19,7 @@ func NewConnContext(conn net.Conn, metadata *C.Metadata) *ConnContext {
return &ConnContext{ return &ConnContext{
id: id, id: id,
metadata: metadata, metadata: metadata,
conn: CN.NewBufferedConn(conn), conn: conn,
} }
} }

View File

@@ -2,7 +2,6 @@ package dns
import ( import (
"net" "net"
"net/netip"
"github.com/Dreamacro/clash/common/cache" "github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/component/fakeip" "github.com/Dreamacro/clash/component/fakeip"
@@ -12,7 +11,7 @@ import (
type ResolverEnhancer struct { type ResolverEnhancer struct {
mode C.DNSMode mode C.DNSMode
fakePool *fakeip.Pool fakePool *fakeip.Pool
mapping *cache.LruCache[netip.Addr, string] mapping *cache.LruCache
} }
func (h *ResolverEnhancer) FakeIPEnabled() bool { func (h *ResolverEnhancer) FakeIPEnabled() bool {
@@ -29,7 +28,7 @@ func (h *ResolverEnhancer) IsExistFakeIP(ip net.IP) bool {
} }
if pool := h.fakePool; pool != nil { if pool := h.fakePool; pool != nil {
return pool.Exist(ipToAddr(ip)) return pool.Exist(ip)
} }
return false return false
@@ -40,10 +39,8 @@ func (h *ResolverEnhancer) IsFakeIP(ip net.IP) bool {
return false return false
} }
addr := ipToAddr(ip)
if pool := h.fakePool; pool != nil { if pool := h.fakePool; pool != nil {
return pool.IPNet().Contains(addr) && addr != pool.Gateway() && addr != pool.Broadcast() return pool.IPNet().Contains(ip) && !pool.Gateway().Equal(ip) && !pool.Broadcast().Equal(ip)
} }
return false return false
@@ -55,35 +52,28 @@ func (h *ResolverEnhancer) IsFakeBroadcastIP(ip net.IP) bool {
} }
if pool := h.fakePool; pool != nil { if pool := h.fakePool; pool != nil {
return pool.Broadcast() == ipToAddr(ip) return pool.Broadcast().Equal(ip)
} }
return false return false
} }
func (h *ResolverEnhancer) FindHostByIP(ip net.IP) (string, bool) { func (h *ResolverEnhancer) FindHostByIP(ip net.IP) (string, bool) {
addr := ipToAddr(ip)
if pool := h.fakePool; pool != nil { if pool := h.fakePool; pool != nil {
if host, existed := pool.LookBack(addr); existed { if host, existed := pool.LookBack(ip); existed {
return host, true return host, true
} }
} }
if mapping := h.mapping; mapping != nil { if mapping := h.mapping; mapping != nil {
if host, existed := h.mapping.Get(addr); existed { if host, existed := h.mapping.Get(ip.String()); existed {
return host, true return host.(string), true
} }
} }
return "", false return "", false
} }
func (h *ResolverEnhancer) InsertHostByIP(ip net.IP, host string) {
if mapping := h.mapping; mapping != nil {
h.mapping.Set(ipToAddr(ip), host)
}
}
func (h *ResolverEnhancer) PatchFrom(o *ResolverEnhancer) { func (h *ResolverEnhancer) PatchFrom(o *ResolverEnhancer) {
if h.mapping != nil && o.mapping != nil { if h.mapping != nil && o.mapping != nil {
o.mapping.CloneTo(h.mapping) o.mapping.CloneTo(h.mapping)
@@ -103,11 +93,11 @@ func (h *ResolverEnhancer) FlushFakeIP() error {
func NewEnhancer(cfg Config) *ResolverEnhancer { func NewEnhancer(cfg Config) *ResolverEnhancer {
var fakePool *fakeip.Pool var fakePool *fakeip.Pool
var mapping *cache.LruCache[netip.Addr, string] var mapping *cache.LruCache
if cfg.EnhancedMode != C.DNSNormal { if cfg.EnhancedMode != C.DNSNormal {
fakePool = cfg.Pool fakePool = cfg.Pool
mapping = cache.NewLRUCache[netip.Addr, string](cache.WithSize[netip.Addr, string](4096), cache.WithStale[netip.Addr, string](true)) mapping = cache.NewLRUCache(cache.WithSize(4096), cache.WithStale(true))
} }
return &ResolverEnhancer{ return &ResolverEnhancer{

View File

@@ -70,13 +70,13 @@ type fallbackDomainFilter interface {
} }
type domainFilter struct { type domainFilter struct {
tree *trie.DomainTrie[bool] tree *trie.DomainTrie
} }
func NewDomainFilter(domains []string) *domainFilter { func NewDomainFilter(domains []string) *domainFilter {
df := domainFilter{tree: trie.New[bool]()} df := domainFilter{tree: trie.New()}
for _, domain := range domains { for _, domain := range domains {
df.tree.Insert(domain, true) df.tree.Insert(domain, "")
} }
return &df return &df
} }

View File

@@ -2,7 +2,6 @@ package dns
import ( import (
"net" "net"
"net/netip"
"strings" "strings"
"time" "time"
@@ -21,7 +20,7 @@ type (
middleware func(next handler) handler middleware func(next handler) handler
) )
func withHosts(hosts *trie.DomainTrie[netip.Addr], mapping *cache.LruCache[netip.Addr, string]) middleware { func withHosts(hosts *trie.DomainTrie) middleware {
return func(next handler) handler { return func(next handler) handler {
return func(ctx *context.DNSContext, r *D.Msg) (*D.Msg, error) { return func(ctx *context.DNSContext, r *D.Msg) (*D.Msg, error) {
q := r.Question[0] q := r.Question[0]
@@ -30,36 +29,30 @@ func withHosts(hosts *trie.DomainTrie[netip.Addr], mapping *cache.LruCache[netip
return next(ctx, r) return next(ctx, r)
} }
host := strings.TrimRight(q.Name, ".") record := hosts.Search(strings.TrimRight(q.Name, "."))
record := hosts.Search(host)
if record == nil { if record == nil {
return next(ctx, r) return next(ctx, r)
} }
ip := record.Data ip := record.Data.(net.IP)
msg := r.Copy() msg := r.Copy()
if ip.Is4() && q.Qtype == D.TypeA { if v4 := ip.To4(); v4 != nil && q.Qtype == D.TypeA {
rr := &D.A{} rr := &D.A{}
rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeA, Class: D.ClassINET, Ttl: 10} rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeA, Class: D.ClassINET, Ttl: dnsDefaultTTL}
rr.A = ip.AsSlice() rr.A = v4
msg.Answer = []D.RR{rr} msg.Answer = []D.RR{rr}
} else if ip.Is6() && q.Qtype == D.TypeAAAA { } else if v6 := ip.To16(); v6 != nil && q.Qtype == D.TypeAAAA {
rr := &D.AAAA{} rr := &D.AAAA{}
rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeAAAA, Class: D.ClassINET, Ttl: 10} rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeAAAA, Class: D.ClassINET, Ttl: dnsDefaultTTL}
rr.AAAA = ip.AsSlice() rr.AAAA = v6
msg.Answer = []D.RR{rr} msg.Answer = []D.RR{rr}
} else { } else {
return next(ctx, r) return next(ctx, r)
} }
if mapping != nil {
mapping.SetWithExpire(ip, host, time.Now().Add(time.Second*10))
}
ctx.SetType(context.DNSTypeHost) ctx.SetType(context.DNSTypeHost)
msg.SetRcode(r, D.RcodeSuccess) msg.SetRcode(r, D.RcodeSuccess)
msg.Authoritative = true msg.Authoritative = true
@@ -70,7 +63,7 @@ func withHosts(hosts *trie.DomainTrie[netip.Addr], mapping *cache.LruCache[netip
} }
} }
func withMapping(mapping *cache.LruCache[netip.Addr, string]) middleware { func withMapping(mapping *cache.LruCache) middleware {
return func(next handler) handler { return func(next handler) handler {
return func(ctx *context.DNSContext, r *D.Msg) (*D.Msg, error) { return func(ctx *context.DNSContext, r *D.Msg) (*D.Msg, error) {
q := r.Question[0] q := r.Question[0]
@@ -101,7 +94,7 @@ func withMapping(mapping *cache.LruCache[netip.Addr, string]) middleware {
continue continue
} }
mapping.SetWithExpire(ipToAddr(ip), host, time.Now().Add(time.Second*time.Duration(ttl))) mapping.SetWithExpire(ip.String(), host, time.Now().Add(time.Second*time.Duration(ttl)))
} }
return msg, nil return msg, nil
@@ -131,7 +124,7 @@ func withFakeIP(fakePool *fakeip.Pool) middleware {
rr := &D.A{} rr := &D.A{}
rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeA, Class: D.ClassINET, Ttl: dnsDefaultTTL} rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeA, Class: D.ClassINET, Ttl: dnsDefaultTTL}
ip := fakePool.Lookup(host) ip := fakePool.Lookup(host)
rr.A = ip.AsSlice() rr.A = ip
msg := r.Copy() msg := r.Copy()
msg.Answer = []D.RR{rr} msg.Answer = []D.RR{rr}
@@ -183,7 +176,7 @@ func NewHandler(resolver *Resolver, mapper *ResolverEnhancer) handler {
middlewares := []middleware{} middlewares := []middleware{}
if resolver.hosts != nil { if resolver.hosts != nil {
middlewares = append(middlewares, withHosts(resolver.hosts, mapper.mapping)) middlewares = append(middlewares, withHosts(resolver.hosts))
} }
if mapper.mode == C.DNSFakeIP { if mapper.mode == C.DNSFakeIP {

View File

@@ -1,30 +0,0 @@
package dns
type Policy struct {
data []dnsClient
}
func (p *Policy) GetData() []dnsClient {
return p.data
}
func (p *Policy) Compare(p2 *Policy) int {
if p2 == nil {
return 1
}
l1 := len(p.data)
l2 := len(p2.data)
if l1 == l2 {
return 0
}
if l1 > l2 {
return 1
}
return -1
}
func NewPolicy(data []dnsClient) *Policy {
return &Policy{
data: data,
}
}

View File

@@ -19,7 +19,7 @@ var bytesPool = sync.Pool{New: func() interface{} { return &bytes.Buffer{} }}
type quicClient struct { type quicClient struct {
addr string addr string
session quic.Connection session quic.Session
sync.RWMutex // protects session and bytesPool sync.RWMutex // protects session and bytesPool
} }
@@ -67,7 +67,7 @@ func (dc *quicClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg
return reply, nil return reply, nil
} }
func isActive(s quic.Connection) bool { func isActive(s quic.Session) bool {
select { select {
case <-s.Context().Done(): case <-s.Context().Done():
return false return false
@@ -76,11 +76,11 @@ func isActive(s quic.Connection) bool {
} }
} }
// getSession - opens or returns an existing quic.Connection // getSession - opens or returns an existing quic.Session
// useCached - if true and cached session exists, return it right away // useCached - if true and cached session exists, return it right away
// otherwise - forcibly creates a new session // otherwise - forcibly creates a new session
func (dc *quicClient) getSession() (quic.Connection, error) { func (dc *quicClient) getSession() (quic.Session, error) {
var session quic.Connection var session quic.Session
dc.RLock() dc.RLock()
session = dc.session session = dc.session
if session != nil && isActive(session) { if session != nil && isActive(session) {
@@ -113,7 +113,7 @@ func (dc *quicClient) getSession() (quic.Connection, error) {
return session, nil return session, nil
} }
func (dc *quicClient) openSession() (quic.Connection, error) { func (dc *quicClient) openSession() (quic.Session, error) {
tlsConfig := &tls.Config{ tlsConfig := &tls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,
NextProtos: []string{ NextProtos: []string{

View File

@@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"math/rand" "math/rand"
"net" "net"
"net/netip"
"strings" "strings"
"time" "time"
@@ -34,14 +33,14 @@ type result struct {
type Resolver struct { type Resolver struct {
ipv6 bool ipv6 bool
hosts *trie.DomainTrie[netip.Addr] hosts *trie.DomainTrie
main []dnsClient main []dnsClient
fallback []dnsClient fallback []dnsClient
fallbackDomainFilters []fallbackDomainFilter fallbackDomainFilters []fallbackDomainFilter
fallbackIPFilters []fallbackIPFilter fallbackIPFilters []fallbackIPFilter
group singleflight.Group group singleflight.Group
lruCache *cache.LruCache[string, *D.Msg] lruCache *cache.LruCache
policy *trie.DomainTrie[*Policy] policy *trie.DomainTrie
proxyServer []dnsClient proxyServer []dnsClient
} }
@@ -104,7 +103,7 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e
cache, expireTime, hit := r.lruCache.GetWithExpire(q.String()) cache, expireTime, hit := r.lruCache.GetWithExpire(q.String())
if hit { if hit {
now := time.Now() now := time.Now()
msg = cache.Copy() msg = cache.(*D.Msg).Copy()
if expireTime.Before(now) { if expireTime.Before(now) {
setMsgTTL(msg, uint32(1)) // Continue fetch setMsgTTL(msg, uint32(1)) // Continue fetch
go r.exchangeWithoutCache(ctx, m) go r.exchangeWithoutCache(ctx, m)
@@ -195,8 +194,7 @@ func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient {
return nil return nil
} }
p := record.Data return record.Data.([]dnsClient)
return p.GetData()
} }
func (r *Resolver) shouldOnlyQueryFallback(m *D.Msg) bool { func (r *Resolver) shouldOnlyQueryFallback(m *D.Msg) bool {
@@ -331,20 +329,20 @@ type Config struct {
EnhancedMode C.DNSMode EnhancedMode C.DNSMode
FallbackFilter FallbackFilter FallbackFilter FallbackFilter
Pool *fakeip.Pool Pool *fakeip.Pool
Hosts *trie.DomainTrie[netip.Addr] Hosts *trie.DomainTrie
Policy map[string]NameServer Policy map[string]NameServer
} }
func NewResolver(config Config) *Resolver { func NewResolver(config Config) *Resolver {
defaultResolver := &Resolver{ defaultResolver := &Resolver{
main: transform(config.Default, nil), main: transform(config.Default, nil),
lruCache: cache.NewLRUCache[string, *D.Msg](cache.WithSize[string, *D.Msg](4096), cache.WithStale[string, *D.Msg](true)), lruCache: cache.NewLRUCache(cache.WithSize(4096), cache.WithStale(true)),
} }
r := &Resolver{ r := &Resolver{
ipv6: config.IPv6, ipv6: config.IPv6,
main: transform(config.Main, defaultResolver), main: transform(config.Main, defaultResolver),
lruCache: cache.NewLRUCache[string, *D.Msg](cache.WithSize[string, *D.Msg](4096), cache.WithStale[string, *D.Msg](true)), lruCache: cache.NewLRUCache(cache.WithSize(4096), cache.WithStale(true)),
hosts: config.Hosts, hosts: config.Hosts,
} }
@@ -357,9 +355,9 @@ func NewResolver(config Config) *Resolver {
} }
if len(config.Policy) != 0 { if len(config.Policy) != 0 {
r.policy = trie.New[*Policy]() r.policy = trie.New()
for domain, nameserver := range config.Policy { for domain, nameserver := range config.Policy {
r.policy.Insert(domain, NewPolicy(transform([]NameServer{nameserver}, defaultResolver))) r.policy.Insert(domain, transform([]NameServer{nameserver}, defaultResolver))
} }
} }

View File

@@ -5,7 +5,6 @@ import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"net" "net"
"net/netip"
"time" "time"
"github.com/Dreamacro/clash/common/cache" "github.com/Dreamacro/clash/common/cache"
@@ -17,7 +16,7 @@ import (
D "github.com/miekg/dns" D "github.com/miekg/dns"
) )
func putMsgToCache(c *cache.LruCache[string, *D.Msg], key string, msg *D.Msg) { func putMsgToCache(c *cache.LruCache, key string, msg *D.Msg) {
var ttl uint32 var ttl uint32
switch { switch {
case len(msg.Answer) != 0: case len(msg.Answer) != 0:
@@ -115,22 +114,6 @@ func msgToIP(msg *D.Msg) []net.IP {
return ips return ips
} }
func ipToAddr(ip net.IP) netip.Addr {
if ip == nil {
return netip.Addr{}
}
l := len(ip)
if l == 4 {
return netip.AddrFrom4(*(*[4]byte)(ip))
} else if l == 16 {
return netip.AddrFrom16(*(*[16]byte)(ip))
} else {
return netip.Addr{}
}
}
type wrapPacketConn struct { type wrapPacketConn struct {
net.PacketConn net.PacketConn
rAddr net.Addr rAddr net.Addr

27
go.mod
View File

@@ -3,34 +3,33 @@ module github.com/Dreamacro/clash
go 1.18 go 1.18
require ( require (
github.com/Dreamacro/go-shadowsocks2 v0.1.8 github.com/Dreamacro/go-shadowsocks2 v0.1.7
github.com/dlclark/regexp2 v1.4.0 github.com/dlclark/regexp2 v1.4.0
github.com/go-chi/chi/v5 v5.0.7 github.com/go-chi/chi/v5 v5.0.7
github.com/go-chi/cors v1.2.0 github.com/go-chi/cors v1.2.0
github.com/go-chi/render v1.0.1 github.com/go-chi/render v1.0.1
github.com/gofrs/uuid v4.2.0+incompatible github.com/gofrs/uuid v4.2.0+incompatible
github.com/gorilla/websocket v1.5.0 github.com/gorilla/websocket v1.5.0
github.com/insomniacslk/dhcp v0.0.0-20220405050111-12fbdcb11b41 github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd
github.com/lucas-clemente/quic-go v0.27.0 github.com/lucas-clemente/quic-go v0.26.0
github.com/miekg/dns v1.1.48 github.com/miekg/dns v1.1.47
github.com/oschwald/geoip2-golang v1.7.0 github.com/oschwald/geoip2-golang v1.7.0
github.com/sirupsen/logrus v1.8.1 github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.1 github.com/stretchr/testify v1.7.1
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 github.com/xtls/go v0.0.0-20210920065950-d4af136d3672
go.etcd.io/bbolt v1.3.6 go.etcd.io/bbolt v1.3.6
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.4.0
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064
golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd golang.org/x/net v0.0.0-20220325170049-de3da57026de
golang.org/x/net v0.0.0-20220412020605-290c469a71a5
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20220412071739-889880a91fd5 golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f
golang.org/x/time v0.0.0-20220411224347-583f2d630306 golang.org/x/time v0.0.0-20220224211638-0e9765cccd65
golang.zx2c4.com/wireguard v0.0.0-20220407013110-ef5c587f782d golang.zx2c4.com/wireguard v0.0.0-20220318042302-193cf8d6a5d6
golang.zx2c4.com/wireguard/windows v0.5.4-0.20220317000008-6432784c2469 golang.zx2c4.com/wireguard/windows v0.5.4-0.20220317000008-6432784c2469
google.golang.org/protobuf v1.28.0 google.golang.org/protobuf v1.28.0
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
gvisor.dev/gvisor v0.0.0-20220412020520-6917e582612b gvisor.dev/gvisor v0.0.0-20220326024801-5d1f3d24cb84
) )
require ( require (
@@ -51,10 +50,10 @@ require (
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab // indirect golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab // indirect
golang.org/x/tools v0.1.10 // indirect golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
) )
replace golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 => github.com/MetaCubeX/wintun-go v0.0.0-20220319102620-bbc5e6b2015e replace golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 => github.com/MetaCubeX/wintun-go v0.0.0-20220319102620-bbc5e6b2015e

54
go.sum
View File

@@ -8,8 +8,8 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Dreamacro/go-shadowsocks2 v0.1.8 h1:Ixejp5JscEc866gAvm/l6TFd7BOBvDviKgwb1quWw3g= github.com/Dreamacro/go-shadowsocks2 v0.1.7 h1:8CtbE1HoPPMfrQZGXmlluq6dO2lL31W6WRRE8fabc4Q=
github.com/Dreamacro/go-shadowsocks2 v0.1.8/go.mod h1:51y4Q6tJoCE7e8TmYXcQRqfoxPfE9Cvn79V6pB6Df7Y= github.com/Dreamacro/go-shadowsocks2 v0.1.7/go.mod h1:8p5G4cAj5ZlXwUR+Ww63gfSikr8kvw8uw3TDwLAJpUc=
github.com/MetaCubeX/wintun-go v0.0.0-20220319102620-bbc5e6b2015e h1:GRfT5Lf8HP7RNczKIwTYLoCh1PPuIs/sY9hj+W+3deg= github.com/MetaCubeX/wintun-go v0.0.0-20220319102620-bbc5e6b2015e h1:GRfT5Lf8HP7RNczKIwTYLoCh1PPuIs/sY9hj+W+3deg=
github.com/MetaCubeX/wintun-go v0.0.0-20220319102620-bbc5e6b2015e/go.mod h1:ARUuShAtcziEJ/vnZ2hgoP+zc0J7Ukcca2S/NPDoQCc= github.com/MetaCubeX/wintun-go v0.0.0-20220319102620-bbc5e6b2015e/go.mod h1:ARUuShAtcziEJ/vnZ2hgoP+zc0J7Ukcca2S/NPDoQCc=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
@@ -88,8 +88,8 @@ github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:Fecb
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
github.com/insomniacslk/dhcp v0.0.0-20220405050111-12fbdcb11b41 h1:Yg3n3AI7GoHnWt7dyjsLPU+TEuZfPAg0OdiA3MJUV6I= github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd h1:efcJu2Vzz6DoSq245deWNzTz6l/gsqdphm3FjmI88/g=
github.com/insomniacslk/dhcp v0.0.0-20220405050111-12fbdcb11b41/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= 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-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
@@ -106,8 +106,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lucas-clemente/quic-go v0.27.0 h1:v6WY87q9zD4dKASbG8hy/LpzAVNzEQzw8sEIeloJsc4= github.com/lucas-clemente/quic-go v0.26.0 h1:ALBQXr9UJ8A1LyzvceX4jd9QFsHvlI0RR6BkV16o00A=
github.com/lucas-clemente/quic-go v0.27.0/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI= github.com/lucas-clemente/quic-go v0.26.0/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
@@ -126,8 +126,8 @@ github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZ
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= 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/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.48 h1:Ucfr7IIVyMBz4lRE8qmGUuZ4Wt3/ZGu9hmcMT3Uu4tQ= github.com/miekg/dns v1.1.47 h1:J9bWiXbqMbnZPcY8Qi2E3EWIBsIm6MZzzJB9VRg5gL8=
github.com/miekg/dns v1.1.48/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
@@ -154,7 +154,6 @@ github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
@@ -192,6 +191,7 @@ github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
@@ -211,8 +211,8 @@ go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= 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.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk= go.uber.org/automaxprocs v1.4.0 h1:CpDZl6aOlLhReez+8S3eEotD7Jx0Os++lemPlMULQP0=
go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU= go.uber.org/automaxprocs v1.4.0/go.mod h1:/mTEdr7LvHhs0v7mjdxDreTz1OG5zdZGqgOnhWiR/+Q=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -221,8 +221,9 @@ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
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-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s=
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -252,11 +253,12 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/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-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
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-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
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-20220412020605-290c469a71a5 h1:bRb386wvrE+oBNdF1d/Xh9mQrfQ4ecYhW5qJ5GvTGT4= golang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc=
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -302,8 +304,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-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-20220412071739-889880a91fd5 h1:NubxfvTRuNb4RVzWrIDAUzUvREH1HkCD4JjyQTSG9As= golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f h1:rlezHXNlxYWvBCzNses9Dlc7nGFaNMJeqLolcmQSSZY=
golang.org/x/sys v0.0.0-20220412071739-889880a91fd5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/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.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -314,8 +316,8 @@ golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab h1:eHo2TTVBaAPw9lDGK2Gb9G
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-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs=
golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -332,11 +334,10 @@ golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= golang.zx2c4.com/wireguard v0.0.0-20220318042302-193cf8d6a5d6 h1:kgBK1EGuTIYbwoKROmsoV0FQp08gnCcVa110A4Unqhk=
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wireguard v0.0.0-20220318042302-193cf8d6a5d6/go.mod h1:bVQfyl2sCM/QIIGHpWbFGfHPuDvqnCNkT6MQLTCjO/U=
golang.zx2c4.com/wireguard v0.0.0-20220407013110-ef5c587f782d h1:q4JksJ2n0fmbXC0Aj0eOs6E0AcPqnKglxWXWFqGD6x0=
golang.zx2c4.com/wireguard v0.0.0-20220407013110-ef5c587f782d/go.mod h1:bVQfyl2sCM/QIIGHpWbFGfHPuDvqnCNkT6MQLTCjO/U=
golang.zx2c4.com/wireguard/windows v0.5.4-0.20220317000008-6432784c2469 h1:SEYkJAIuYAsSAPkCffOiYLtq5brBDSI+L0mRjSsvSTY= golang.zx2c4.com/wireguard/windows v0.5.4-0.20220317000008-6432784c2469 h1:SEYkJAIuYAsSAPkCffOiYLtq5brBDSI+L0mRjSsvSTY=
golang.zx2c4.com/wireguard/windows v0.5.4-0.20220317000008-6432784c2469/go.mod h1:1CeiatTZwcwSFA3cAtMm8CQoroviTldnxd7DOgM/vI4= golang.zx2c4.com/wireguard/windows v0.5.4-0.20220317000008-6432784c2469/go.mod h1:1CeiatTZwcwSFA3cAtMm8CQoroviTldnxd7DOgM/vI4=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
@@ -378,12 +379,11 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
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.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
gvisor.dev/gvisor v0.0.0-20220412020520-6917e582612b h1:JW1pUBe6A3H+b0B9DwEOcfK+TLS/04A3A9cettPpfV0= gvisor.dev/gvisor v0.0.0-20220326024801-5d1f3d24cb84 h1:nENO+rT8Nx+Vtp/VK+K7g9VpHdvJwJYCFgdN5yaoAzA=
gvisor.dev/gvisor v0.0.0-20220412020520-6917e582612b/go.mod h1:tWwEcFvJavs154OdjFCw78axNrsDlz4Zh8jvPqwcpGI= gvisor.dev/gvisor v0.0.0-20220326024801-5d1f3d24cb84/go.mod h1:tWwEcFvJavs154OdjFCw78axNrsDlz4Zh8jvPqwcpGI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -3,7 +3,6 @@ package executor
import ( import (
"fmt" "fmt"
"net" "net"
"net/netip"
"os" "os"
"runtime" "runtime"
"strconv" "strconv"
@@ -18,7 +17,6 @@ import (
"github.com/Dreamacro/clash/component/profile" "github.com/Dreamacro/clash/component/profile"
"github.com/Dreamacro/clash/component/profile/cachefile" "github.com/Dreamacro/clash/component/profile/cachefile"
"github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/component/resolver"
SNI "github.com/Dreamacro/clash/component/sniffer"
"github.com/Dreamacro/clash/component/trie" "github.com/Dreamacro/clash/component/trie"
"github.com/Dreamacro/clash/config" "github.com/Dreamacro/clash/config"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
@@ -77,7 +75,6 @@ func ApplyConfig(cfg *config.Config, force bool) {
updateUsers(cfg.Users) updateUsers(cfg.Users)
updateProxies(cfg.Proxies, cfg.Providers) updateProxies(cfg.Proxies, cfg.Providers)
updateRules(cfg.Rules, cfg.RuleProviders) updateRules(cfg.Rules, cfg.RuleProviders)
updateSniffer(cfg.Sniffer)
updateDNS(cfg.DNS, cfg.Tun) updateDNS(cfg.DNS, cfg.Tun)
updateGeneral(cfg.General, force) updateGeneral(cfg.General, force)
updateIPTables(cfg) updateIPTables(cfg)
@@ -172,7 +169,7 @@ func updateDNS(c *config.DNS, t *config.Tun) {
} }
} }
func updateHosts(tree *trie.DomainTrie[netip.Addr]) { func updateHosts(tree *trie.DomainTrie) {
resolver.DefaultHosts = tree resolver.DefaultHosts = tree
} }
@@ -220,26 +217,6 @@ func updateTun(tun *config.Tun, dns *config.DNS) {
P.ReCreateTun(tun, dns, tunnel.TCPIn(), tunnel.UDPIn()) P.ReCreateTun(tun, dns, tunnel.TCPIn(), tunnel.UDPIn())
} }
func updateSniffer(sniffer *config.Sniffer) {
if sniffer.Enable {
dispatcher, err := SNI.NewSnifferDispatcher(sniffer.Sniffers, sniffer.ForceDomain, sniffer.SkipSNI, sniffer.Ports)
if err != nil {
log.Warnln("initial sniffer failed, err:%v", err)
}
tunnel.UpdateSniffer(dispatcher)
log.Infoln("Sniffer is loaded and working")
} else {
dispatcher, err := SNI.NewCloseSnifferDispatcher()
if err != nil {
log.Warnln("initial sniffer failed, err:%v", err)
}
tunnel.UpdateSniffer(dispatcher)
log.Infoln("Sniffer is closed")
}
}
func updateGeneral(general *config.General, force bool) { func updateGeneral(general *config.General, force bool) {
log.SetLevel(general.LogLevel) log.SetLevel(general.LogLevel)
tunnel.SetMode(general.Mode) tunnel.SetMode(general.Mode)

View File

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

View File

@@ -40,9 +40,9 @@ func NewWithAuthenticate(addr string, in chan<- C.ConnContext, authenticate bool
return nil, err return nil, err
} }
var c *cache.Cache[string, bool] var c *cache.Cache
if authenticate { if authenticate {
c = cache.New[string, bool](time.Second * 30) c = cache.New(time.Second * 30)
} }
hl := &Listener{ hl := &Listener{

View File

@@ -40,7 +40,7 @@ func removeExtraHTTPHostPort(req *http.Request) {
host = req.URL.Host host = req.URL.Host
} }
if pHost, port, err := net.SplitHostPort(host); err == nil && (port == "80" || port == "443") { if pHost, port, err := net.SplitHostPort(host); err == nil && port == "80" {
host = pHost host = pHost
} }

View File

@@ -16,7 +16,7 @@ import (
type Listener struct { type Listener struct {
listener net.Listener listener net.Listener
addr string addr string
cache *cache.Cache[string, bool] cache *cache.Cache
closed bool closed bool
} }
@@ -45,7 +45,7 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) {
ml := &Listener{ ml := &Listener{
listener: l, listener: l,
addr: addr, addr: addr,
cache: cache.New[string, bool](30 * time.Second), cache: cache.New(30 * time.Second),
} }
go func() { go func() {
for { for {
@@ -63,7 +63,7 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) {
return ml, nil return ml, nil
} }
func handleConn(conn net.Conn, in chan<- C.ConnContext, cache *cache.Cache[string, bool]) { func handleConn(conn net.Conn, in chan<- C.ConnContext, cache *cache.Cache) {
conn.(*net.TCPConn).SetKeepAlive(true) conn.(*net.TCPConn).SetKeepAlive(true)
bufConn := N.NewBufferedConn(conn) bufConn := N.NewBufferedConn(conn)

View File

@@ -29,4 +29,7 @@ type Device interface {
// UseIOBased work for other ip stack // UseIOBased work for other ip stack
UseIOBased() error UseIOBased() error
// Wait waits for the device to close.
Wait()
} }

View File

@@ -36,9 +36,6 @@ type Endpoint struct {
// once is used to perform the init action once when attaching. // once is used to perform the init action once when attaching.
once sync.Once once sync.Once
// wg keeps track of running goroutines.
wg sync.WaitGroup
} }
// New returns stack.LinkEndpoint(.*Endpoint) and error. // New returns stack.LinkEndpoint(.*Endpoint) and error.
@@ -63,26 +60,19 @@ func New(rw io.ReadWriter, mtu uint32, offset int) (*Endpoint, error) {
}, nil }, nil
} }
func (e *Endpoint) Wait() { func (e *Endpoint) Close() {
e.wg.Wait() e.Endpoint.Close()
} }
// Attach launches the goroutine that reads packets from io.Reader and // Attach launches the goroutine that reads packets from io.Reader and
// dispatches them via the provided dispatcher. // dispatches them via the provided dispatcher.
func (e *Endpoint) Attach(dispatcher stack.NetworkDispatcher) { func (e *Endpoint) Attach(dispatcher stack.NetworkDispatcher) {
e.Endpoint.Attach(dispatcher)
e.once.Do(func() { e.once.Do(func() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
e.wg.Add(2) go e.dispatchLoop(cancel)
go func() { go e.outboundLoop(ctx)
e.outboundLoop(ctx)
e.wg.Done()
}()
go func() {
e.dispatchLoop(cancel)
e.wg.Done()
}()
}) })
e.Endpoint.Attach(dispatcher)
} }
// dispatchLoop dispatches packets to upper layer. // dispatchLoop dispatches packets to upper layer.
@@ -91,19 +81,14 @@ func (e *Endpoint) dispatchLoop(cancel context.CancelFunc) {
// gracefully after (*Endpoint).dispatchLoop(context.CancelFunc) returns. // gracefully after (*Endpoint).dispatchLoop(context.CancelFunc) returns.
defer cancel() defer cancel()
mtu := int(e.mtu)
for { for {
data := make([]byte, mtu) data := make([]byte, int(e.mtu))
n, err := e.rw.Read(data) n, err := e.rw.Read(data)
if err != nil { if err != nil {
break break
} }
if n == 0 || n > mtu {
continue
}
if !e.IsAttached() { if !e.IsAttached() {
continue /* unattached, drop packet */ continue /* unattached, drop packet */
} }

View File

@@ -5,11 +5,6 @@ import (
"golang.zx2c4.com/wireguard/tun" "golang.zx2c4.com/wireguard/tun"
) )
const (
offset = 0
defaultMTU = 0 /* auto */
)
func init() { func init() {
guid, _ := windows.GUIDFromString("{330EAEF8-7578-5DF2-D97B-8DADC0EA85CB}") guid, _ := windows.GUIDFromString("{330EAEF8-7578-5DF2-D97B-8DADC0EA85CB}")

View File

@@ -32,6 +32,15 @@ func Open(name string, mtu uint32) (_ device.Device, err error) {
} }
}() }()
var (
offset = 4 /* 4 bytes TUN_PI */
defaultMTU = 1500
)
if runtime.GOOS == "windows" {
offset = 0
defaultMTU = 0 /* auto */
}
t := &TUN{ t := &TUN{
name: name, name: name,
mtu: mtu, mtu: mtu,
@@ -92,11 +101,9 @@ func (t *TUN) Write(packet []byte) (int, error) {
} }
func (t *TUN) Close() error { func (t *TUN) Close() error {
defer func(ep *iobased.Endpoint) { if t.Endpoint != nil {
if ep != nil { t.Endpoint.Close()
ep.Close() }
}
}(t.Endpoint)
return t.nt.Close() return t.nt.Close()
} }

View File

@@ -1,8 +0,0 @@
//go:build !linux && !windows
package tun
const (
offset = 4 /* 4 bytes TUN_PI */
defaultMTU = 1500
)

View File

@@ -27,7 +27,7 @@ type GVHandler struct {
func (gh *GVHandler) HandleTCP(tunConn adapter.TCPConn) { func (gh *GVHandler) HandleTCP(tunConn adapter.TCPConn) {
id := tunConn.ID() id := tunConn.ID()
rAddr := &net.TCPAddr{ rAddr := &net.UDPAddr{
IP: net.IP(id.LocalAddress), IP: net.IP(id.LocalAddress),
Port: int(id.LocalPort), Port: int(id.LocalPort),
Zone: "", Zone: "",

View File

@@ -37,7 +37,7 @@ const (
// tcpModerateReceiveBufferEnabled is the value used by stack to // tcpModerateReceiveBufferEnabled is the value used by stack to
// enable or disable tcp receive buffer auto-tuning option. // enable or disable tcp receive buffer auto-tuning option.
tcpModerateReceiveBufferEnabled = false tcpModerateReceiveBufferEnabled = true
// tcpSACKEnabled is the value used by stack to enable or disable // tcpSACKEnabled is the value used by stack to enable or disable
// tcp selective ACK. // tcp selective ACK.
@@ -47,18 +47,14 @@ const (
tcpRecovery = tcpip.TCPRACKLossDetection tcpRecovery = tcpip.TCPRACKLossDetection
// tcpMinBufferSize is the smallest size of a send/recv buffer. // tcpMinBufferSize is the smallest size of a send/recv buffer.
tcpMinBufferSize = tcp.MinBufferSize tcpMinBufferSize = tcp.MinBufferSize // 4 KiB
// tcpMaxBufferSize is the maximum permitted size of a send/recv buffer. // tcpMaxBufferSize is the maximum permitted size of a send/recv buffer.
tcpMaxBufferSize = tcp.MaxBufferSize tcpMaxBufferSize = tcp.MaxBufferSize // 4 MiB
// tcpDefaultBufferSize is the default size of the send buffer for // tcpDefaultBufferSize is the default size of the send/recv buffer for
// a transport endpoint. // a transport endpoint.
tcpDefaultSendBufferSize = tcp.DefaultSendBufferSize tcpDefaultBufferSize = 212 << 10 // 212 KiB
// tcpDefaultReceiveBufferSize is the default size of the receive buffer
// for a transport endpoint.
tcpDefaultReceiveBufferSize = tcp.DefaultReceiveBufferSize
) )
type Option func(*stack.Stack) error type Option func(*stack.Stack) error
@@ -78,8 +74,7 @@ func WithDefault() Option {
// in too large buffers. // in too large buffers.
// //
// Ref: https://github.com/cloudflare/slirpnetstack/blob/master/stack.go // Ref: https://github.com/cloudflare/slirpnetstack/blob/master/stack.go
WithTCPSendBufferSizeRange(tcpMinBufferSize, tcpDefaultSendBufferSize, tcpMaxBufferSize), WithTCPBufferSizeRange(tcpMinBufferSize, tcpDefaultBufferSize, tcpMaxBufferSize),
WithTCPReceiveBufferSizeRange(tcpMinBufferSize, tcpDefaultReceiveBufferSize, tcpMaxBufferSize),
WithTCPCongestionControl(tcpCongestionControlAlgorithm), WithTCPCongestionControl(tcpCongestionControlAlgorithm),
WithTCPDelay(tcpDelayEnabled), WithTCPDelay(tcpDelayEnabled),
@@ -159,46 +154,17 @@ func WithICMPLimit(limit rate.Limit) Option {
} }
} }
// WithTCPSendBufferSize sets default the send buffer size for TCP. // WithTCPBufferSizeRange sets the receive and send buffer size range for TCP.
func WithTCPSendBufferSize(size int) Option { func WithTCPBufferSizeRange(a, b, c int) Option {
return func(s *stack.Stack) error {
sndOpt := tcpip.TCPSendBufferSizeRangeOption{Min: tcpMinBufferSize, Default: size, Max: tcpMaxBufferSize}
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &sndOpt); err != nil {
return fmt.Errorf("set TCP send buffer size range: %s", err)
}
return nil
}
}
// WithTCPSendBufferSizeRange sets the send buffer size range for TCP.
func WithTCPSendBufferSizeRange(a, b, c int) Option {
return func(s *stack.Stack) error {
sndOpt := tcpip.TCPSendBufferSizeRangeOption{Min: a, Default: b, Max: c}
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &sndOpt); err != nil {
return fmt.Errorf("set TCP send buffer size range: %s", err)
}
return nil
}
}
// WithTCPReceiveBufferSize sets the default receive buffer size for TCP.
func WithTCPReceiveBufferSize(size int) Option {
return func(s *stack.Stack) error {
rcvOpt := tcpip.TCPReceiveBufferSizeRangeOption{Min: tcpMinBufferSize, Default: size, Max: tcpMaxBufferSize}
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &rcvOpt); err != nil {
return fmt.Errorf("set TCP receive buffer size range: %s", err)
}
return nil
}
}
// WithTCPReceiveBufferSizeRange sets the receive buffer size range for TCP.
func WithTCPReceiveBufferSizeRange(a, b, c int) Option {
return func(s *stack.Stack) error { return func(s *stack.Stack) error {
rcvOpt := tcpip.TCPReceiveBufferSizeRangeOption{Min: a, Default: b, Max: c} rcvOpt := tcpip.TCPReceiveBufferSizeRangeOption{Min: a, Default: b, Max: c}
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &rcvOpt); err != nil { if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &rcvOpt); err != nil {
return fmt.Errorf("set TCP receive buffer size range: %s", err) return fmt.Errorf("set TCP receive buffer size range: %s", err)
} }
sndOpt := tcpip.TCPSendBufferSizeRangeOption{Min: a, Default: b, Max: c}
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &sndOpt); err != nil {
return fmt.Errorf("set TCP send buffer size range: %s", err)
}
return nil return nil
} }
} }

View File

@@ -25,6 +25,7 @@ func (s *gvStack) Close() error {
var err error var err error
if s.device != nil { if s.device != nil {
err = s.device.Close() err = s.device.Close()
s.device.Wait()
} }
if s.Stack != nil { if s.Stack != nil {
s.Stack.Close() s.Stack.Close()

View File

@@ -1,15 +1,14 @@
package gvisor package gvisor
import ( import (
"fmt"
"time" "time"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter" "github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option" "github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option"
"github.com/Dreamacro/clash/log"
"gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp" "gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/waiter" "gvisor.dev/gvisor/pkg/waiter"
@@ -44,21 +43,8 @@ const (
func withTCPHandler(handle adapter.TCPHandleFunc) option.Option { func withTCPHandler(handle adapter.TCPHandleFunc) option.Option {
return func(s *stack.Stack) error { return func(s *stack.Stack) error {
tcpForwarder := tcp.NewForwarder(s, defaultWndSize, maxConnAttempts, func(r *tcp.ForwarderRequest) { tcpForwarder := tcp.NewForwarder(s, defaultWndSize, maxConnAttempts, func(r *tcp.ForwarderRequest) {
var ( var wq waiter.Queue
wq waiter.Queue ep, err := r.CreateEndpoint(&wq)
ep tcpip.Endpoint
err tcpip.Error
id = r.ID()
)
defer func() {
if err != nil {
log.Warnln("[STACK] forward tcp request %s:%d->%s:%d: %s", id.RemoteAddress, id.RemotePort, id.LocalAddress, id.LocalPort, err)
}
}()
// Perform a TCP three-way handshake.
ep, err = r.CreateEndpoint(&wq)
if err != nil { if err != nil {
// RST: prevent potential half-open TCP connection leak. // RST: prevent potential half-open TCP connection leak.
r.Complete(true) r.Complete(true)
@@ -66,11 +52,11 @@ func withTCPHandler(handle adapter.TCPHandleFunc) option.Option {
} }
defer r.Complete(false) defer r.Complete(false)
err = setSocketOptions(s, ep) setKeepalive(ep)
conn := &tcpConn{ conn := &tcpConn{
TCPConn: gonet.NewTCPConn(&wq, ep), TCPConn: gonet.NewTCPConn(&wq, ep),
id: id, id: r.ID(),
} }
handle(conn) handle(conn)
}) })
@@ -79,34 +65,21 @@ func withTCPHandler(handle adapter.TCPHandleFunc) option.Option {
} }
} }
func setSocketOptions(s *stack.Stack, ep tcpip.Endpoint) tcpip.Error { func setKeepalive(ep tcpip.Endpoint) error {
{ /* TCP keepalive options */ ep.SocketOptions().SetKeepAlive(true)
ep.SocketOptions().SetKeepAlive(true)
idle := tcpip.KeepaliveIdleOption(tcpKeepaliveIdle) idle := tcpip.KeepaliveIdleOption(tcpKeepaliveIdle)
if err := ep.SetSockOpt(&idle); err != nil { if err := ep.SetSockOpt(&idle); err != nil {
return err return fmt.Errorf("set keepalive idle: %s", err)
}
interval := tcpip.KeepaliveIntervalOption(tcpKeepaliveInterval)
if err := ep.SetSockOpt(&interval); err != nil {
return err
}
if err := ep.SetSockOptInt(tcpip.KeepaliveCountOption, tcpKeepaliveCount); err != nil {
return err
}
} }
{ /* TCP recv/send buffer size */
var ss tcpip.TCPSendBufferSizeRangeOption
if err := s.TransportProtocolOption(header.TCPProtocolNumber, &ss); err == nil {
ep.SocketOptions().SetReceiveBufferSize(int64(ss.Default), false)
}
var rs tcpip.TCPReceiveBufferSizeRangeOption interval := tcpip.KeepaliveIntervalOption(tcpKeepaliveInterval)
if err := s.TransportProtocolOption(header.TCPProtocolNumber, &rs); err == nil { if err := ep.SetSockOpt(&interval); err != nil {
ep.SocketOptions().SetReceiveBufferSize(int64(rs.Default), false) return fmt.Errorf("set keepalive interval: %s", err)
} }
if err := ep.SetSockOptInt(tcpip.KeepaliveCountOption, tcpKeepaliveCount); err != nil {
return fmt.Errorf("set keepalive count: %s", err)
} }
return nil return nil
} }

View File

@@ -6,7 +6,6 @@ import (
"github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter" "github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option" "github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option"
"github.com/Dreamacro/clash/log"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/stack"
@@ -17,19 +16,16 @@ import (
func withUDPHandler(handle adapter.UDPHandleFunc) option.Option { func withUDPHandler(handle adapter.UDPHandleFunc) option.Option {
return func(s *stack.Stack) error { return func(s *stack.Stack) error {
udpForwarder := udp.NewForwarder(s, func(r *udp.ForwarderRequest) { udpForwarder := udp.NewForwarder(s, func(r *udp.ForwarderRequest) {
var ( var wq waiter.Queue
wq waiter.Queue
id = r.ID()
)
ep, err := r.CreateEndpoint(&wq) ep, err := r.CreateEndpoint(&wq)
if err != nil { if err != nil {
log.Warnln("[STACK] udp forwarder request %s:%d->%s:%d: %s", id.RemoteAddress, id.RemotePort, id.LocalAddress, id.LocalPort, err) // TODO: handler errors in the future.
return return
} }
conn := &udpConn{ conn := &udpConn{
UDPConn: gonet.NewUDPConn(s, &wq, ep), UDPConn: gonet.NewUDPConn(s, &wq, ep),
id: id, id: r.ID(),
} }
handle(conn) handle(conn)
}) })
@@ -58,7 +54,7 @@ func (c *packet) Data() []byte {
} }
// WriteBack write UDP packet with source(ip, port) = `addr` // WriteBack write UDP packet with source(ip, port) = `addr`
func (c *packet) WriteBack(b []byte, _ net.Addr) (n int, err error) { func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
return c.pc.WriteTo(b, c.rAddr) return c.pc.WriteTo(b, c.rAddr)
} }
@@ -68,5 +64,5 @@ func (c *packet) LocalAddr() net.Addr {
} }
func (c *packet) Drop() { func (c *packet) Drop() {
_ = pool.Put(c.payload) pool.Put(c.payload)
} }

View File

@@ -17,7 +17,6 @@ import (
"github.com/Dreamacro/clash/listener/tun/ipstack" "github.com/Dreamacro/clash/listener/tun/ipstack"
D "github.com/Dreamacro/clash/listener/tun/ipstack/commons" D "github.com/Dreamacro/clash/listener/tun/ipstack/commons"
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars" "github.com/Dreamacro/clash/listener/tun/ipstack/system/mars"
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/nat"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/socks5" "github.com/Dreamacro/clash/transport/socks5"
) )
@@ -25,20 +24,14 @@ import (
type sysStack struct { type sysStack struct {
stack io.Closer stack io.Closer
device device.Device device device.Device
closed bool
} }
func (s *sysStack) Close() error { func (s sysStack) Close() error {
defer func() {
if s.device != nil {
_ = s.device.Close()
}
}()
s.closed = true
if s.stack != nil { if s.stack != nil {
return s.stack.Close() _ = s.stack.Close()
}
if s.device != nil {
_ = s.device.Close()
} }
return nil return nil
} }
@@ -56,25 +49,17 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
return nil, err return nil, err
} }
ipStack := &sysStack{stack: stack, device: device}
dnsAddr := dnsHijack dnsAddr := dnsHijack
tcp := func() { tcp := func() {
defer func(tcp *nat.TCP) { defer stack.TCP().Close()
_ = tcp.Close()
}(stack.TCP())
defer log.Debugln("TCP: closed") defer log.Debugln("TCP: closed")
for !ipStack.closed { for stack.TCP().SetDeadline(time.Time{}) == nil {
if err = stack.TCP().SetDeadline(time.Time{}); err != nil {
break
}
conn, err := stack.TCP().Accept() conn, err := stack.TCP().Accept()
if err != nil { if err != nil {
log.Debugln("Accept connection: %v", err) log.Debugln("Accept connection: %v", err)
continue continue
} }
@@ -88,19 +73,13 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
go func() { go func() {
log.Debugln("[TUN] hijack dns tcp: %s", rAddrPort.String()) log.Debugln("[TUN] hijack dns tcp: %s", rAddrPort.String())
defer func(conn net.Conn) { defer conn.Close()
_ = conn.Close()
}(conn)
buf := pool.Get(pool.UDPBufferSize) buf := pool.Get(pool.UDPBufferSize)
defer func(buf []byte) { defer pool.Put(buf)
_ = pool.Put(buf)
}(buf)
for { for {
if err = conn.SetReadDeadline(time.Now().Add(C.DefaultTCPTimeout)); err != nil { conn.SetReadDeadline(time.Now().Add(C.DefaultTCPTimeout))
break
}
length := uint16(0) length := uint16(0)
if err := binary.Read(conn, binary.BigEndian, &length); err != nil { if err := binary.Read(conn, binary.BigEndian, &length); err != nil {
@@ -144,13 +123,10 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
} }
udp := func() { udp := func() {
defer func(udp *nat.UDP) { defer stack.UDP().Close()
_ = udp.Close()
}(stack.UDP())
defer log.Debugln("UDP: closed") defer log.Debugln("UDP: closed")
for !ipStack.closed { for {
buf := pool.Get(pool.UDPBufferSize) buf := pool.Get(pool.UDPBufferSize)
n, lRAddr, rRAddr, err := stack.UDP().ReadFrom(buf) n, lRAddr, rRAddr, err := stack.UDP().ReadFrom(buf)
@@ -167,16 +143,15 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
if D.ShouldHijackDns(dnsAddr, rAddrPort) { if D.ShouldHijackDns(dnsAddr, rAddrPort) {
go func() { go func() {
defer pool.Put(buf)
msg, err := D.RelayDnsPacket(raw) msg, err := D.RelayDnsPacket(raw)
if err != nil { if err != nil {
_ = pool.Put(buf)
return return
} }
_, _ = stack.UDP().WriteTo(msg, rAddr, lAddr) _, _ = stack.UDP().WriteTo(msg, rAddr, lAddr)
_ = pool.Put(buf)
log.Debugln("[TUN] hijack dns udp: %s", rAddrPort.String()) log.Debugln("[TUN] hijack dns udp: %s", rAddrPort.String())
}() }()
@@ -190,7 +165,7 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
return stack.UDP().WriteTo(b, rAddr, lAddr) return stack.UDP().WriteTo(b, rAddr, lAddr)
}, },
drop: func() { drop: func() {
_ = pool.Put(buf) pool.Put(buf)
}, },
} }
@@ -211,5 +186,5 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
go udp() go udp()
} }
return ipStack, nil return &sysStack{stack: stack, device: device}, nil
} }

View File

@@ -53,6 +53,7 @@ func New(tunConf *config.Tun, dnsConf *config.DNS, tcpIn chan<- C.ConnContext, u
process.AppendLocalIPs(tunAddress.Masked().Addr().Next().AsSlice()) process.AppendLocalIPs(tunAddress.Masked().Addr().Next().AsSlice())
// open tun device // open tun device
tunDevice, err = parseDevice(devName, uint32(mtu)) tunDevice, err = parseDevice(devName, uint32(mtu))
if err != nil { if err != nil {
return nil, fmt.Errorf("can't open tun: %w", err) return nil, fmt.Errorf("can't open tun: %w", err)
@@ -111,7 +112,7 @@ func New(tunConf *config.Tun, dnsConf *config.DNS, tcpIn chan<- C.ConnContext, u
func generateDeviceName() string { func generateDeviceName() string {
switch runtime.GOOS { switch runtime.GOOS {
case "darwin": case "darwin":
return tun.Driver + "://utun" return tun.Driver + "://Meta"
case "windows": case "windows":
return tun.Driver + "://Meta" return tun.Driver + "://Meta"
default: default:
@@ -148,10 +149,9 @@ func setAtLatest(stackType C.TUNStack, devName string) {
} }
switch runtime.GOOS { switch runtime.GOOS {
case "darwin":
_, _ = cmd.ExecCmd("sysctl net.inet.ip.forwarding=1")
case "windows": case "windows":
_, _ = cmd.ExecCmd("ipconfig /renew") _, _ = cmd.ExecCmd("ipconfig /renew")
case "linux": case "linux":
// _, _ = cmd.ExecCmd("sysctl -w net.ipv4.ip_forward=1") // _, _ = cmd.ExecCmd("sysctl -w net.ipv4.ip_forward=1")
// _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.forwarding = 1") // _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.forwarding = 1")

View File

@@ -13,23 +13,22 @@ type NetworkType struct {
} }
func NewNetworkType(network, adapter string) (*NetworkType, error) { func NewNetworkType(network, adapter string) (*NetworkType, error) {
ntType := NetworkType{ var netType C.NetWork
Base: &Base{},
}
ntType.adapter = adapter
switch strings.ToUpper(network) { switch strings.ToUpper(network) {
case "TCP": case "TCP":
ntType.network = C.TCP netType = C.TCP
break break
case "UDP": case "UDP":
ntType.network = C.UDP netType = C.UDP
break break
default: default:
return nil, fmt.Errorf("unsupported network type, only TCP/UDP") return nil, fmt.Errorf("unsupported network type, only TCP/UDP")
} }
return &NetworkType{
return &ntType, nil Base: &Base{},
network: netType,
adapter: adapter,
}, nil
} }
func (n *NetworkType) RuleType() C.RuleType { func (n *NetworkType) RuleType() C.RuleType {

View File

@@ -5,16 +5,20 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/Dreamacro/clash/common/utils"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
) )
type portReal struct {
portStart int
portEnd int
}
type Port struct { type Port struct {
*Base *Base
adapter string adapter string
port string port string
isSource bool isSource bool
portList []utils.Range[uint16] portList []portReal
} }
func (p *Port) RuleType() C.RuleType { func (p *Port) RuleType() C.RuleType {
@@ -41,13 +45,17 @@ func (p *Port) Payload() string {
func (p *Port) matchPortReal(portRef string) bool { func (p *Port) matchPortReal(portRef string) bool {
port, _ := strconv.Atoi(portRef) port, _ := strconv.Atoi(portRef)
var rs bool
for _, pr := range p.portList { for _, pr := range p.portList {
if pr.Contains(uint16(port)) { if pr.portEnd == -1 {
rs = port == pr.portStart
} else {
rs = port >= pr.portStart && port <= pr.portEnd
}
if rs {
return true return true
} }
} }
return false return false
} }
@@ -57,7 +65,7 @@ func NewPort(port string, adapter string, isSource bool) (*Port, error) {
return nil, fmt.Errorf("%s, too many ports to use, maximum support 28 ports", errPayload.Error()) return nil, fmt.Errorf("%s, too many ports to use, maximum support 28 ports", errPayload.Error())
} }
var portRange []utils.Range[uint16] var portList []portReal
for _, p := range ports { for _, p := range ports {
if p == "" { if p == "" {
continue continue
@@ -76,18 +84,23 @@ func NewPort(port string, adapter string, isSource bool) (*Port, error) {
switch subPortsLen { switch subPortsLen {
case 1: case 1:
portRange = append(portRange, *utils.NewRange(uint16(portStart), uint16(portStart))) portList = append(portList, portReal{int(portStart), -1})
case 2: case 2:
portEnd, err := strconv.ParseUint(strings.Trim(subPorts[1], "[ ]"), 10, 16) portEnd, err := strconv.ParseUint(strings.Trim(subPorts[1], "[ ]"), 10, 16)
if err != nil { if err != nil {
return nil, errPayload return nil, errPayload
} }
portRange = append(portRange, *utils.NewRange(uint16(portStart), uint16(portEnd))) shouldReverse := portStart > portEnd
if shouldReverse {
portList = append(portList, portReal{int(portEnd), int(portStart)})
} else {
portList = append(portList, portReal{int(portStart), int(portEnd)})
}
} }
} }
if len(portRange) == 0 { if len(portList) == 0 {
return nil, errPayload return nil, errPayload
} }
@@ -96,7 +109,7 @@ func NewPort(port string, adapter string, isSource bool) (*Port, error) {
adapter: adapter, adapter: adapter,
port: port, port: port,
isSource: isSource, isSource: isSource,
portList: portRange, portList: portList,
}, nil }, nil
} }

View File

@@ -51,10 +51,6 @@ func containRange(r Range, preStart, preEnd int) bool {
func payloadToRule(subPayload string) (C.Rule, error) { func payloadToRule(subPayload string) (C.Rule, error) {
splitStr := strings.SplitN(subPayload, ",", 2) splitStr := strings.SplitN(subPayload, ",", 2)
if len(splitStr) < 2 {
return nil, fmt.Errorf("[%s] format is error", subPayload)
}
tp := splitStr[0] tp := splitStr[0]
payload := splitStr[1] payload := splitStr[1]
if tp == "NOT" || tp == "OR" || tp == "AND" { if tp == "NOT" || tp == "OR" || tp == "AND" {
@@ -90,9 +86,9 @@ func parseRule(tp, payload string, params []string) (C.Rule, error) {
parsed, parseErr = RC.NewGEOIP(payload, "", noResolve) parsed, parseErr = RC.NewGEOIP(payload, "", noResolve)
case "IP-CIDR", "IP-CIDR6": case "IP-CIDR", "IP-CIDR6":
noResolve := RC.HasNoResolve(params) noResolve := RC.HasNoResolve(params)
parsed, parseErr = RC.NewIPCIDR(payload, "", RC.WithIPCIDRNoResolve(noResolve)) parsed, parseErr = RC.NewIPCIDR(payload, "", nil, RC.WithIPCIDRNoResolve(noResolve))
case "SRC-IP-CIDR": case "SRC-IP-CIDR":
parsed, parseErr = RC.NewIPCIDR(payload, "", RC.WithIPCIDRSourceIP(true), RC.WithIPCIDRNoResolve(true)) parsed, parseErr = RC.NewIPCIDR(payload, "", nil, RC.WithIPCIDRSourceIP(true), RC.WithIPCIDRNoResolve(true))
case "SRC-PORT": case "SRC-PORT":
parsed, parseErr = RC.NewPort(payload, "", true) parsed, parseErr = RC.NewPort(payload, "", true)
case "DST-PORT": case "DST-PORT":
@@ -112,7 +108,7 @@ func parseRule(tp, payload string, params []string) (C.Rule, error) {
case "NETWORK": case "NETWORK":
parsed, parseErr = RC.NewNetworkType(payload, "") parsed, parseErr = RC.NewNetworkType(payload, "")
default: default:
parsed, parseErr = nil, fmt.Errorf("unsupported rule type %s", tp) parseErr = fmt.Errorf("unsupported rule type %s", tp)
} }
if parseErr != nil { if parseErr != nil {
@@ -150,10 +146,6 @@ func format(payload string) ([]Range, error) {
num++ num++
stack.Push(sr) stack.Push(sr)
} else if c == ')' { } else if c == ')' {
if stack.Len() == 0 {
return nil, fmt.Errorf("missing '('")
}
sr := stack.Pop().(Range) sr := stack.Pop().(Range)
sr.end = i sr.end = i
subRanges = append(subRanges, sr) subRanges = append(subRanges, sr)

View File

@@ -1,49 +0,0 @@
package logic
import (
"github.com/Dreamacro/clash/constant"
"github.com/stretchr/testify/assert"
"testing"
)
func TestAND(t *testing.T) {
and, err := NewAND("((DOMAIN,baidu.com),(NETWORK,TCP),(DST-PORT,10001-65535))", "DIRECT")
assert.Equal(t, nil, err)
assert.Equal(t, "DIRECT", and.adapter)
assert.Equal(t, false, and.ShouldResolveIP())
assert.Equal(t, true, and.Match(&constant.Metadata{
Host: "baidu.com",
AddrType: constant.AtypDomainName,
NetWork: constant.TCP,
DstPort: "20000",
}))
and, err = NewAND("(DOMAIN,baidu.com),(NETWORK,TCP),(DST-PORT,10001-65535))", "DIRECT")
assert.NotEqual(t, nil, err)
and, err = NewAND("((AND,(DOMAIN,baidu.com),(NETWORK,TCP)),(NETWORK,TCP),(DST-PORT,10001-65535))", "DIRECT")
assert.Equal(t, nil, err)
}
func TestNOT(t *testing.T) {
not, err := NewNOT("((DST-PORT,6000-6500))", "REJECT")
assert.Equal(t, nil, err)
assert.Equal(t, false, not.Match(&constant.Metadata{
DstPort: "6100",
}))
_, err = NewNOT("((DST-PORT,5600-6666),(DOMAIN,baidu.com))", "DIRECT")
assert.NotEqual(t, nil, err)
_, err = NewNOT("(())", "DIRECT")
assert.NotEqual(t, nil, err)
}
func TestOR(t *testing.T) {
or, err := NewOR("((DOMAIN,baidu.com),(NETWORK,TCP),(DST-PORT,10001-65535))", "DIRECT")
assert.Equal(t, nil, err)
assert.Equal(t, true, or.Match(&constant.Metadata{
NetWork: constant.TCP,
}))
assert.Equal(t, false, or.ShouldResolveIP())
}

View File

@@ -24,14 +24,11 @@ func NewNOT(payload string, adapter string) (*NOT, error) {
return nil, err return nil, err
} }
if len(rule) > 1 { if len(rule) < 1 {
return nil, fmt.Errorf("not rule can contain at most one rule") return nil, fmt.Errorf("the parsed rule is empty")
}
if len(rule) > 0 {
not.rule = rule[0]
} }
not.rule = rule[0]
return not, nil return not, nil
} }
@@ -40,7 +37,7 @@ func (not *NOT) RuleType() C.RuleType {
} }
func (not *NOT) Match(metadata *C.Metadata) bool { func (not *NOT) Match(metadata *C.Metadata) bool {
return not.rule == nil || !not.rule.Match(metadata) return !not.rule.Match(metadata)
} }
func (not *NOT) Adapter() string { func (not *NOT) Adapter() string {
@@ -52,5 +49,5 @@ func (not *NOT) Payload() string {
} }
func (not *NOT) ShouldResolveIP() bool { func (not *NOT) ShouldResolveIP() bool {
return not.rule != nil && not.rule.ShouldResolveIP() return not.rule.ShouldResolveIP()
} }

View File

@@ -10,7 +10,7 @@ import (
type domainStrategy struct { type domainStrategy struct {
shouldResolveIP bool shouldResolveIP bool
count int count int
domainRules *trie.DomainTrie[bool] domainRules *trie.DomainTrie
} }
func (d *domainStrategy) Match(metadata *C.Metadata) bool { func (d *domainStrategy) Match(metadata *C.Metadata) bool {
@@ -26,9 +26,9 @@ func (d *domainStrategy) ShouldResolveIP() bool {
} }
func (d *domainStrategy) OnUpdate(rules []string) { func (d *domainStrategy) OnUpdate(rules []string) {
domainTrie := trie.New[bool]() domainTrie := trie.New()
for _, rule := range rules { for _, rule := range rules {
err := domainTrie.Insert(rule, true) err := domainTrie.Insert(rule, "")
if err != nil { if err != nil {
log.Warnln("invalid domain:[%s]", rule) log.Warnln("invalid domain:[%s]", rule)
} else { } else {

View File

@@ -148,7 +148,7 @@ func (t *Trojan) PresetXTLSConn(conn net.Conn) (net.Conn, error) {
xtlsConn.DirectMode = true xtlsConn.DirectMode = true
} }
} else { } else {
return conn, fmt.Errorf("failed to use %s, maybe \"security\" is not \"xtls\"", t.option.Flow) return nil, fmt.Errorf("failed to use %s, maybe \"security\" is not \"xtls\"", t.option.Flow)
} }
} }

View File

@@ -14,7 +14,6 @@ import (
"github.com/Dreamacro/clash/component/nat" "github.com/Dreamacro/clash/component/nat"
P "github.com/Dreamacro/clash/component/process" P "github.com/Dreamacro/clash/component/process"
"github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/component/resolver"
"github.com/Dreamacro/clash/component/sniffer"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/constant/provider" "github.com/Dreamacro/clash/constant/provider"
icontext "github.com/Dreamacro/clash/context" icontext "github.com/Dreamacro/clash/context"
@@ -37,8 +36,6 @@ var (
// default timeout for UDP session // default timeout for UDP session
udpTimeout = 60 * time.Second udpTimeout = 60 * time.Second
snifferDispatcher *sniffer.SnifferDispatcher
) )
func init() { func init() {
@@ -91,12 +88,6 @@ func UpdateProxies(newProxies map[string]C.Proxy, newProviders map[string]provid
configMux.Unlock() configMux.Unlock()
} }
func UpdateSniffer(dispatcher *sniffer.SnifferDispatcher) {
configMux.Lock()
sniffer.Dispatcher = *dispatcher
configMux.Unlock()
}
// Mode return current mode // Mode return current mode
func Mode() TunnelMode { func Mode() TunnelMode {
return mode return mode
@@ -158,7 +149,7 @@ func preHandleMetadata(metadata *C.Metadata) error {
metadata.DNSMode = C.DNSFakeIP metadata.DNSMode = C.DNSFakeIP
} else if node := resolver.DefaultHosts.Search(host); node != nil { } else if node := resolver.DefaultHosts.Search(host); node != nil {
// redir-host should lookup the hosts // redir-host should lookup the hosts
metadata.DstIP = node.Data.AsSlice() metadata.DstIP = node.Data.(net.IP)
} }
} else if resolver.IsFakeIP(metadata.DstIP) { } else if resolver.IsFakeIP(metadata.DstIP) {
return fmt.Errorf("fake DNS record %s missing", metadata.DstIP) return fmt.Errorf("fake DNS record %s missing", metadata.DstIP)
@@ -181,7 +172,7 @@ func preHandleMetadata(metadata *C.Metadata) error {
return nil return nil
} }
func resolveMetadata(_ C.PlainContext, metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) { func resolveMetadata(ctx C.PlainContext, metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) {
switch mode { switch mode {
case Direct: case Direct:
proxy = proxies["DIRECT"] proxy = proxies["DIRECT"]
@@ -217,7 +208,7 @@ func handleUDPConn(packet *inbound.PacketAdapter) {
handle := func() bool { handle := func() bool {
pc := natTable.Get(key) pc := natTable.Get(key)
if pc != nil { if pc != nil {
_ = handleUDPToRemote(packet, pc, metadata) handleUDPToRemote(packet, pc, metadata)
return true return true
} }
return false return false
@@ -290,9 +281,7 @@ func handleUDPConn(packet *inbound.PacketAdapter) {
} }
func handleTCPConn(connCtx C.ConnContext) { func handleTCPConn(connCtx C.ConnContext) {
defer func(conn net.Conn) { defer connCtx.Conn().Close()
_ = conn.Close()
}(connCtx.Conn())
metadata := connCtx.Metadata() metadata := connCtx.Metadata()
if !metadata.Valid() { if !metadata.Valid() {
@@ -305,10 +294,6 @@ func handleTCPConn(connCtx C.ConnContext) {
return return
} }
if sniffer.Dispatcher.Enable() {
sniffer.Dispatcher.TCPSniff(connCtx.Conn(), metadata)
}
proxy, rule, err := resolveMetadata(connCtx, metadata) proxy, rule, err := resolveMetadata(connCtx, metadata)
if err != nil { if err != nil {
log.Warnln("[Metadata] parse failed: %s", err.Error()) log.Warnln("[Metadata] parse failed: %s", err.Error())
@@ -327,9 +312,7 @@ func handleTCPConn(connCtx C.ConnContext) {
return return
} }
remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule) remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule)
defer func(remoteConn C.Conn) { defer remoteConn.Close()
_ = remoteConn.Close()
}(remoteConn)
switch true { switch true {
case rule != nil: case rule != nil:
@@ -362,7 +345,8 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
var resolved bool var resolved bool
if node := resolver.DefaultHosts.Search(metadata.Host); node != nil { if node := resolver.DefaultHosts.Search(metadata.Host); node != nil {
metadata.DstIP = node.Data.AsSlice() ip := node.Data.(net.IP)
metadata.DstIP = ip
resolved = true resolved = true
} }