Skip to content

Commit

Permalink
Merge branch 'clashr-new' into clashr-new-tun
Browse files Browse the repository at this point in the history
  • Loading branch information
wwqgtxx committed Jan 7, 2021
2 parents 006595d + 06dd693 commit 2df1053
Show file tree
Hide file tree
Showing 28 changed files with 431 additions and 460 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.15.x
stable: false
go-version: '1.16.0-beta1'

- name: Check out code into the Go module directory
uses: actions/checkout@v2
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,18 @@
- Netfilter TCP redirecting. Deploy Clash on your Internet gateway with `iptables`.
- Comprehensive HTTP RESTful API controller

## Premium Features

- TUN mode on macOS, Linux and Windows. [Doc](https://github.com/Dreamacro/clash/wiki/premium-core-features#tun-device)
- Match your tunnel by [Script](https://github.com/Dreamacro/clash/wiki/premium-core-features#script)
- [Rule Provider](https://github.com/Dreamacro/clash/wiki/premium-core-features#rule-providers)

## Getting Started
Documentations are now moved to [GitHub Wiki](https://github.com/Dreamacro/clash/wiki).

## Premium Release
[Release](https://github.com/Dreamacro/clash/releases/tag/premium)

## Credits

* [riobard/go-shadowsocks2](https://github.com/riobard/go-shadowsocks2)
Expand Down
2 changes: 1 addition & 1 deletion adapters/outbound/shadowsocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type ShadowSocksOption struct {
}

type simpleObfsOption struct {
Mode string `obfs:"mode"`
Mode string `obfs:"mode,omitempty"`
Host string `obfs:"host,omitempty"`
}

Expand Down
5 changes: 4 additions & 1 deletion adapters/outbound/snell.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ func NewSnell(option SnellOption) (*Snell, error) {
return nil, fmt.Errorf("snell %s initialize obfs error: %w", addr, err)
}

if obfsOption.Mode != "tls" && obfsOption.Mode != "http" {
switch obfsOption.Mode {
case "tls", "http", "":
break
default:
return nil, fmt.Errorf("snell %s obfs mode error: %s", addr, obfsOption.Mode)
}

Expand Down
2 changes: 1 addition & 1 deletion adapters/outboundgroup/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func ParseProxyGroup(config map[string]interface{}, proxyMap map[string]C.Proxy,
decoder := structure.NewDecoder(structure.Option{TagName: "group", WeaklyTypedInput: true})

groupOption := &GroupCommonOption{
Lazy: true,
Lazy: provider.HealthCheckLazyDefault(),
}
if err := decoder.Decode(config, groupOption); err != nil {
return nil, errFormat
Expand Down
113 changes: 81 additions & 32 deletions adapters/provider/healthcheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,31 @@ const (
waitAfterAURLTest = time.Second * 1
)

var (
healthCheckLazyDefault = true
touchAfterLazyPassNum = 0
)

type HealthCheckOption struct {
URL string
Interval uint
}

type HealthCheck struct {
url string
proxies []C.Proxy
interval uint
lazy bool
lastTouch *atomic.Int64
done chan struct{}
gtype string
mutex sync.Mutex
checking bool
url string
proxies []C.Proxy
interval uint
lazy bool
lastTouch *atomic.Int64
done chan struct{}
gtype string
checking *atomic.Bool
cleanerRun *atomic.Bool
}

func (hc *HealthCheck) process() {
ticker := time.NewTicker(time.Duration(hc.interval) * time.Second)
passNum := 0

switch hc.gtype {
case "fallback":
Expand All @@ -48,12 +54,18 @@ func (hc *HealthCheck) process() {
case <-ticker.C:
now := time.Now().Unix()
if !hc.lazy || now-hc.lastTouch.Load() < int64(hc.interval) {
passNum = 0
switch hc.gtype {
case "fallback":
go hc.fallbackCheck()
default:
hc.check()
}
} else {
passNum++
if passNum > 0 && passNum > touchAfterLazyPassNum {
hc.touch()
}
}
case <-hc.done:
ticker.Stop()
Expand All @@ -76,53 +88,73 @@ func (hc *HealthCheck) touch() {

func (hc *HealthCheck) check() {
ctx, cancel := context.WithTimeout(context.Background(), defaultURLTestTimeout)
wg := &sync.WaitGroup{}
id := ""
if uid, err := uuid.NewV4(); err == nil {
id = uid.String()
}
log.Infoln("Start New Health Checking {%s}", id)
for _, proxy := range hc.proxies {
go func(proxy C.Proxy) {
proxy.URLTest(ctx, hc.url)
log.Infoln("Health Checked %s : %t %d ms {%s}", proxy.Name(), proxy.Alive(), proxy.LastDelay(), id)
go func(p C.Proxy) {
p.URLTest(ctx, hc.url)
wg.Done()
log.Infoln("Health Checked %s : %t %d ms {%s}", p.Name(), p.Alive(), p.LastDelay(), id)
}(proxy)
}

<-ctx.Done()
wg.Wait()
cancel()
log.Infoln("Finish A Health Checking {%s}", id)
}

func (hc *HealthCheck) fallbackCheck() {
hc.mutex.Lock()
if hc.checking {
hc.mutex.Unlock()
if hc.checking.Swap(true) {
log.Infoln("A Health Checking is Running, break")
return
}
hc.checking = true
hc.mutex.Unlock()
defer func() {
hc.mutex.Lock()
hc.checking = false
hc.mutex.Unlock()
hc.checking.Store(false)
}()
id := ""
if uid, err := uuid.NewV4(); err == nil {
id = uid.String()
}
log.Infoln("Start New Health Checking {%s}", id)
for _, proxy := range hc.proxies {
check := func(proxy C.Proxy) {
ctx, cancel := context.WithTimeout(context.Background(), defaultURLTestTimeout)
log.Infoln("Health Checking %s {%s}", proxy.Name(), id)
proxy.URLTest(ctx, hc.url)
//<-ctx.Done()
cancel()
log.Infoln("Health Checked %s : %t %d ms {%s}", proxy.Name(), proxy.Alive(), proxy.LastDelay(), id)
}
wait := func() {
<-time.After(waitAfterAURLTest)
}
log.Infoln("Start New Health Checking {%s}", id)
for i, proxy := range hc.proxies {
wait()
check(proxy)
if proxy.Alive() {
go func() {
if hc.cleanerRun.Swap(true) {
log.Infoln("A Health Check Cleaner is Running, break")
return
}
defer func() {
hc.cleanerRun.Store(false)
}()
log.Infoln("Start New Health Check Cleaner {%s}", id)
for _, proxy := range hc.proxies[i:] {
if proxy.Alive() {
continue
}
wait()
check(proxy)
}
log.Infoln("Finish A Health Check Cleaner {%s}", id)
}()
break
}
<-time.After(waitAfterAURLTest)
}

log.Infoln("Finish A Health Checking {%s}", id)
Expand All @@ -134,13 +166,30 @@ func (hc *HealthCheck) close() {

func NewHealthCheck(proxies []C.Proxy, url string, interval uint, lazy bool, gtype string) *HealthCheck {
return &HealthCheck{
proxies: proxies,
url: url,
interval: interval,
lazy: lazy,
lastTouch: atomic.NewInt64(0),
done: make(chan struct{}, 1),
gtype: gtype,
checking: false,
proxies: proxies,
url: url,
interval: interval,
lazy: lazy,
lastTouch: atomic.NewInt64(0),
done: make(chan struct{}, 1),
gtype: gtype,
checking: atomic.NewBool(false),
cleanerRun: atomic.NewBool(false),
}
}

func HealthCheckLazyDefault() bool {
return healthCheckLazyDefault
}

func SetHealthCheckLazyDefault(newHealthCheckLazyDefault bool) {
healthCheckLazyDefault = newHealthCheckLazyDefault
}

func TouchAfterLazyPassNum() int {
return touchAfterLazyPassNum
}

func SetTouchAfterLazyPassNum(newTouchAfterLazyPassNum int) {
touchAfterLazyPassNum = newTouchAfterLazyPassNum
}
2 changes: 1 addition & 1 deletion adapters/provider/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func ParseProxyProvider(name string, mapping map[string]interface{}) (ProxyProvi

schema := &proxyProviderSchema{
HealthCheck: healthCheckSchema{
Lazy: true,
Lazy: HealthCheckLazyDefault(),
},
}
if err := decoder.Decode(mapping, schema); err != nil {
Expand Down
2 changes: 2 additions & 0 deletions build.bat
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
cd %~dp0
rmdir /S /Q bin
mkdir bin
set CGO_ENABLED=0
set GOARCH=amd64
set GOOS=linux
Expand Down
Binary file added component/mmdb/Country.mmdb
Binary file not shown.
19 changes: 17 additions & 2 deletions component/mmdb/mmdb.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mmdb

import (
_ "embed"
"sync"

C "github.com/Dreamacro/clash/constant"
Expand All @@ -9,6 +10,9 @@ import (
"github.com/oschwald/geoip2-golang"
)

//go:embed Country.mmdb
var EmbedMMDB []byte

var mmdb *geoip2.Reader
var once sync.Once

Expand All @@ -22,8 +26,18 @@ func LoadFromBytes(buffer []byte) {
})
}

func getInstance() (instance *geoip2.Reader, err error) {
if path := C.Path.MMDB(); path == "embed" {
instance, err = geoip2.FromBytes(EmbedMMDB)
} else {
instance, err = geoip2.Open(C.Path.MMDB())
}

return
}

func Verify() bool {
instance, err := geoip2.Open(C.Path.MMDB())
instance, err := getInstance()
if err == nil {
instance.Close()
}
Expand All @@ -33,7 +47,8 @@ func Verify() bool {
func Instance() *geoip2.Reader {
once.Do(func() {
var err error
mmdb, err = geoip2.Open(C.Path.MMDB())
mmdb, err = getInstance()

if err != nil {
log.Fatalln("Can't load mmdb: %s", err.Error())
}
Expand Down
21 changes: 21 additions & 0 deletions component/process/process.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package process

import (
"errors"
"net"
)

var (
ErrInvalidNetwork = errors.New("invalid network")
ErrPlatformNotSupport = errors.New("not support on this platform")
ErrNotFound = errors.New("process not found")
)

const (
TCP = "tcp"
UDP = "udp"
)

func FindProcessName(network string, srcIP net.IP, srcPort int) (string, error) {
return findProcessName(network, srcIP, srcPort)
}
Loading

0 comments on commit 2df1053

Please sign in to comment.