Merge branch 'dev' into release
This commit is contained in:
commit
f03f33840e
@ -8,11 +8,13 @@ import (
|
|||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/constant/provider"
|
"github.com/Dreamacro/clash/constant/provider"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Fallback struct {
|
type Fallback struct {
|
||||||
*GroupBase
|
*GroupBase
|
||||||
disableUDP bool
|
disableUDP bool
|
||||||
|
testUrl string
|
||||||
selected string
|
selected string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,14 +93,26 @@ func (f *Fallback) findAliveProxy(touch bool) C.Proxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fallback) Set(name string) error {
|
func (f *Fallback) Set(name string) error {
|
||||||
|
var p C.Proxy
|
||||||
for _, proxy := range f.GetProxies(false) {
|
for _, proxy := range f.GetProxies(false) {
|
||||||
if proxy.Name() == name {
|
if proxy.Name() == name {
|
||||||
f.selected = name
|
p = proxy
|
||||||
return nil
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.New("proxy not exist")
|
if p == nil {
|
||||||
|
return errors.New("proxy not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
f.selected = name
|
||||||
|
if !p.Alive() {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(5000))
|
||||||
|
defer cancel()
|
||||||
|
_, _ = p.URLTest(ctx, f.testUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider) *Fallback {
|
func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider) *Fallback {
|
||||||
@ -114,5 +128,6 @@ func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider)
|
|||||||
providers,
|
providers,
|
||||||
}),
|
}),
|
||||||
disableUDP: option.DisableUDP,
|
disableUDP: option.DisableUDP,
|
||||||
|
testUrl: option.URL,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,14 +146,14 @@ func (gb *GroupBase) onDialFailed() {
|
|||||||
|
|
||||||
gb.failedTimes++
|
gb.failedTimes++
|
||||||
if gb.failedTimes == 1 {
|
if gb.failedTimes == 1 {
|
||||||
log.Warnln("ProxyGroup: %s first failed", gb.Name())
|
log.Debugln("ProxyGroup: %s first failed", gb.Name())
|
||||||
gb.failedTime = time.Now()
|
gb.failedTime = time.Now()
|
||||||
} else {
|
} else {
|
||||||
if time.Since(gb.failedTime) > gb.failedTimeoutInterval() {
|
if time.Since(gb.failedTime) > gb.failedTimeoutInterval() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Warnln("ProxyGroup: %s failed count: %d", gb.Name(), gb.failedTimes)
|
log.Debugln("ProxyGroup: %s failed count: %d", gb.Name(), gb.failedTimes)
|
||||||
if gb.failedTimes >= gb.maxFailedTimes() {
|
if gb.failedTimes >= gb.maxFailedTimes() {
|
||||||
gb.failedTesting.Store(true)
|
gb.failedTesting.Store(true)
|
||||||
log.Warnln("because %s failed multiple times, active health check", gb.Name())
|
log.Warnln("because %s failed multiple times, active health check", gb.Name())
|
||||||
|
@ -75,7 +75,7 @@ func (pp *proxySetProvider) Initial() error {
|
|||||||
|
|
||||||
pp.onUpdate(elm)
|
pp.onUpdate(elm)
|
||||||
if pp.healthCheck.auto() {
|
if pp.healthCheck.auto() {
|
||||||
go pp.healthCheck.process()
|
defer func() { go pp.healthCheck.process() }()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -2,16 +2,12 @@ package provider
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/Dreamacro/clash/listener/inner"
|
netHttp "github.com/Dreamacro/clash/component/http"
|
||||||
|
types "github.com/Dreamacro/clash/constant/provider"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
netHttp "github.com/Dreamacro/clash/common/net"
|
|
||||||
types "github.com/Dreamacro/clash/constant/provider"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type FileVehicle struct {
|
type FileVehicle struct {
|
||||||
@ -50,52 +46,16 @@ func (h *HTTPVehicle) Path() string {
|
|||||||
func (h *HTTPVehicle) Read() ([]byte, error) {
|
func (h *HTTPVehicle) Read() ([]byte, error) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
resp, err := netHttp.HttpRequest(ctx, h.url, http.MethodGet, nil, nil)
|
||||||
uri, err := url.Parse(h.url)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, uri.String(), nil)
|
|
||||||
req.Header.Set("User-Agent", netHttp.UA)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if user := uri.User; user != nil {
|
|
||||||
password, _ := user.Password()
|
|
||||||
req.SetBasicAuth(user.Username(), password)
|
|
||||||
}
|
|
||||||
|
|
||||||
req = req.WithContext(ctx)
|
|
||||||
|
|
||||||
transport := &http.Transport{
|
|
||||||
// from http.DefaultTransport
|
|
||||||
MaxIdleConns: 100,
|
|
||||||
IdleConnTimeout: 30 * time.Second,
|
|
||||||
TLSHandshakeTimeout: 10 * time.Second,
|
|
||||||
ExpectContinueTimeout: 1 * time.Second,
|
|
||||||
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
|
|
||||||
conn := inner.HandleTcp(address, uri.Hostname())
|
|
||||||
return conn, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
client := http.Client{Transport: transport}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
buf, err := io.ReadAll(resp.Body)
|
buf, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf, nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
package net
|
|
||||||
|
|
||||||
const (
|
|
||||||
UA = "Clash"
|
|
||||||
)
|
|
@ -30,7 +30,7 @@ func (l *loader) LoadGeoSiteWithAttr(file string, siteWithAttr string) ([]*route
|
|||||||
return nil, fmt.Errorf("empty listname in rule: %s", siteWithAttr)
|
return nil, fmt.Errorf("empty listname in rule: %s", siteWithAttr)
|
||||||
}
|
}
|
||||||
|
|
||||||
domains, err := l.LoadSite(file, list)
|
domains, err := l.LoadSiteByPath(file, list)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ func (l *loader) LoadGeoSiteWithAttr(file string, siteWithAttr string) ([]*route
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *loader) LoadGeoIP(country string) ([]*router.CIDR, error) {
|
func (l *loader) LoadGeoIP(country string) ([]*router.CIDR, error) {
|
||||||
return l.LoadIP(C.GeoipName, country)
|
return l.LoadIPByPath(C.GeoipName, country)
|
||||||
}
|
}
|
||||||
|
|
||||||
var loaders map[string]func() LoaderImplementation
|
var loaders map[string]func() LoaderImplementation
|
||||||
|
@ -5,8 +5,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type LoaderImplementation interface {
|
type LoaderImplementation interface {
|
||||||
LoadSite(filename, list string) ([]*router.Domain, error)
|
LoadSiteByPath(filename, list string) ([]*router.Domain, error)
|
||||||
LoadIP(filename, country string) ([]*router.CIDR, error)
|
LoadSiteByBytes(geositeBytes []byte, list string) ([]*router.Domain, error)
|
||||||
|
LoadIPByPath(filename, country string) ([]*router.CIDR, error)
|
||||||
|
LoadIPByBytes(geoipBytes []byte, country string) ([]*router.CIDR, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Loader interface {
|
type Loader interface {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package memconservative
|
package memconservative
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ type memConservativeLoader struct {
|
|||||||
geositecache GeoSiteCache
|
geositecache GeoSiteCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memConservativeLoader) LoadIP(filename, country string) ([]*router.CIDR, error) {
|
func (m *memConservativeLoader) LoadIPByPath(filename, country string) ([]*router.CIDR, error) {
|
||||||
defer runtime.GC()
|
defer runtime.GC()
|
||||||
geoip, err := m.geoipcache.Unmarshal(filename, country)
|
geoip, err := m.geoipcache.Unmarshal(filename, country)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -22,7 +23,11 @@ func (m *memConservativeLoader) LoadIP(filename, country string) ([]*router.CIDR
|
|||||||
return geoip.Cidr, nil
|
return geoip.Cidr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memConservativeLoader) LoadSite(filename, list string) ([]*router.Domain, error) {
|
func (m *memConservativeLoader) LoadIPByBytes(geoipBytes []byte, country string) ([]*router.CIDR, error) {
|
||||||
|
return nil, errors.New("memConservative do not support LoadIPByBytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memConservativeLoader) LoadSiteByPath(filename, list string) ([]*router.Domain, error) {
|
||||||
defer runtime.GC()
|
defer runtime.GC()
|
||||||
geosite, err := m.geositecache.Unmarshal(filename, list)
|
geosite, err := m.geositecache.Unmarshal(filename, list)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -31,6 +36,10 @@ func (m *memConservativeLoader) LoadSite(filename, list string) ([]*router.Domai
|
|||||||
return geosite.Domain, nil
|
return geosite.Domain, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *memConservativeLoader) LoadSiteByBytes(geositeBytes []byte, list string) ([]*router.Domain, error) {
|
||||||
|
return nil, errors.New("memConservative do not support LoadSiteByBytes")
|
||||||
|
}
|
||||||
|
|
||||||
func newMemConservativeLoader() geodata.LoaderImplementation {
|
func newMemConservativeLoader() geodata.LoaderImplementation {
|
||||||
return &memConservativeLoader{make(map[string]*router.GeoIP), make(map[string]*router.GeoSite)}
|
return &memConservativeLoader{make(map[string]*router.GeoIP), make(map[string]*router.GeoSite)}
|
||||||
}
|
}
|
||||||
|
@ -29,11 +29,7 @@ func ReadAsset(file string) ([]byte, error) {
|
|||||||
return ReadFile(C.Path.GetAssetLocation(file))
|
return ReadFile(C.Path.GetAssetLocation(file))
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadIP(filename, country string) ([]*router.CIDR, error) {
|
func loadIP(geoipBytes []byte, country string) ([]*router.CIDR, error) {
|
||||||
geoipBytes, err := ReadAsset(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error())
|
|
||||||
}
|
|
||||||
var geoipList router.GeoIPList
|
var geoipList router.GeoIPList
|
||||||
if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
|
if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -45,14 +41,10 @@ func loadIP(filename, country string) ([]*router.CIDR, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("country not found in %s%s%s", filename, ": ", country)
|
return nil, fmt.Errorf("country %s not found", country)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadSite(filename, list string) ([]*router.Domain, error) {
|
func loadSite(geositeBytes []byte, list string) ([]*router.Domain, error) {
|
||||||
geositeBytes, err := ReadAsset(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error())
|
|
||||||
}
|
|
||||||
var geositeList router.GeoSiteList
|
var geositeList router.GeoSiteList
|
||||||
if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {
|
if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -64,17 +56,33 @@ func loadSite(filename, list string) ([]*router.Domain, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("list not found in %s%s%s", filename, ": ", list)
|
return nil, fmt.Errorf("list %s not found", list)
|
||||||
}
|
}
|
||||||
|
|
||||||
type standardLoader struct{}
|
type standardLoader struct{}
|
||||||
|
|
||||||
func (d standardLoader) LoadSite(filename, list string) ([]*router.Domain, error) {
|
func (d standardLoader) LoadSiteByPath(filename, list string) ([]*router.Domain, error) {
|
||||||
return loadSite(filename, list)
|
geositeBytes, err := ReadAsset(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error())
|
||||||
|
}
|
||||||
|
return loadSite(geositeBytes, list)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d standardLoader) LoadIP(filename, country string) ([]*router.CIDR, error) {
|
func (d standardLoader) LoadSiteByBytes(geositeBytes []byte, list string) ([]*router.Domain, error) {
|
||||||
return loadIP(filename, country)
|
return loadSite(geositeBytes, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d standardLoader) LoadIPByPath(filename, country string) ([]*router.CIDR, error) {
|
||||||
|
geoipBytes, err := ReadAsset(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error())
|
||||||
|
}
|
||||||
|
return loadIP(geoipBytes, country)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d standardLoader) LoadIPByBytes(geoipBytes []byte, country string) ([]*router.CIDR, error) {
|
||||||
|
return loadIP(geoipBytes, country)
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
64
component/http/http.go
Normal file
64
component/http/http.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/Dreamacro/clash/listener/inner"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
URL "net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
UA = "Clash"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HttpRequest(ctx context.Context, url, method string, header map[string][]string, body io.Reader) (*http.Response, error) {
|
||||||
|
method = strings.ToUpper(method)
|
||||||
|
urlRes, err := URL.Parse(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(method, urlRes.String(), body)
|
||||||
|
for k, v := range header {
|
||||||
|
for _, v := range v {
|
||||||
|
req.Header.Add(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := header["User-Agent"]; !ok {
|
||||||
|
req.Header.Set("User-Agent", UA)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if user := urlRes.User; user != nil {
|
||||||
|
password, _ := user.Password()
|
||||||
|
req.SetBasicAuth(user.Username(), password)
|
||||||
|
}
|
||||||
|
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
|
||||||
|
transport := &http.Transport{
|
||||||
|
// from http.DefaultTransport
|
||||||
|
MaxIdleConns: 100,
|
||||||
|
IdleConnTimeout: 30 * time.Second,
|
||||||
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
|
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
log.Infoln(urlRes.String())
|
||||||
|
conn := inner.HandleTcp(address, urlRes.Hostname())
|
||||||
|
return conn, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
client := http.Client{Transport: transport}
|
||||||
|
return client.Do(req)
|
||||||
|
|
||||||
|
}
|
@ -6,77 +6,75 @@ import (
|
|||||||
_ "github.com/Dreamacro/clash/component/geodata/standard"
|
_ "github.com/Dreamacro/clash/component/geodata/standard"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/oschwald/geoip2-golang"
|
"github.com/oschwald/geoip2-golang"
|
||||||
"os"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func UpdateGeoDatabases() error {
|
func UpdateGeoDatabases() error {
|
||||||
var (
|
defer runtime.GC()
|
||||||
tmpMMDB = C.Path.Resolve("temp_country.mmdb")
|
|
||||||
tmpGepIP = C.Path.Resolve("temp_geoip.dat")
|
|
||||||
tmpGeoSite = C.Path.Resolve("temp_geosite.dat")
|
|
||||||
)
|
|
||||||
|
|
||||||
if C.GeodataMode {
|
|
||||||
if err := downloadGeoIP(tmpGepIP); err != nil {
|
|
||||||
return fmt.Errorf("can't download GeoIP database file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := verifyGeoSite("temp_geoip.dat"); err != nil {
|
|
||||||
_ = os.Remove(tmpGepIP)
|
|
||||||
return fmt.Errorf("invalid GeoIP database file: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.Rename(tmpGepIP, C.Path.GeoIP()); err != nil {
|
|
||||||
return fmt.Errorf("can't rename MMDB database file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if err := downloadMMDB(tmpMMDB); err != nil {
|
|
||||||
return fmt.Errorf("can't download MMDB database file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := verifyMMDB("temp_country.mmdb"); err != nil {
|
|
||||||
_ = os.Remove(tmpMMDB)
|
|
||||||
return fmt.Errorf("invalid MMDB database file: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.Rename(tmpMMDB, C.Path.MMDB()); err != nil {
|
|
||||||
return fmt.Errorf("can't rename MMDB database file: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := downloadGeoSite(tmpGeoSite); err != nil {
|
|
||||||
return fmt.Errorf("can't download GeoSite database file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := verifyGeoSite("temp_geosite.dat"); err != nil {
|
|
||||||
_ = os.Remove(tmpGeoSite)
|
|
||||||
return fmt.Errorf("invalid GeoSite database file: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.Rename(tmpGeoSite, C.Path.GeoSite()); err != nil {
|
|
||||||
return fmt.Errorf("can't rename GeoSite database file: %w", err)
|
|
||||||
}
|
|
||||||
runtime.GC()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func verifyMMDB(path string) error {
|
|
||||||
instance, err := geoip2.Open(path)
|
|
||||||
if err == nil {
|
|
||||||
_ = instance.Close()
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func verifyGeoSite(path string) error {
|
|
||||||
geoLoader, err := geodata.GetGeoDataLoader("standard")
|
geoLoader, err := geodata.GetGeoDataLoader("standard")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = geoLoader.LoadSite(path, "cn")
|
if C.GeodataMode {
|
||||||
|
data, err := downloadForBytes(C.GeoIpUrl)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't download GeoIP database file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
if _, err = geoLoader.LoadIPByBytes(data, "cn"); err != nil {
|
||||||
|
return fmt.Errorf("invalid GeoIP database file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if saveFile(data, C.Path.GeoIP()) != nil {
|
||||||
|
return fmt.Errorf("can't save GeoIP database file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
data, err := downloadForBytes(C.MmdbUrl)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't download MMDB database file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
instance, err := geoip2.FromBytes(data)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid MMDB database file: %s", err)
|
||||||
|
}
|
||||||
|
_ = instance.Close()
|
||||||
|
|
||||||
|
if saveFile(data, C.Path.MMDB()) != nil {
|
||||||
|
return fmt.Errorf("can't save MMDB database file: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := downloadForBytes(C.GeoSiteUrl)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't download GeoSite database file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = geoLoader.LoadSiteByBytes(data, "cn"); err != nil {
|
||||||
|
return fmt.Errorf("invalid GeoSite database file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if saveFile(data, C.Path.GeoSite()) != nil {
|
||||||
|
return fmt.Errorf("can't save GeoSite database file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadForBytes(url string) ([]byte, error) {
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
return ioutil.ReadAll(resp.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveFile(bytes []byte, path string) error {
|
||||||
|
return ioutil.WriteFile(path, bytes, 0o644)
|
||||||
}
|
}
|
||||||
|
7
constant/features/no_doq.go
Normal file
7
constant/features/no_doq.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
//go:build no_doq
|
||||||
|
|
||||||
|
package features
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
TAGS = append(TAGS, "no_doq")
|
||||||
|
}
|
7
constant/features/no_gvisor.go
Normal file
7
constant/features/no_gvisor.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
//go:build no_gvisor
|
||||||
|
|
||||||
|
package features
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
TAGS = append(TAGS, "no_gvisor")
|
||||||
|
}
|
3
constant/features/tags.go
Normal file
3
constant/features/tags.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package features
|
||||||
|
|
||||||
|
var TAGS = make([]string, 0, 0)
|
@ -1,3 +1,5 @@
|
|||||||
|
//go:build !no_gvisor
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
29
listener/tun/device/device_no_gvisor.go
Normal file
29
listener/tun/device/device_no_gvisor.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
//go:build no_gvisor
|
||||||
|
|
||||||
|
package device
|
||||||
|
|
||||||
|
// Device is the interface that implemented by network layer devices (e.g. tun),
|
||||||
|
// and easy to use as stack.LinkEndpoint.
|
||||||
|
type Device interface {
|
||||||
|
|
||||||
|
// Name returns the current name of the device.
|
||||||
|
Name() string
|
||||||
|
|
||||||
|
// Type returns the driver type of the device.
|
||||||
|
Type() string
|
||||||
|
|
||||||
|
// Read packets from tun device
|
||||||
|
Read(packet []byte) (int, error)
|
||||||
|
|
||||||
|
// Write packets to tun device
|
||||||
|
Write(packet []byte) (int, error)
|
||||||
|
|
||||||
|
// Close stops and closes the device.
|
||||||
|
Close() error
|
||||||
|
|
||||||
|
// UseEndpoint work for gVisor stack
|
||||||
|
UseEndpoint() error
|
||||||
|
|
||||||
|
// UseIOBased work for other ip stack
|
||||||
|
UseIOBased() error
|
||||||
|
}
|
@ -4,24 +4,13 @@ package fdbased
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/listener/tun/device"
|
"github.com/Dreamacro/clash/listener/tun/device"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type FD struct {
|
|
||||||
stack.LinkEndpoint
|
|
||||||
|
|
||||||
fd int
|
|
||||||
mtu uint32
|
|
||||||
|
|
||||||
file *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
func Open(name string, mtu uint32) (device.Device, error) {
|
func Open(name string, mtu uint32) (device.Device, error) {
|
||||||
fd, err := strconv.Atoi(name)
|
fd, err := strconv.Atoi(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
17
listener/tun/device/fdbased/fd_unix_gvisor.go
Normal file
17
listener/tun/device/fdbased/fd_unix_gvisor.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//go:build !no_gvisor
|
||||||
|
|
||||||
|
package fdbased
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FD struct {
|
||||||
|
stack.LinkEndpoint
|
||||||
|
|
||||||
|
fd int
|
||||||
|
mtu uint32
|
||||||
|
|
||||||
|
file *os.File
|
||||||
|
}
|
14
listener/tun/device/fdbased/fd_unix_no_gvisor.go
Normal file
14
listener/tun/device/fdbased/fd_unix_no_gvisor.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
//go:build no_gvisor
|
||||||
|
|
||||||
|
package fdbased
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FD struct {
|
||||||
|
fd int
|
||||||
|
mtu uint32
|
||||||
|
|
||||||
|
file *os.File
|
||||||
|
}
|
@ -7,7 +7,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/listener/tun/device"
|
"github.com/Dreamacro/clash/listener/tun/device"
|
||||||
"github.com/Dreamacro/clash/listener/tun/device/iobased"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func open(fd int, mtu uint32) (device.Device, error) {
|
func open(fd int, mtu uint32) (device.Device, error) {
|
||||||
@ -17,12 +16,7 @@ func open(fd int, mtu uint32) (device.Device, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *FD) useEndpoint() error {
|
func (f *FD) useEndpoint() error {
|
||||||
ep, err := iobased.New(os.NewFile(uintptr(f.fd), f.Name()), f.mtu, 0)
|
return newEp(f)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("create endpoint: %w", err)
|
|
||||||
}
|
|
||||||
f.LinkEndpoint = ep
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FD) useIOBased() error {
|
func (f *FD) useIOBased() error {
|
||||||
|
19
listener/tun/device/fdbased/open_others_gvisor.go
Normal file
19
listener/tun/device/fdbased/open_others_gvisor.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
//go:build !no_gvisor && !linux && !windows
|
||||||
|
|
||||||
|
package fdbased
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/device/iobased"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newEp(f *FD) error {
|
||||||
|
ep, err := iobased.New(os.NewFile(uintptr(f.fd), f.Name()), f.mtu, 0)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create endpoint: %w", err)
|
||||||
|
}
|
||||||
|
f.LinkEndpoint = ep
|
||||||
|
return nil
|
||||||
|
}
|
11
listener/tun/device/fdbased/open_others_no_gvisor.go
Normal file
11
listener/tun/device/fdbased/open_others_no_gvisor.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
//go:build no_gvisor && !linux && !windows
|
||||||
|
|
||||||
|
package fdbased
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newEp(f *FD) error {
|
||||||
|
return fmt.Errorf("unsupported gvisor on the build")
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
//go:build !no_gvisor
|
||||||
|
|
||||||
// Package iobased provides the implementation of io.ReadWriter
|
// Package iobased provides the implementation of io.ReadWriter
|
||||||
// based data-link layer endpoints.
|
// based data-link layer endpoints.
|
||||||
package iobased
|
package iobased
|
||||||
|
1
listener/tun/device/iobased/iobased.go
Normal file
1
listener/tun/device/iobased/iobased.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package iobased
|
@ -8,22 +8,10 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/listener/tun/device"
|
"github.com/Dreamacro/clash/listener/tun/device"
|
||||||
"github.com/Dreamacro/clash/listener/tun/device/iobased"
|
|
||||||
|
|
||||||
"golang.zx2c4.com/wireguard/tun"
|
"golang.zx2c4.com/wireguard/tun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TUN struct {
|
|
||||||
*iobased.Endpoint
|
|
||||||
|
|
||||||
nt *tun.NativeTun
|
|
||||||
mtu uint32
|
|
||||||
name string
|
|
||||||
offset int
|
|
||||||
|
|
||||||
cache []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func Open(name string, mtu uint32) (_ device.Device, err error) {
|
func Open(name string, mtu uint32) (_ device.Device, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@ -91,11 +79,7 @@ func (t *TUN) Write(packet []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *TUN) Close() error {
|
func (t *TUN) Close() error {
|
||||||
defer func(ep *iobased.Endpoint) {
|
defer closeIO(t)
|
||||||
if ep != nil {
|
|
||||||
ep.Close()
|
|
||||||
}
|
|
||||||
}(t.Endpoint)
|
|
||||||
return t.nt.Close()
|
return t.nt.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,12 +89,7 @@ func (t *TUN) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *TUN) UseEndpoint() error {
|
func (t *TUN) UseEndpoint() error {
|
||||||
ep, err := iobased.New(t, t.mtu, t.offset)
|
return newEq(t)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("create endpoint: %w", err)
|
|
||||||
}
|
|
||||||
t.Endpoint = ep
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TUN) UseIOBased() error {
|
func (t *TUN) UseIOBased() error {
|
||||||
|
34
listener/tun/device/tun/tun_wireguard_gvisor.go
Normal file
34
listener/tun/device/tun/tun_wireguard_gvisor.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
//go:build !linux && !no_gvisor
|
||||||
|
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/device/iobased"
|
||||||
|
"golang.zx2c4.com/wireguard/tun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TUN struct {
|
||||||
|
*iobased.Endpoint
|
||||||
|
nt *tun.NativeTun
|
||||||
|
mtu uint32
|
||||||
|
name string
|
||||||
|
offset int
|
||||||
|
|
||||||
|
cache []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeIO(t *TUN) {
|
||||||
|
if t.Endpoint != nil {
|
||||||
|
t.Endpoint.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEq(t *TUN) error {
|
||||||
|
ep, err := iobased.New(t, t.mtu, t.offset)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create endpoint: %w", err)
|
||||||
|
}
|
||||||
|
t.Endpoint = ep
|
||||||
|
return nil
|
||||||
|
}
|
24
listener/tun/device/tun/tun_wireguard_no_gvisor.go
Normal file
24
listener/tun/device/tun/tun_wireguard_no_gvisor.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//go:build !linux && no_gvisor
|
||||||
|
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.zx2c4.com/wireguard/tun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TUN struct {
|
||||||
|
nt *tun.NativeTun
|
||||||
|
mtu uint32
|
||||||
|
name string
|
||||||
|
offset int
|
||||||
|
|
||||||
|
cache []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeIO(t *TUN) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEq(t *TUN) error {
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
//go:build !no_gvisor
|
||||||
|
|
||||||
package adapter
|
package adapter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//go:build !no_gvisor
|
||||||
|
|
||||||
package adapter
|
package adapter
|
||||||
|
|
||||||
// Handler is a TCP/UDP connection handler that implements
|
// Handler is a TCP/UDP connection handler that implements
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//go:build !no_gvisor
|
||||||
|
|
||||||
package gvisor
|
package gvisor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//go:build !no_gvisor
|
||||||
|
|
||||||
package gvisor
|
package gvisor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//go:build !no_gvisor
|
||||||
|
|
||||||
package option
|
package option
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//go:build !no_gvisor
|
||||||
|
|
||||||
package gvisor
|
package gvisor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//go:build !no_gvisor
|
||||||
|
|
||||||
// Package gvisor provides a thin wrapper around a gVisor's stack.
|
// Package gvisor provides a thin wrapper around a gVisor's stack.
|
||||||
package gvisor
|
package gvisor
|
||||||
|
|
||||||
@ -64,8 +66,9 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
|
|||||||
|
|
||||||
// Generate unique NIC id.
|
// Generate unique NIC id.
|
||||||
nicID := tcpip.NICID(s.Stack.UniqueID())
|
nicID := tcpip.NICID(s.Stack.UniqueID())
|
||||||
|
defaultOpts := []option.Option{option.WithDefault()}
|
||||||
opts = append(opts,
|
defaultOpts = append(defaultOpts, opts...)
|
||||||
|
opts = append(defaultOpts,
|
||||||
// Create stack NIC and then bind link endpoint to it.
|
// Create stack NIC and then bind link endpoint to it.
|
||||||
withCreatingNIC(nicID, device),
|
withCreatingNIC(nicID, device),
|
||||||
|
|
||||||
|
19
listener/tun/ipstack/gvisor/stack_no_gvisor.go
Normal file
19
listener/tun/ipstack/gvisor/stack_no_gvisor.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
//go:build no_gvisor
|
||||||
|
|
||||||
|
package gvisor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Dreamacro/clash/adapter/inbound"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/device"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/ipstack"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"net/netip"
|
||||||
|
)
|
||||||
|
|
||||||
|
// New allocates a new *gvStack with given options.
|
||||||
|
func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) {
|
||||||
|
log.Fatalln("unsupported gvisor stack on the build")
|
||||||
|
return nil, fmt.Errorf("unsupported gvisor stack on the build")
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
//go:build !no_gvisor
|
||||||
|
|
||||||
package gvisor
|
package gvisor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//go:build !no_gvisor
|
||||||
|
|
||||||
package gvisor
|
package gvisor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
"github.com/Dreamacro/clash/listener/tun/ipstack"
|
"github.com/Dreamacro/clash/listener/tun/ipstack"
|
||||||
"github.com/Dreamacro/clash/listener/tun/ipstack/commons"
|
"github.com/Dreamacro/clash/listener/tun/ipstack/commons"
|
||||||
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor"
|
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor"
|
||||||
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option"
|
|
||||||
"github.com/Dreamacro/clash/listener/tun/ipstack/system"
|
"github.com/Dreamacro/clash/listener/tun/ipstack/system"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
@ -63,7 +62,7 @@ func New(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.
|
|||||||
return nil, fmt.Errorf("can't attach endpoint to tun: %w", err)
|
return nil, fmt.Errorf("can't attach endpoint to tun: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tunStack, err = gvisor.New(tunDevice, tunConf.DNSHijack, tunAddress, tcpIn, udpIn, option.WithDefault())
|
tunStack, err = gvisor.New(tunDevice, tunConf.DNSHijack, tunAddress, tcpIn, udpIn)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tunDevice.Close()
|
_ = tunDevice.Close()
|
||||||
|
9
main.go
9
main.go
@ -3,10 +3,12 @@ package main
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/Dreamacro/clash/constant/features"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/config"
|
"github.com/Dreamacro/clash/config"
|
||||||
@ -50,7 +52,12 @@ func init() {
|
|||||||
func main() {
|
func main() {
|
||||||
_, _ = maxprocs.Set(maxprocs.Logger(func(string, ...any) {}))
|
_, _ = maxprocs.Set(maxprocs.Logger(func(string, ...any) {}))
|
||||||
if version {
|
if version {
|
||||||
fmt.Printf("Clash Meta %s %s %s with %s %s\n", C.Version, runtime.GOOS, runtime.GOARCH, runtime.Version(), C.BuildTime)
|
fmt.Printf("Clash Meta %s %s %s with %s %s\n",
|
||||||
|
C.Version, runtime.GOOS, runtime.GOARCH, runtime.Version(), C.BuildTime)
|
||||||
|
if len(features.TAGS) != 0 {
|
||||||
|
fmt.Printf("Use tags: %s\n", strings.Join(features.TAGS, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,20 +30,24 @@ func (c *classicalStrategy) ShouldResolveIP() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *classicalStrategy) OnUpdate(rules []string) {
|
func (c *classicalStrategy) OnUpdate(rules []string) {
|
||||||
|
var classicalRules []C.Rule
|
||||||
|
shouldResolveIP := false
|
||||||
for _, rawRule := range rules {
|
for _, rawRule := range rules {
|
||||||
ruleType, rule, params := ruleParse(rawRule)
|
ruleType, rule, params := ruleParse(rawRule)
|
||||||
r, err := parseRule(ruleType, rule, "", params)
|
r, err := parseRule(ruleType, rule, "", params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnln("parse rule error:[%s]", err.Error())
|
log.Warnln("parse rule error:[%s]", err.Error())
|
||||||
} else {
|
} else {
|
||||||
if !c.shouldResolveIP {
|
if !shouldResolveIP {
|
||||||
c.shouldResolveIP = r.ShouldResolveIP()
|
shouldResolveIP = r.ShouldResolveIP()
|
||||||
}
|
}
|
||||||
|
|
||||||
c.rules = append(c.rules, r)
|
classicalRules = append(classicalRules, r)
|
||||||
c.count++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.rules = classicalRules
|
||||||
|
c.count = len(classicalRules)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClassicalStrategy() *classicalStrategy {
|
func NewClassicalStrategy() *classicalStrategy {
|
||||||
|
Reference in New Issue
Block a user