diff --git a/conf.go b/conf.go index f8f81a7..19c5850 100644 --- a/conf.go +++ b/conf.go @@ -10,7 +10,6 @@ import ( "github.com/nadoo/glider/dns" "github.com/nadoo/glider/rule" - "github.com/nadoo/glider/strategy" ) var flag = conflag.New() @@ -21,7 +20,7 @@ var conf struct { Listen []string Forward []string - StrategyConfig strategy.Config + StrategyConfig rule.StrategyConfig RuleFile []string RulesDir string diff --git a/go.mod b/go.mod index 636c617..7347142 100644 --- a/go.mod +++ b/go.mod @@ -10,8 +10,8 @@ require ( github.com/xtaci/kcp-go/v5 v5.5.15 golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a golang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect - golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 // indirect - golang.org/x/tools v0.0.0-20200913032122-97363e29fc9b // indirect + golang.org/x/sys v0.0.0-20200922070232-aee5d888a860 // indirect + golang.org/x/tools v0.0.0-20200923053713-ba800b16d873 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect ) diff --git a/go.sum b/go.sum index 42bfe66..02d30b6 100644 --- a/go.sum +++ b/go.sum @@ -120,15 +120,15 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9 h1:yi1hN8dcqI9l8klZfy4B8mJvFmmAxJEePIQQFNSd7Cs= golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200922070232-aee5d888a860 h1:YEu4SMq7D0cmT7CBbXfcH0NZeuChAXwsHe/9XueUO6o= +golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200425043458-8463f397d07c h1:iHhCR0b26amDCiiO+kBguKZom9aMF+NrFxh9zeKR/XU= golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200913032122-97363e29fc9b h1:3/5GThpuWHBq2GFcurHBWuWlzdbln+Er+cyzGqQAPOs= -golang.org/x/tools v0.0.0-20200913032122-97363e29fc9b/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20200923053713-ba800b16d873 h1:Q5Sq7Lt0bkn6Ax1NAraQhKRN7xxxy1LV4guxsyFHZx4= +golang.org/x/tools v0.0.0-20200923053713-ba800b16d873/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= 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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= diff --git a/main.go b/main.go index 539b1ea..c4ff691 100644 --- a/main.go +++ b/main.go @@ -1,18 +1,20 @@ package main import ( + "context" "fmt" stdlog "log" + "net" "os" "os/signal" "syscall" + "time" "github.com/nadoo/glider/common/log" "github.com/nadoo/glider/dns" "github.com/nadoo/glider/ipset" "github.com/nadoo/glider/proxy" "github.com/nadoo/glider/rule" - "github.com/nadoo/glider/strategy" // comment out the protocol you don't need to make the compiled binary smaller. _ "github.com/nadoo/glider/proxy/http" @@ -34,7 +36,7 @@ import ( _ "github.com/nadoo/glider/proxy/ws" ) -var version = "0.10.4" +var version = "0.11.0" func main() { // read configs @@ -48,7 +50,7 @@ func main() { } // global rule proxy - p := rule.NewProxy(conf.rules, strategy.NewProxy("default", conf.Forward, &conf.StrategyConfig)) + p := rule.NewProxy(conf.Forward, &conf.StrategyConfig, conf.rules) // ipset manager ipsetM, _ := ipset.NewManager(conf.rules) @@ -69,6 +71,15 @@ func main() { } } + // custom resolver + net.DefaultResolver = &net.Resolver{ + PreferGo: true, + Dial: func(ctx context.Context, network, address string) (net.Conn, error) { + d := net.Dialer{Timeout: time.Second * 3} + return d.DialContext(ctx, "udp", conf.DNS) + }, + } + // add a handler to update proxy rules when a domain resolved d.AddHandler(p.AddDomainIP) if ipsetM != nil { diff --git a/proxy/dialer.go b/proxy/dialer.go index bad5668..be82bee 100644 --- a/proxy/dialer.go +++ b/proxy/dialer.go @@ -37,12 +37,12 @@ type UDPDialer interface { type DialerCreator func(s string, dialer Dialer) (Dialer, error) var ( - dialerMap = make(map[string]DialerCreator) + dialerCreators = make(map[string]DialerCreator) ) // RegisterDialer is used to register a dialer. func RegisterDialer(name string, c DialerCreator) { - dialerMap[name] = c + dialerCreators[name] = c } // DialerFromURL calls the registered creator to create dialers. @@ -58,7 +58,7 @@ func DialerFromURL(s string, dialer Dialer) (Dialer, error) { return nil, err } - c, ok := dialerMap[strings.ToLower(u.Scheme)] + c, ok := dialerCreators[strings.ToLower(u.Scheme)] if ok { return c(s, dialer) } diff --git a/proxy/server.go b/proxy/server.go index 78fdac1..2e1cc28 100644 --- a/proxy/server.go +++ b/proxy/server.go @@ -22,12 +22,12 @@ type Server interface { type ServerCreator func(s string, proxy Proxy) (Server, error) var ( - serverMap = make(map[string]ServerCreator) + serverCreators = make(map[string]ServerCreator) ) // RegisterServer is used to register a proxy server func RegisterServer(name string, c ServerCreator) { - serverMap[name] = c + serverCreators[name] = c } // ServerFromURL calls the registered creator to create proxy servers @@ -47,7 +47,7 @@ func ServerFromURL(s string, p Proxy) (Server, error) { return nil, err } - c, ok := serverMap[strings.ToLower(u.Scheme)] + c, ok := serverCreators[strings.ToLower(u.Scheme)] if ok { return c(s, p) } diff --git a/rule/config.go b/rule/config.go index 895b41b..8820a18 100644 --- a/rule/config.go +++ b/rule/config.go @@ -7,8 +7,6 @@ import ( "strings" "github.com/nadoo/conflag" - - "github.com/nadoo/glider/strategy" ) // Config of rule dialer. @@ -16,7 +14,7 @@ type Config struct { Name string Forward []string - StrategyConfig strategy.Config + StrategyConfig StrategyConfig DNSServers []string IPSet string diff --git a/strategy/forward.go b/rule/forward.go similarity index 99% rename from strategy/forward.go rename to rule/forward.go index 1ebcc51..d828718 100644 --- a/strategy/forward.go +++ b/rule/forward.go @@ -1,4 +1,4 @@ -package strategy +package rule import ( "net" diff --git a/rule/rule.go b/rule/rule.go index 74d76b3..4d68188 100644 --- a/rule/rule.go +++ b/rule/rule.go @@ -7,77 +7,83 @@ import ( "github.com/nadoo/glider/common/log" "github.com/nadoo/glider/proxy" - "github.com/nadoo/glider/strategy" ) // Proxy struct. type Proxy struct { - proxy *strategy.Proxy - proxies []*strategy.Proxy - + main *FwdrGroup + all []*FwdrGroup domainMap sync.Map ipMap sync.Map cidrMap sync.Map } // NewProxy returns a new rule proxy. -func NewProxy(rules []*Config, proxy *strategy.Proxy) *Proxy { - rd := &Proxy{proxy: proxy} +func NewProxy(mainForwarders []string, mainStrategy *StrategyConfig, rules []*Config) *Proxy { + rd := &Proxy{main: NewFwdrGroup("main", mainForwarders, mainStrategy)} for _, r := range rules { - sd := strategy.NewProxy(r.Name, r.Forward, &r.StrategyConfig) - rd.proxies = append(rd.proxies, sd) + group := NewFwdrGroup(r.Name, r.Forward, &r.StrategyConfig) + rd.all = append(rd.all, group) for _, domain := range r.Domain { - rd.domainMap.Store(strings.ToLower(domain), sd) + rd.domainMap.Store(strings.ToLower(domain), group) } for _, ip := range r.IP { - rd.ipMap.Store(ip, sd) + rd.ipMap.Store(ip, group) } for _, s := range r.CIDR { if _, cidr, err := net.ParseCIDR(s); err == nil { - rd.cidrMap.Store(cidr, sd) + rd.cidrMap.Store(cidr, group) } } } + if len(mainForwarders) > 0 { + direct := NewFwdrGroup("backup", nil, mainStrategy) + for _, f := range rd.main.fwdrs { + // Note: the addr maybe ip address, but no matter here. + rd.domainMap.Store(strings.ToLower(strings.Split(f.addr, ":")[0]), direct) + } + } + return rd } // Dial dials to targer addr and return a conn. func (p *Proxy) Dial(network, addr string) (net.Conn, proxy.Dialer, error) { - return p.nextProxy(addr).Dial(network, addr) + return p.chooseProxy(addr).Dial(network, addr) } // DialUDP connects to the given address via the proxy. func (p *Proxy) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) { - return p.nextProxy(addr).DialUDP(network, addr) + return p.chooseProxy(addr).DialUDP(network, addr) } -// nextProxy return next proxy according to rule. -func (p *Proxy) nextProxy(dstAddr string) *strategy.Proxy { +// chooseProxy returns a proxy according to rule. +func (p *Proxy) chooseProxy(dstAddr string) *FwdrGroup { host, _, err := net.SplitHostPort(dstAddr) if err != nil { // TODO: check here // logf("[rule] SplitHostPort ERROR: %s", err) - return p.proxy + return p.main } // find ip if ip := net.ParseIP(host); ip != nil { // check ip if proxy, ok := p.ipMap.Load(ip.String()); ok { - return proxy.(*strategy.Proxy) + return proxy.(*FwdrGroup) } - var ret *strategy.Proxy + var ret *FwdrGroup // check cidr p.cidrMap.Range(func(key, value interface{}) bool { cidr := key.(*net.IPNet) if cidr.Contains(ip) { - ret = value.(*strategy.Proxy) + ret = value.(*FwdrGroup) return false } @@ -94,21 +100,21 @@ func (p *Proxy) nextProxy(dstAddr string) *strategy.Proxy { for i := len(host); i != -1; { i = strings.LastIndexByte(host[:i], '.') if proxy, ok := p.domainMap.Load(host[i+1:]); ok { - return proxy.(*strategy.Proxy) + return proxy.(*FwdrGroup) } } - return p.proxy + return p.main } -// NextDialer return next dialer according to rule. +// NextDialer returns next dialer according to rule. func (p *Proxy) NextDialer(dstAddr string) proxy.Dialer { - return p.nextProxy(dstAddr).NextDialer(dstAddr) + return p.chooseProxy(dstAddr).NextDialer(dstAddr) } // Record records result while using the dialer from proxy. func (p *Proxy) Record(dialer proxy.Dialer, success bool) { - strategy.OnRecord(dialer, success) + OnRecord(dialer, success) } // AddDomainIP used to update ipMap rules according to domainMap rule. @@ -128,9 +134,9 @@ func (p *Proxy) AddDomainIP(domain, ip string) error { // Check . func (p *Proxy) Check() { - p.proxy.Check() + p.main.Check() - for _, d := range p.proxies { - d.Check() + for _, fwdr := range p.all { + fwdr.Check() } } diff --git a/strategy/strategy.go b/rule/strategy.go similarity index 83% rename from strategy/strategy.go rename to rule/strategy.go index 68d9674..1a8552a 100644 --- a/strategy/strategy.go +++ b/rule/strategy.go @@ -1,4 +1,4 @@ -package strategy +package rule import ( "bytes" @@ -15,8 +15,8 @@ import ( "github.com/nadoo/glider/proxy" ) -// Config is strategy config struct. -type Config struct { +// StrategyConfig is strategy config struct. +type StrategyConfig struct { Strategy string CheckWebSite string CheckInterval int @@ -35,9 +35,9 @@ func (p priSlice) Len() int { return len(p) } func (p priSlice) Less(i, j int) bool { return p[i].Priority() > p[j].Priority() } func (p priSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } -// Proxy is base proxy struct. -type Proxy struct { - config *Config +// FwdrGroup is a forwarder group. +type FwdrGroup struct { + config *StrategyConfig fwdrs priSlice avail []*Forwarder // available forwarders mu sync.RWMutex @@ -46,8 +46,8 @@ type Proxy struct { next func(addr string) *Forwarder } -// NewProxy returns a new strategy proxy. -func NewProxy(name string, s []string, c *Config) *Proxy { +// NewFwdrGroup returns a new forward group. +func NewFwdrGroup(name string, s []string, c *StrategyConfig) *FwdrGroup { var fwdrs []*Forwarder for _, chain := range s { fwdr, err := ForwarderFromURL(chain, c.IntFace, @@ -66,12 +66,12 @@ func NewProxy(name string, s []string, c *Config) *Proxy { c.Strategy = "rr" } - return newProxy(name, fwdrs, c) + return newFwdrGroup(name, fwdrs, c) } -// newProxy returns a new Proxy. -func newProxy(name string, fwdrs []*Forwarder, c *Config) *Proxy { - p := &Proxy{fwdrs: fwdrs, config: c} +// newFwdrGroup returns a new Proxy. +func newFwdrGroup(name string, fwdrs []*Forwarder, c *StrategyConfig) *FwdrGroup { + p := &FwdrGroup{fwdrs: fwdrs, config: c} sort.Sort(p.fwdrs) p.init() @@ -106,19 +106,19 @@ func newProxy(name string, fwdrs []*Forwarder, c *Config) *Proxy { } // Dial connects to the address addr on the network net. -func (p *Proxy) Dial(network, addr string) (net.Conn, proxy.Dialer, error) { +func (p *FwdrGroup) Dial(network, addr string) (net.Conn, proxy.Dialer, error) { nd := p.NextDialer(addr) c, err := nd.Dial(network, addr) return c, nd, err } // DialUDP connects to the given address. -func (p *Proxy) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) { +func (p *FwdrGroup) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) { return p.NextDialer(addr).DialUDP(network, addr) } // NextDialer returns the next dialer. -func (p *Proxy) NextDialer(dstAddr string) proxy.Dialer { +func (p *FwdrGroup) NextDialer(dstAddr string) proxy.Dialer { p.mu.RLock() defer p.mu.RUnlock() @@ -130,7 +130,7 @@ func (p *Proxy) NextDialer(dstAddr string) proxy.Dialer { } // Record records result while using the dialer from proxy. -func (p *Proxy) Record(dialer proxy.Dialer, success bool) { +func (p *FwdrGroup) Record(dialer proxy.Dialer, success bool) { OnRecord(dialer, success) } @@ -146,13 +146,13 @@ func OnRecord(dialer proxy.Dialer, success bool) { } // Priority returns the active priority of dialer. -func (p *Proxy) Priority() uint32 { return atomic.LoadUint32(&p.priority) } +func (p *FwdrGroup) Priority() uint32 { return atomic.LoadUint32(&p.priority) } // SetPriority sets the active priority of daler. -func (p *Proxy) SetPriority(pri uint32) { atomic.StoreUint32(&p.priority, pri) } +func (p *FwdrGroup) SetPriority(pri uint32) { atomic.StoreUint32(&p.priority, pri) } // init traverse d.fwdrs and init the available forwarder slice. -func (p *Proxy) init() { +func (p *FwdrGroup) init() { for _, f := range p.fwdrs { if f.Enabled() { p.SetPriority(f.Priority()) @@ -175,7 +175,7 @@ func (p *Proxy) init() { } // onStatusChanged will be called when fwdr's status changed. -func (p *Proxy) onStatusChanged(fwdr *Forwarder) { +func (p *FwdrGroup) onStatusChanged(fwdr *Forwarder) { p.mu.Lock() defer p.mu.Unlock() @@ -202,7 +202,7 @@ func (p *Proxy) onStatusChanged(fwdr *Forwarder) { } // Check implements the Checker interface. -func (p *Proxy) Check() { +func (p *FwdrGroup) Check() { // no need to check when there's only 1 forwarder if len(p.fwdrs) > 1 { for i := 0; i < len(p.fwdrs); i++ { @@ -211,7 +211,7 @@ func (p *Proxy) Check() { } } -func (p *Proxy) check(f *Forwarder) { +func (p *FwdrGroup) check(f *Forwarder) { wait := uint8(0) buf := make([]byte, 4) intval := time.Duration(p.config.CheckInterval) * time.Second @@ -301,17 +301,17 @@ func checkWebSite(fwdr *Forwarder, website string, timeout time.Duration, buf [] } // Round Robin -func (p *Proxy) scheduleRR(dstAddr string) *Forwarder { +func (p *FwdrGroup) scheduleRR(dstAddr string) *Forwarder { return p.avail[atomic.AddUint32(&p.index, 1)%uint32(len(p.avail))] } // High Availability -func (p *Proxy) scheduleHA(dstAddr string) *Forwarder { +func (p *FwdrGroup) scheduleHA(dstAddr string) *Forwarder { return p.avail[0] } // Latency based High Availability -func (p *Proxy) scheduleLHA(dstAddr string) *Forwarder { +func (p *FwdrGroup) scheduleLHA(dstAddr string) *Forwarder { fwdr := p.avail[0] lowest := fwdr.Latency() for _, f := range p.avail { @@ -324,7 +324,7 @@ func (p *Proxy) scheduleLHA(dstAddr string) *Forwarder { } // Destination Hashing -func (p *Proxy) scheduleDH(dstAddr string) *Forwarder { +func (p *FwdrGroup) scheduleDH(dstAddr string) *Forwarder { fnv1a := fnv.New32a() fnv1a.Write([]byte(dstAddr)) return p.avail[fnv1a.Sum32()%uint32(len(p.avail))]