rule: optimized codes

This commit is contained in:
nadoo 2020-09-24 18:50:04 +08:00
parent 84b00d6db6
commit 04c65fb444
10 changed files with 97 additions and 100 deletions

View File

@ -88,10 +88,10 @@ Use the linux server's ip as your dns server.
#### When client requesting to access http://example1.com (in office.rule), the whole process:
DNS Resolving:
1. client sends a udp dns request to linux server, and glider will receive the request(as it listen on default dns port :53)
1. client sends a udp dns request to linux server, and glider will receive the request(as it listens on the default dns port :53)
2. upstream dns server choice: glider will lookup it's rule config and find out the dns server to use for this domain(matched "example1.com" in office.rule, so 208.67.222.222:53 will be chosen)
3. glider uses the forwarder in office.rule to ask 208.67.222.222:53 for the resolve answers.
4. glider updates it's office rule config, add the resolved ip address to it.
3. glider uses the forwarder in office.rule to ask 208.67.222.222:53 for the resolve answers(dns over proxy).
4. glider updates it's office rule config, adds the resolved ip address to it.
5. glider adds the resolved ip into ipset "glider", and return the dns answer to client.
Destination Accessing:

6
go.mod
View File

@ -9,9 +9,9 @@ require (
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
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-20200922070232-aee5d888a860 // indirect
golang.org/x/tools v0.0.0-20200923053713-ba800b16d873 // indirect
golang.org/x/net v0.0.0-20200923182212-328152dc79b1 // indirect
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d // indirect
golang.org/x/tools v0.0.0-20200923182640-463111b69878 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
)

12
go.sum
View File

@ -104,8 +104,8 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgN
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200923182212-328152dc79b1 h1:Iu68XRPd67wN4aRGGWwwq6bZo/25jR6uu52l/j2KkUE=
golang.org/x/net v0.0.0-20200923182212-328152dc79b1/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -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-20200922070232-aee5d888a860 h1:YEu4SMq7D0cmT7CBbXfcH0NZeuChAXwsHe/9XueUO6o=
golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d h1:L/IKR6COd7ubZrs2oTnTi73IhgqJ71c9s80WsQnh0Es=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/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-20200923053713-ba800b16d873 h1:Q5Sq7Lt0bkn6Ax1NAraQhKRN7xxxy1LV4guxsyFHZx4=
golang.org/x/tools v0.0.0-20200923053713-ba800b16d873/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20200923182640-463111b69878 h1:VUw1+Jf6KJPf82mbTQMia6HCnNMv2BbAipkEZ4KTcqQ=
golang.org/x/tools v0.0.0-20200923182640-463111b69878/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=

10
main.go
View File

