glider/rule/proxy.go

157 lines
3.7 KiB
Go
Raw Permalink Normal View History

2018-08-12 22:24:49 +08:00
package rule
2017-07-30 01:54:19 +08:00
import (
"net"
2022-01-28 23:35:29 +08:00
"net/netip"
2017-07-30 01:54:19 +08:00
"strings"
"sync"
2022-01-28 23:35:29 +08:00
"github.com/nadoo/glider/pkg/log"
"github.com/nadoo/glider/proxy"
2017-07-30 01:54:19 +08:00
)
2020-09-24 18:50:04 +08:00
// Proxy implements the proxy.Proxy interface with rule support.
type Proxy struct {
main *FwdrGroup
all []*FwdrGroup
domainMap sync.Map
ipMap sync.Map
cidrMap sync.Map
}
// NewProxy returns a new rule proxy.
func NewProxy(mainForwarders []string, mainStrategy *Strategy, rules []*Config) *Proxy {
rd := &Proxy{main: NewFwdrGroup("main", mainForwarders, mainStrategy)}
for _, r := range rules {
2021-12-22 21:20:29 +08:00
group := NewFwdrGroup(r.RulePath, r.Forward, &r.Strategy)
rd.all = append(rd.all, group)
for _, domain := range r.Domain {
rd.domainMap.Store(strings.ToLower(domain), group)
}
for _, s := range r.IP {
ip, err := netip.ParseAddr(s)
if err != nil {
log.F("[rule] parse ip error: %s", err)
continue
}
rd.ipMap.Store(ip, group)
}
2017-07-30 01:54:19 +08:00
2017-08-23 21:11:08 +08:00
for _, s := range r.CIDR {
2022-01-28 23:35:29 +08:00
cidr, err := netip.ParsePrefix(s)
if err != nil {
log.F("[rule] parse cidr error: %s", err)
continue
2017-08-23 21:11:08 +08:00
}
2022-01-28 23:35:29 +08:00
rd.cidrMap.Store(cidr, group)
}
}
2017-07-30 01:54:19 +08:00
2021-07-02 19:09:01 +08:00
direct := NewFwdrGroup("", nil, mainStrategy)
rd.domainMap.Store("direct", direct)
2020-09-24 18:50:04 +08:00
// if there's any forwarder defined in main config, make sure they will be accessed directly.
if len(mainForwarders) > 0 {
for _, f := range rd.main.fwdrs {
addr := strings.Split(f.addr, ",")[0]
host, _, _ := net.SplitHostPort(addr)
2022-01-28 23:35:29 +08:00
if _, err := netip.ParseAddr(host); err != nil {
2020-09-24 18:50:04 +08:00
rd.domainMap.Store(strings.ToLower(host), direct)
}
}
}
return rd
}
2017-07-30 01:54:19 +08:00
// Dial dials to targer addr and return a conn.
func (p *Proxy) Dial(network, addr string) (net.Conn, proxy.Dialer, error) {
2020-09-24 18:50:04 +08:00
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, dialer proxy.UDPDialer, err error) {
2020-09-24 18:50:04 +08:00
return p.findDialer(addr).DialUDP(network, addr)
}
2020-09-24 18:50:04 +08:00
// findDialer returns a dialer by dstAddr according to rule.
func (p *Proxy) findDialer(dstAddr string) *FwdrGroup {
host, _, err := net.SplitHostPort(dstAddr)
2017-07-30 01:54:19 +08:00
if err != nil {
return p.main
2017-07-30 01:54:19 +08:00
}
if ip, err := netip.ParseAddr(host); err == nil {
// check ip
if proxy, ok := p.ipMap.Load(ip); ok {
return proxy.(*FwdrGroup)
}
// check cidr
var ret *FwdrGroup
2022-01-26 23:40:49 +08:00
p.cidrMap.Range(func(key, value any) bool {
2022-01-28 23:35:29 +08:00
if key.(netip.Prefix).Contains(ip) {
ret = value.(*FwdrGroup)
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
}
}
// check host
host = strings.ToLower(host)
for i := len(host); i != -1; {
i = strings.LastIndexByte(host[:i], '.')
if proxy, ok := p.domainMap.Load(host[i+1:]); ok {
return proxy.(*FwdrGroup)
}
}
return p.main
}
2017-07-30 01:54:19 +08:00
// NextDialer returns next dialer according to rule.
func (p *Proxy) NextDialer(dstAddr string) proxy.Dialer {
2020-09-24 18:50:04 +08:00
return p.findDialer(dstAddr).NextDialer(dstAddr)
2018-01-17 00:26:38 +08:00
}
// Record records result while using the dialer from proxy.
func (p *Proxy) Record(dialer proxy.Dialer, success bool) {
2020-09-24 18:50:04 +08:00
if fwdr, ok := dialer.(*Forwarder); ok {
if !success {
fwdr.IncFailures()
return
}
fwdr.Enable()
}
}
// AddDomainIP used to update ipMap rules according to domainMap rule.
func (p *Proxy) AddDomainIP(domain string, ip netip.Addr) error {
domain = strings.ToLower(domain)
for i := len(domain); i != -1; {
i = strings.LastIndexByte(domain[:i], '.')
if dialer, ok := p.domainMap.Load(domain[i+1:]); ok {
p.ipMap.Store(ip, dialer)
// log.F("[rule] update map: %s/%s based on rule: domain=%s\n", domain, ip, domain[i+1:])
}
}
return nil
}
2020-09-24 18:50:04 +08:00
// Check checks availability of forwarders inside proxy.
func (p *Proxy) Check() {
p.main.Check()
2020-09-24 18:50:04 +08:00
for _, fwdrGroup := range p.all {
fwdrGroup.Check()
}
}