glider/rule/rule.go

143 lines
3.1 KiB
Go

package rule
import (
"net"
"strings"
"sync"
"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
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}
for _, r := range rules {
sd := strategy.NewProxy(r.Forward, &r.StrategyConfig)
rd.proxies = append(rd.proxies, sd)
for _, domain := range r.Domain {
rd.domainMap.Store(strings.ToLower(domain), sd)
}
for _, ip := range r.IP {
rd.ipMap.Store(ip, sd)
}
for _, s := range r.CIDR {
if _, cidr, err := net.ParseCIDR(s); err == nil {
rd.cidrMap.Store(cidr, sd)
}
}
}
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)
}
// 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)
}
// nextProxy return next proxy according to rule
func (p *Proxy) nextProxy(dstAddr string) *strategy.Proxy {
host, _, err := net.SplitHostPort(dstAddr)
if err != nil {
// TODO: check here
// logf("[rule] SplitHostPort ERROR: %s", err)
return p.proxy
}
// find ip
if ip := net.ParseIP(host); ip != nil {
// check ip
if proxy, ok := p.ipMap.Load(ip.String()); ok {
return proxy.(*strategy.Proxy)
}
var ret *strategy.Proxy
// check cidr
p.cidrMap.Range(func(key, value interface{}) bool {
cidr := key.(*net.IPNet)
if cidr.Contains(ip) {
ret = value.(*strategy.Proxy)
return false
}
return true
})
if ret != nil {
return ret
}
}
domainParts := strings.Split(host, ".")
length := len(domainParts)
for i := length - 1; i >= 0; i-- {
domain := strings.Join(domainParts[i:length], ".")
// find in domainMap
if proxy, ok := p.domainMap.Load(domain); ok {
return proxy.(*strategy.Proxy)
}
}
return p.proxy
}
// NextDialer return next dialer according to rule
func (p *Proxy) NextDialer(dstAddr string) proxy.Dialer {
return p.nextProxy(dstAddr).NextDialer(dstAddr)
}
// Record records result while using the dialer from proxy.
func (p *Proxy) Record(dialer proxy.Dialer, success bool) {
p.proxy.Record(dialer, success)
}
// AddDomainIP used to update ipMap rules according to domainMap rule
func (p *Proxy) AddDomainIP(domain, ip string) error {
if ip != "" {
domainParts := strings.Split(domain, ".")
length := len(domainParts)
for i := length - 1; i >= 0; i-- {
pDomain := strings.ToLower(strings.Join(domainParts[i:length], "."))
// find in domainMap
if dialer, ok := p.domainMap.Load(pDomain); ok {
p.ipMap.Store(ip, dialer)
log.F("[rule] add ip=%s, based on rule: domain=%s & domain/ip: %s/%s\n", ip, pDomain, domain, ip)
}
}
}
return nil
}
// Check .
func (p *Proxy) Check() {
p.proxy.Check()
for _, d := range p.proxies {
d.Check()
}
}