@ -50,14 +50,14 @@ func main() {
}
// global rule proxy
p := rule.NewProxy(conf.Forward, &conf.StrategyConfig, conf.rules)
pxy := rule.NewProxy(conf.Forward, &conf.StrategyConfig, conf.rules)
// ipset manager
ipsetM, _ := ipset.NewManager(conf.rules)
// check and setup dns server
if conf.DNS != "" {
d, err := dns.NewServer(conf.DNS, p, &conf.DNSConfig)
d, err := dns.NewServer(conf.DNS, pxy, &conf.DNSConfig)
if err != nil {
log.Fatal(err)
}
@ -81,7 +81,7 @@ func main() {
}
// add a handler to update proxy rules when a domain resolved
d.AddHandler(p.AddDomainIP)
d.AddHandler(pxy.AddDomainIP)
if ipsetM != nil {
d.AddHandler(ipsetM.AddDomainIP)
}
@ -90,11 +90,11 @@ func main() {
}
// enable checkers
p.Check()
pxy.Check()
// Proxy Servers
for _, listen := range conf.Listen {
local, err := proxy.ServerFromURL(listen, p)
local, err := proxy.ServerFromURL(listen, pxy)
if err != nil {
log.Fatal(err)
}

View File

@ -8,7 +8,7 @@ import (
"github.com/nadoo/glider/common/log"
)
// Direct proxy
// Direct proxy.
type Direct struct {
iface *net.Interface // interface specified by user
ip net.IP
@ -16,7 +16,7 @@ type Direct struct {
relayTimeout time.Duration
}
// Default dialer
// Default dialer.
var Default = &Direct{dialTimeout: time.Second * 3}
// NewDirect returns a Direct dialer.

View File

@ -2,7 +2,7 @@ package proxy
import "net"
// Proxy is a dialer manager
// Proxy is a dialer manager.
type Proxy interface {
// Dial connects to the given address via the proxy.
Dial(network, addr string) (c net.Conn, dialer Dialer, err error)

View File

@ -9,7 +9,7 @@ import (
"github.com/nadoo/conflag"
)
// Config of rule dialer.
// Config is config of rule.
type Config struct {
Name string
@ -24,6 +24,19 @@ type Config struct {
CIDR []string
}
// StrategyConfig is config of strategy.
type StrategyConfig struct {
Strategy string
CheckWebSite string
CheckInterval int
CheckTimeout int
CheckDisabledOnly bool
MaxFailures int
DialTimeout int
RelayTimeout int
IntFace string
}
// NewConfFromFile returns a new config from file.
func NewConfFromFile(ruleFile string) (*Config, error) {
p := &Config{Name: ruleFile}

View File

@ -12,10 +12,10 @@ import (
"github.com/nadoo/glider/proxy"
)
// StatusHandler function will be called when the forwarder's status changed
// StatusHandler function will be called when the forwarder's status changed.
type StatusHandler func(*Forwarder)
// Forwarder is a forwarder
// Forwarder associates with a `-forward` command, usually a dialer or a chain of dialers.
type Forwarder struct {
proxy.Dialer
addr string

View File

@ -15,20 +15,7 @@ import (
"github.com/nadoo/glider/proxy"
)
// StrategyConfig is strategy config struct.
type StrategyConfig struct {
Strategy string
CheckWebSite string
CheckInterval int
CheckTimeout int
CheckDisabledOnly bool
MaxFailures int
DialTimeout int
RelayTimeout int
IntFace string
}
// forwarder slice orderd by priority
// forwarder slice orderd by priority.
type priSlice []*Forwarder
func (p priSlice) Len() int { return len(p) }
@ -69,7 +56,7 @@ func NewFwdrGroup(name string, s []string, c *StrategyConfig) *FwdrGroup {
return newFwdrGroup(name, fwdrs, c)
}
// newFwdrGroup returns a new Proxy.
// newFwdrGroup returns a new FwdrGroup.
func newFwdrGroup(name string, fwdrs []*Forwarder, c *StrategyConfig) *FwdrGroup {
p := &FwdrGroup{fwdrs: fwdrs, config: c}
sort.Sort(p.fwdrs)
@ -80,22 +67,28 @@ func newFwdrGroup(name string, fwdrs []*Forwarder, c *StrategyConfig) *FwdrGroup
p.config.CheckWebSite += ":80"
}
switch c.Strategy {
case "rr":
p.next = p.scheduleRR
log.F("[strategy] %s: forward in round robin mode.", name)
case "ha":
p.next = p.scheduleHA
log.F("[strategy] %s: forward in high availability mode.", name)
case "lha":
p.next = p.scheduleLHA
log.F("[strategy] %s: forward in latency based high availability mode.", name)
case "dh":
p.next = p.scheduleDH
log.F("[strategy] %s: forward in destination hashing mode.", name)
default:
p.next = p.scheduleRR
log.F("[strategy] %s: not supported forward mode '%s', use round robin mode.", name, c.Strategy)
// default scheduler
p.next = p.scheduleRR
// if there're more than 1 forwarders, we care about the strategy.
if count := len(fwdrs); count > 1 {
switch c.Strategy {
case "rr":
p.next = p.scheduleRR
log.F("[strategy] %s: %d forwarders forward in round robin mode.", name, count)
case "ha":
p.next = p.scheduleHA
log.F("[strategy] %s: %d forwarders forward in high availability mode.", name, count)
case "lha":
p.next = p.scheduleLHA
log.F("[strategy] %s: %d forwarders forward in latency based high availability mode.", name, count)
case "dh":
p.next = p.scheduleDH
log.F("[strategy] %s: %d forwarders forward in destination hashing mode.", name, count)
default:
p.next = p.scheduleRR
log.F("[strategy] %s: not supported forward mode '%s', use round robin mode for %d forwarders.", name, c.Strategy, count)
}
}
for _, f := range fwdrs {
@ -129,22 +122,6 @@ func (p *FwdrGroup) NextDialer(dstAddr string) proxy.Dialer {
return p.next(dstAddr)
}
// Record records result while using the dialer from proxy.
func (p *FwdrGroup) Record(dialer proxy.Dialer, success bool) {
OnRecord(dialer, success)
}
// OnRecord records result while using the dialer from proxy.
func OnRecord(dialer proxy.Dialer, success bool) {
if fwdr, ok := dialer.(*Forwarder); ok {
if !success {
fwdr.IncFailures()
} else {
fwdr.Enable()
}
}
}
// Priority returns the active priority of dialer.
func (p *FwdrGroup) Priority() uint32 { return atomic.LoadUint32(&p.priority) }
@ -170,7 +147,7 @@ func (p *FwdrGroup) init() {
if len(p.avail) == 0 {
// no available forwarders, set priority to 0 to check all forwarders in check func
p.SetPriority(0)
// log.F("[strategy] no available forwarders, please check your config file or network settings")
// log.F("[group] no available forwarders, please check your config file or network settings")
}
}
@ -180,14 +157,14 @@ func (p *FwdrGroup) onStatusChanged(fwdr *Forwarder) {
defer p.mu.Unlock()
if fwdr.Enabled() {
log.F("[strategy] %s changed status from Disabled to Enabled ", fwdr.Addr())
log.F("[group] %s(%d) changed status from DISABLED to ENABLED ", fwdr.Addr(), fwdr.Priority())
if fwdr.Priority() == p.Priority() {
p.avail = append(p.avail, fwdr)
} else if fwdr.Priority() > p.Priority() {
p.init()
}
} else {
log.F("[strategy] %s changed status from Enabled to Disabled", fwdr.Addr())
log.F("[group] %s(%d) changed status from ENABLED to DISABLED", fwdr.Addr(), fwdr.Priority())
for i, f := range p.avail {
if f == fwdr {
p.avail[i], p.avail = p.avail[len(p.avail)-1], p.avail[:len(p.avail)-1]
@ -250,7 +227,7 @@ func checkWebSite(fwdr *Forwarder, website string, timeout time.Duration, buf []
rc, err := fwdr.Dial("tcp", website)
if err != nil {
fwdr.Disable()
log.F("[check] %s(%d) -> %s, DISABLED. error in dial: %s", fwdr.Addr(), fwdr.Priority(),
log.F("[check] %s(%d) -> %s, FAILED. error in dial: %s", fwdr.Addr(), fwdr.Priority(),
website, err)
return false
}
@ -263,7 +240,7 @@ func checkWebSite(fwdr *Forwarder, website string, timeout time.Duration, buf []
_, err = io.WriteString(rc, "GET / HTTP/1.0\r\n\r\n")
if err != nil {
fwdr.Disable()
log.F("[check] %s(%d) -> %s, DISABLED. error in write: %s", fwdr.Addr(), fwdr.Priority(),
log.F("[check] %s(%d) -> %s, FAILED. error in write: %s", fwdr.Addr(), fwdr.Priority(),
website, err)
return false
}
@ -271,14 +248,14 @@ func checkWebSite(fwdr *Forwarder, website string, timeout time.Duration, buf []
_, err = io.ReadFull(rc, buf)
if err != nil {
fwdr.Disable()
log.F("[check] %s(%d) -> %s, DISABLED. error in read: %s", fwdr.Addr(), fwdr.Priority(),
log.F("[check] %s(%d) -> %s, FAILED. error in read: %s", fwdr.Addr(), fwdr.Priority(),
website, err)
return false
}
if !bytes.Equal([]byte("HTTP"), buf) {
fwdr.Disable()
log.F("[check] %s(%d) -> %s, DISABLED. server response: %s", fwdr.Addr(), fwdr.Priority(),
log.F("[check] %s(%d) -> %s, FAILED. server response: %s", fwdr.Addr(), fwdr.Priority(),
website, buf)
return false
}
@ -288,29 +265,29 @@ func checkWebSite(fwdr *Forwarder, website string, timeout time.Duration, buf []
if readTime > timeout {
fwdr.Disable()
log.F("[check] %s(%d) -> %s, DISABLED. check timeout: %s", fwdr.Addr(), fwdr.Priority(),
log.F("[check] %s(%d) -> %s, FAILED. check timeout: %s", fwdr.Addr(), fwdr.Priority(),
website, readTime)
return false
}
fwdr.Enable()
log.F("[check] %s(%d) -> %s, ENABLED. connect time: %s", fwdr.Addr(), fwdr.Priority(),
log.F("[check] %s(%d) -> %s, SUCCEEDED. connect time: %s", fwdr.Addr(), fwdr.Priority(),
website, readTime)
return true
}
// Round Robin
// Round Robin.
func (p *FwdrGroup) scheduleRR(dstAddr string) *Forwarder {
return p.avail[atomic.AddUint32(&p.index, 1)%uint32(len(p.avail))]
}
// High Availability
// High Availability.
func (p *FwdrGroup) scheduleHA(dstAddr string) *Forwarder {
return p.avail[0]
}
// Latency based High Availability
// Latency based High Availability.
func (p *FwdrGroup) scheduleLHA(dstAddr string) *Forwarder {
fwdr := p.avail[0]
lowest := fwdr.Latency()
@ -323,7 +300,7 @@ func (p *FwdrGroup) scheduleLHA(dstAddr string) *Forwarder {
return fwdr
}
// Destination Hashing
// Destination Hashing.
func (p *FwdrGroup) scheduleDH(dstAddr string) *Forwarder {
fnv1a := fnv.New32a()
fnv1a.Write([]byte(dstAddr))

View File

@ -9,7 +9,7 @@ import (
"github.com/nadoo/glider/proxy"
)
// Proxy struct.
// Proxy implements the proxy.Proxy interface with rule support.
type Proxy struct {
main *FwdrGroup
all []*FwdrGroup
@ -41,11 +41,14 @@ func NewProxy(mainForwarders []string, mainStrategy *StrategyConfig, rules []*Co
}
}
// if there's any forwarder defined in main config, make sure they will be accessed directly.
if len(mainForwarders) > 0 {
direct := NewFwdrGroup("backup", nil, mainStrategy)
direct := NewFwdrGroup("", 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)
host := strings.Split(f.addr, ":")[0]
if ip := net.ParseIP(host); ip == nil {
rd.domainMap.Store(strings.ToLower(host), direct)
}
}
}
@ -54,20 +57,18 @@ func NewProxy(mainForwarders []string, mainStrategy *StrategyConfig, rules []*Co
// Dial dials to targer addr and return a conn.
func (p *Proxy) Dial(network, addr string) (net.Conn, proxy.Dialer, error) {
return p.chooseProxy(addr).Dial(network, addr)
return p.findDialer(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.chooseProxy(addr).DialUDP(network, addr)
return p.findDialer(addr).DialUDP(network, addr)
}
// chooseProxy returns a proxy according to rule.
func (p *Proxy) chooseProxy(dstAddr string) *FwdrGroup {
// findDialer returns a dialer by dstAddr according to rule.
func (p *Proxy) findDialer(dstAddr string) *FwdrGroup {
host, _, err := net.SplitHostPort(dstAddr)
if err != nil {
// TODO: check here
// logf("[rule] SplitHostPort ERROR: %s", err)
return p.main
}
@ -86,7 +87,6 @@ func (p *Proxy) chooseProxy(dstAddr string) *FwdrGroup {
ret = value.(*FwdrGroup)
return false
}
return true
})
@ -109,12 +109,18 @@ func (p *Proxy) chooseProxy(dstAddr string) *FwdrGroup {
// NextDialer returns next dialer according to rule.
func (p *Proxy) NextDialer(dstAddr string) proxy.Dialer {
return p.chooseProxy(dstAddr).NextDialer(dstAddr)
return p.findDialer(dstAddr).NextDialer(dstAddr)
}
// Record records result while using the dialer from proxy.
func (p *Proxy) Record(dialer proxy.Dialer, success bool) {
OnRecord(dialer, success)
if fwdr, ok := dialer.(*Forwarder); ok {
if !success {
fwdr.IncFailures()
return
}
fwdr.Enable()
}
}
// AddDomainIP used to update ipMap rules according to domainMap rule.
@ -125,18 +131,19 @@ func (p *Proxy) AddDomainIP(domain, ip string) error {
i = strings.LastIndexByte(domain[:i], '.')
if dialer, ok := p.domainMap.Load(domain[i+1:]); ok {
p.ipMap.Store(ip, dialer)
log.F("[rule] add ip=%s, based on rule: domain=%s & domain/ip: %s/%s\n", ip, domain[i+1:], domain, ip)
log.F("[rule] add ip=%s, based on rule: domain=%s & domain/ip: %s/%s\n",
ip, domain[i+1:], domain, ip)
}
}
}
return nil
}
// Check .
// Check checks availability of forwarders inside proxy.
func (p *Proxy) Check() {
p.main.Check()
for _, fwdr := range p.all {
fwdr.Check()
for _, fwdrGroup := range p.all {
fwdrGroup.Check()
}
}