diff --git a/config.go b/config.go index 06ede2e..5876a3f 100644 --- a/config.go +++ b/config.go @@ -44,7 +44,8 @@ func parseConfig() *Config { flag.StringSliceUniqVar(&conf.Forwards, "forward", nil, "forward url, format: SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS[,SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS]") flag.StringVar(&conf.StrategyConfig.Strategy, "strategy", "rr", "forward strategy, default: rr") - flag.StringVar(&conf.StrategyConfig.CheckWebSite, "checkwebsite", "www.apple.com", "fowarder check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80") + flag.StringVar(&conf.StrategyConfig.CheckType, "checktype", "http", "fowarder check type, http/tcp, default: http") + flag.StringVar(&conf.StrategyConfig.CheckAddr, "checkaddr", "www.apple.com:80", "fowarder check addr, format: HOST[:PORT], default port: 80,") flag.IntVar(&conf.StrategyConfig.CheckInterval, "checkinterval", 30, "fowarder check interval(seconds)") flag.IntVar(&conf.StrategyConfig.CheckTimeout, "checktimeout", 10, "fowarder check timeout(seconds)") flag.IntVar(&conf.StrategyConfig.CheckTolerance, "checktolerance", 0, "fowarder check tolerance(ms), switch only when new_latency < old_latency - tolerance, only used in lha mode") diff --git a/go.mod b/go.mod index 152d268..4b1b7e7 100644 --- a/go.mod +++ b/go.mod @@ -14,8 +14,9 @@ require ( github.com/nadoo/ipset v0.3.0 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/xtaci/kcp-go/v5 v5.6.1 - golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 - golang.org/x/tools v0.0.0-20201111224557-41a3a589386c // indirect + golang.org/x/crypto v0.0.0-20201116153603-4be66e5b6582 + golang.org/x/sys v0.0.0-20201116194326-cc9327a14d48 // indirect + golang.org/x/tools v0.0.0-20201117021029-3c3a81204b10 // indirect gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect ) diff --git a/go.sum b/go.sum index 5869c48..e277c0e 100644 --- a/go.sum +++ b/go.sum @@ -87,8 +87,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201116153603-4be66e5b6582 h1:0WDrJ1E7UolDk1KhTXxxw3Fc8qtk5x7dHP431KHEJls= +golang.org/x/crypto v0.0.0-20201116153603-4be66e5b6582/go.mod h1:tCqSYrHVcf3i63Co2FzBkTCo2gdF6Zak62921dSfraU= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -119,6 +119,7 @@ golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -127,6 +128,10 @@ golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201116194326-cc9327a14d48 h1:AYCWBZhgIw6XobZ5CibNJr0Rc4ZofGGKvWa1vcx2IGk= +golang.org/x/sys v0.0.0-20201116194326-cc9327a14d48/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201113234701-d7a72108b828 h1:htWEtQEuEVJ4tU/Ngx7Cd/4Q7e3A5Up1owgyBtVsTwk= +golang.org/x/term v0.0.0-20201113234701-d7a72108b828/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -136,8 +141,8 @@ golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201105001634-bc3cf281b174 h1:0rx0F4EjJNbxTuzWe0KjKcIzs+3VEb/Mrs/d1ciNz1c= golang.org/x/tools v0.0.0-20201105001634-bc3cf281b174/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201111224557-41a3a589386c h1:o4Kpkl2kZqc7n22gkunVhOJ+Ys5u3NrnXKAr2OgO5s0= -golang.org/x/tools v0.0.0-20201111224557-41a3a589386c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201117021029-3c3a81204b10 h1:epqY6OjPdDktZ8Cbnv7rUhy89e44hYWhxmhdecJr4cg= +golang.org/x/tools v0.0.0-20201117021029-3c3a81204b10/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/main.go b/main.go index b214390..71035f3 100644 --- a/main.go +++ b/main.go @@ -18,7 +18,7 @@ import ( ) var ( - version = "0.12.3" + version = "0.13.0" config = parseConfig() ) diff --git a/rule/config.go b/rule/config.go index 82296cc..c782ed9 100644 --- a/rule/config.go +++ b/rule/config.go @@ -26,7 +26,8 @@ type Config struct { // StrategyConfig is config of strategy. type StrategyConfig struct { Strategy string - CheckWebSite string + CheckType string + CheckAddr string CheckInterval int CheckTimeout int CheckTolerance int @@ -44,7 +45,8 @@ func NewConfFromFile(ruleFile string) (*Config, error) { f := conflag.NewFromFile("rule", ruleFile) f.StringSliceUniqVar(&p.Forward, "forward", nil, "forward url, format: SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS[,SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS]") f.StringVar(&p.StrategyConfig.Strategy, "strategy", "rr", "forward strategy, default: rr") - f.StringVar(&p.StrategyConfig.CheckWebSite, "checkwebsite", "www.apple.com", "fowarder check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80") + f.StringVar(&p.StrategyConfig.CheckType, "checktype", "http", "fowarder check type, http/tcp, default: http") + f.StringVar(&p.StrategyConfig.CheckAddr, "checkaddr", "www.apple.com:80", "fowarder check addr, format: HOST[:PORT], default port: 80,") f.IntVar(&p.StrategyConfig.CheckInterval, "checkinterval", 30, "fowarder check interval(seconds)") f.IntVar(&p.StrategyConfig.CheckTimeout, "checktimeout", 10, "fowarder check timeout(seconds)") f.IntVar(&p.StrategyConfig.CheckTolerance, "checktolerance", 0, "fowarder check tolerance(ms), switch only when new_latency < old_latency - tolerance, only used in lha mode") diff --git a/rule/group.go b/rule/group.go index 9a912cb..eb7288c 100644 --- a/rule/group.go +++ b/rule/group.go @@ -63,8 +63,8 @@ func newFwdrGroup(name string, fwdrs []*Forwarder, c *StrategyConfig) *FwdrGroup p.init() - if strings.IndexByte(p.config.CheckWebSite, ':') == -1 { - p.config.CheckWebSite += ":80" + if strings.IndexByte(p.config.CheckAddr, ':') == -1 { + p.config.CheckAddr += ":80" } // default scheduler @@ -180,15 +180,20 @@ func (p *FwdrGroup) onStatusChanged(fwdr *Forwarder) { // Check implements the Checker interface. func (p *FwdrGroup) Check() { + if p.config.CheckType != "http" && p.config.CheckType != "tcp" { + p.config.MaxFailures = 0 + return + } + // no need to check when there's only 1 forwarder if len(p.fwdrs) > 1 { for i := 0; i < len(p.fwdrs); i++ { - go p.check(p.fwdrs[i]) + go p.check(p.fwdrs[i], p.config.CheckType == "http") } } } -func (p *FwdrGroup) check(f *Forwarder) { +func (p *FwdrGroup) check(f *Forwarder, http bool) { wait := uint8(0) buf := make([]byte, 4) intval := time.Duration(p.config.CheckInterval) * time.Second @@ -205,9 +210,16 @@ func (p *FwdrGroup) check(f *Forwarder) { continue } - if checkWebSite(f, p.config.CheckWebSite, time.Duration(p.config.CheckTimeout)*time.Second, buf) { - wait = 1 - continue + if http { + if checkHttp(f, p.config.CheckAddr, time.Duration(p.config.CheckTimeout)*time.Second, buf) { + wait = 1 + continue + } + } else { + if checkTcp(f, p.config.CheckAddr, time.Duration(p.config.CheckTimeout)*time.Second) { + wait = 1 + continue + } } if wait == 0 { @@ -221,12 +233,12 @@ func (p *FwdrGroup) check(f *Forwarder) { } } -func checkWebSite(fwdr *Forwarder, website string, timeout time.Duration, buf []byte) bool { +func checkTcp(fwdr *Forwarder, addr string, timeout time.Duration) bool { startTime := time.Now() - rc, err := fwdr.Dial("tcp", website) + rc, err := fwdr.Dial("tcp", addr) if err != nil { - log.F("[check] %s(%d) -> %s, FAILED. error in dial: %s", fwdr.Addr(), fwdr.Priority(), website, err) + log.F("[check-tcp] %s(%d), FAILED. error in dial: %s", fwdr.Addr(), fwdr.Priority(), err) fwdr.Disable() return false } @@ -236,36 +248,66 @@ func checkWebSite(fwdr *Forwarder, website string, timeout time.Duration, buf [] rc.SetDeadline(time.Now().Add(timeout)) } - _, err = io.WriteString(rc, "GET / HTTP/1.1\r\nHost:"+website+"\r\nConnection: close"+"\r\n\r\n") + elapsed := time.Since(startTime) + fwdr.SetLatency(int64(elapsed)) + + if elapsed > timeout { + log.F("[check-tcp] %s(%d), FAILED. check timeout: %s", fwdr.Addr(), fwdr.Priority(), elapsed) + fwdr.Disable() + return false + } + + log.F("[check-tcp] %s(%d), SUCCESS. elapsed: %s", fwdr.Addr(), fwdr.Priority(), elapsed) + fwdr.Enable() + + return true +} + +func checkHttp(fwdr *Forwarder, addr string, timeout time.Duration, buf []byte) bool { + startTime := time.Now() + + rc, err := fwdr.Dial("tcp", addr) if err != nil { - log.F("[check] %s(%d) -> %s, FAILED. error in write: %s", fwdr.Addr(), fwdr.Priority(), website, err) + log.F("[check] %s(%d) -> %s, FAILED. error in dial: %s", fwdr.Addr(), fwdr.Priority(), addr, err) + fwdr.Disable() + return false + } + defer rc.Close() + + if timeout > 0 { + rc.SetDeadline(time.Now().Add(timeout)) + } + + _, err = io.WriteString(rc, "GET / HTTP/1.1\r\nHost:"+addr+"\r\nConnection: close"+"\r\n\r\n") + if err != nil { + log.F("[check] %s(%d) -> %s, FAILED. error in write: %s", fwdr.Addr(), fwdr.Priority(), addr, err) fwdr.Disable() return false } _, err = io.ReadFull(rc, buf) if err != nil { - log.F("[check] %s(%d) -> %s, FAILED. error in read: %s", fwdr.Addr(), fwdr.Priority(), website, err) + log.F("[check] %s(%d) -> %s, FAILED. error in read: %s", fwdr.Addr(), fwdr.Priority(), addr, err) fwdr.Disable() return false } if !bytes.Equal([]byte("HTTP"), buf) { - log.F("[check] %s(%d) -> %s, FAILED. server response: %s", fwdr.Addr(), fwdr.Priority(), website, buf) + log.F("[check] %s(%d) -> %s, FAILED. server response: %s", fwdr.Addr(), fwdr.Priority(), addr, buf) fwdr.Disable() return false } - readTime := time.Since(startTime) - fwdr.SetLatency(int64(readTime)) + elapsed := time.Since(startTime) + fwdr.SetLatency(int64(elapsed)) - if readTime > timeout { - log.F("[check] %s(%d) -> %s, FAILED. check timeout: %s", fwdr.Addr(), fwdr.Priority(), website, readTime) + if elapsed > timeout { + log.F("[check] %s(%d) -> %s, FAILED. check timeout: %s", fwdr.Addr(), fwdr.Priority(), addr, elapsed) fwdr.Disable() return false } - log.F("[check] %s(%d) -> %s, SUCCESS. elapsed time: %s", fwdr.Addr(), fwdr.Priority(), website, readTime) + log.F("[check] %s(%d) -> %s, SUCCESS. elapsed: %s", fwdr.Addr(), fwdr.Priority(), addr, elapsed) fwdr.Enable() return true