glider/rule/rule.go

144 lines
3.2 KiB
Go
Raw Normal View History

2018-08-12 22:24:49 +08:00
package rule
2017-07-30 01:54:19 +08:00
import (
"net"
2017-07-30 01:54:19 +08:00
"strings"
"sync"
"github.com/nadoo/glider/common/log"
"github.com/nadoo/glider/proxy"
2018-08-11 11:46:10 +08:00
"github.com/nadoo/glider/strategy"
2017-07-30 01:54:19 +08:00
)
2018-08-12 22:24:49 +08:00
// Dialer struct
type Dialer struct {
gDialer proxy.Dialer
dialers []proxy.Dialer
2017-07-30 01:54:19 +08:00
domainMap sync.Map
ipMap sync.Map
cidrMap sync.Map
}
2018-08-12 22:24:49 +08:00
// NewDialer returns a new rule dialer
func NewDialer(rules []*Config, gDialer proxy.Dialer) *Dialer {
rd := &Dialer{gDialer: gDialer}
for _, r := range rules {
sd := strategy.NewDialer(r.Forward, &r.StrategyConfig)
rd.dialers = append(rd.dialers, sd)
for _, domain := range r.Domain {
rd.domainMap.Store(strings.ToLower(domain), sd)
}
for _, ip := range r.IP {
rd.ipMap.Store(ip, sd)
}
2017-07-30 01:54:19 +08:00
2017-08-23 21:11:08 +08:00
for _, s := range r.CIDR {
if _, cidr, err := net.ParseCIDR(s); err == nil {
rd.cidrMap.Store(cidr, sd)
2017-08-23 21:11:08 +08:00
}
}
}
2017-07-30 01:54:19 +08:00
return rd
}
2017-07-30 01:54:19 +08:00
// Addr returns RuleDialer's address, always be "RULES"
2018-08-12 22:24:49 +08:00
func (rd *Dialer) Addr() string { return "RULE DIALER, DEFAULT: " + rd.gDialer.Addr() }
// NextDialer return next dialer according to rule
2018-08-12 22:24:49 +08:00
func (rd *Dialer) NextDialer(dstAddr string) proxy.Dialer {
host, _, err := net.SplitHostPort(dstAddr)
2017-07-30 01:54:19 +08:00
if err != nil {
// TODO: check here
2018-06-28 23:20:04 +08:00
// logf("[rule] SplitHostPort ERROR: %s", err)
2017-09-21 23:07:04 +08:00
return rd.gDialer
2017-07-30 01:54:19 +08:00
}
// find ip
if ip := net.ParseIP(host); ip != nil {
// check ip
2018-01-06 14:44:58 +08:00
if dialer, ok := rd.ipMap.Load(ip.String()); ok {
return dialer.(proxy.Dialer)
}
var ret proxy.Dialer
// check cidr
2017-09-21 23:07:04 +08:00
rd.cidrMap.Range(func(key, value interface{}) bool {
2017-08-23 21:11:08 +08:00
cidr := key.(*net.IPNet)
if cidr.Contains(ip) {
ret = value.(proxy.Dialer)
2017-08-23 21:11:08 +08:00
return false
2017-07-30 01:54:19 +08:00
}
return true
})
if ret != nil {
return ret
2017-07-30 01:54:19 +08:00
}
2017-07-30 01:54:19 +08:00
}
domainParts := strings.Split(host, ".")
length := len(domainParts)
for i := length - 1; i >= 0; i-- {
domain := strings.Join(domainParts[i:length], ".")
2017-07-30 01:54:19 +08:00
// find in domainMap
2018-01-06 14:44:58 +08:00
if dialer, ok := rd.domainMap.Load(domain); ok {
return dialer.(proxy.Dialer)
}
2017-07-30 01:54:19 +08:00
}
2017-09-21 23:07:04 +08:00
return rd.gDialer
}
2017-07-30 01:54:19 +08:00
// Dial dials to targer addr and return a conn
2018-08-12 22:24:49 +08:00
func (rd *Dialer) Dial(network, addr string) (net.Conn, error) {
d := rd.NextDialer(addr)
log.F("[Dial] %s => %s", addr, d.Addr())
return d.Dial(network, addr)
2017-07-30 01:54:19 +08:00
}
2018-01-21 00:31:10 +08:00
// DialUDP connects to the given address via the proxy
2018-08-12 22:24:49 +08:00
func (rd *Dialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
d := rd.NextDialer(addr)
log.F("[DialUDP] %s => %s", addr, d.Addr())
return d.DialUDP(network, addr)
2018-01-17 00:26:38 +08:00
}
// AddDomainIP used to update ipMap rules according to domainMap rule
2018-08-12 22:24:49 +08:00
func (rd *Dialer) AddDomainIP(domain, ip string) error {
if ip != "" {
domainParts := strings.Split(domain, ".")
length := len(domainParts)
for i := length - 1; i >= 0; i-- {
2018-01-30 23:11:04 +08:00
pDomain := strings.ToLower(strings.Join(domainParts[i:length], "."))
// find in domainMap
2018-01-06 14:44:58 +08:00
if dialer, ok := rd.domainMap.Load(pDomain); ok {
rd.ipMap.Store(ip, dialer)
2018-07-08 23:22:01 +08:00
log.F("[rule] add ip=%s, based on rule: domain=%s & domain/ip: %s/%s\n", ip, pDomain, domain, ip)
}
}
}
return nil
}
// Check .
func (rd *Dialer) Check() {
if checker, ok := rd.gDialer.(strategy.Checker); ok {
checker.Check()
}
for _, d := range rd.dialers {
if checker, ok := d.(strategy.Checker); ok {
checker.Check()
}
}
}