add proxy rule file support.

This commit is contained in:
nadoo 2017-07-30 01:54:19 +08:00
parent bf692de636
commit 4dd429b754
9 changed files with 254 additions and 18 deletions

1
.gitignore vendored
View File

@ -16,5 +16,6 @@
# custom
*.zip
*.conf
*.rule
glider
doc/

View File

@ -4,16 +4,19 @@ import "net"
// direct proxy
type direct struct {
Proxy
}
// Direct proxy
var Direct = &direct{Proxy: &proxy{addr: "127.0.0.1"}}
var Direct = &direct{}
// Direct proxy always enabled
func (d *direct) Enabled() bool {
return true
}
func (d *direct) Addr() string { return "127.0.0.1" }
func (d *direct) ListenAndServe() { logf("base proxy ListenAndServe") }
func (d *direct) Serve(c net.Conn) { logf("base proxy Serve") }
func (d *direct) CurrentProxy() Proxy { return d }
func (d *direct) GetProxy(dstAddr string) Proxy { return d }
func (d *direct) NextProxy() Proxy { return d }
func (d *direct) Enabled() bool { return true }
func (d *direct) SetEnable(enable bool) {}
func (d *direct) Dial(network, addr string) (net.Conn, error) {
c, err := net.Dial(network, addr)

View File

@ -94,3 +94,10 @@ checkwebsite=www.apple.com:443
# check duration(seconds)
checkduration=30
# RULE FILES
# ----------
# Specify additional forward rules
#rulefile=office.rule
#rulefile=home.rule

19
main.go
View File

@ -21,6 +21,7 @@ var conf struct {
CheckDuration int
Listen arrFlags
Forward arrFlags
RuleFile arrFlags
}
var flag = conflag.New()
@ -125,6 +126,7 @@ func main() {
flag.IntVar(&conf.CheckDuration, "checkduration", 30, "proxy check duration(seconds)")
flag.Var(&conf.Listen, "listen", "listen url, format: SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT")
flag.Var(&conf.Forward, "forward", "forward url, format: SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT[,SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT]")
flag.Var(&conf.RuleFile, "rulefile", "rule file path")
flag.Usage = usage
err := flag.Parse()
@ -139,6 +141,7 @@ func main() {
return
}
// global forwarders in xx.conf
var forwarders []Proxy
for _, chain := range conf.Forward {
var forward Proxy
@ -152,7 +155,23 @@ func main() {
forwarders = append(forwarders, forward)
}
// combine forwarders to a singer strategy forwarder
forwarder := newStrategyForwarder(conf.Strategy, forwarders)
// rule forwarders
var ruleForwarders []*ruleForwarder
for _, ruleFile := range conf.RuleFile {
ruleForwarder, err := newRuleProxyFromFile(ruleFile)
if err != nil {
log.Fatal(err)
}
ruleForwarders = append(ruleForwarders, ruleForwarder)
}
// combine ruleforwarders and global strategy forwarder
forwarder = newRulesForwarder(ruleForwarders, forwarder)
for _, listen := range conf.Listen {
local, err := ProxyFromURL(listen, forwarder)
if err != nil {

29
office.rule.example Normal file
View File

@ -0,0 +1,29 @@
# Glider rule configuration file.
#
# Format is the same as glider main config file.
# EXCEPTION: Listeners are NOT allowed to setup here.
# FORWARDERS
# ----------
# Forwarders, we can setup multiple forwarders.
forward=socks5://192.168.1.10:1080
forward=ss://method:pass@1.1.1.1:443
forward=http://192.168.2.1:8080,socks5://192.168.2.2:1080
strategy=rr
checkwebsite=www.apple.com:443
checkduration=30
# DESTINATIONS
# ------------
# ALL destinations matches the following rules will be forward using forwarders specified above
# matches abc.com and *.abc.com
domain=abc.com
# matches 1.1.1.1
ip=1.1.1.1
# matches 192.168.100.0/24
cidr=192.168.100.0/24

View File

@ -11,15 +11,15 @@ import (
// A Proxy means to establish a connection and relay it.
type Proxy interface {
// Get address
Addr() string
// ListenAndServe as proxy server, use only in server mode.
ListenAndServe()
// Serve as proxy server, use only in server mode.
Serve(c net.Conn)
// Get address
Addr() string
// Get current proxy
CurrentProxy() Proxy
@ -55,6 +55,7 @@ func newProxy(addr string, forward Proxy) *proxy {
return &proxy{addr: addr, forward: forward, enabled: true}
}
func (p *proxy) Addr() string { return p.addr }
func (p *proxy) ListenAndServe() { logf("base proxy ListenAndServe") }
func (p *proxy) Serve(c net.Conn) { logf("base proxy Serve") }
func (p *proxy) CurrentProxy() Proxy { return p.forward }
@ -62,7 +63,6 @@ func (p *proxy) GetProxy(dstAddr string) Proxy { return p.forward }
func (p *proxy) NextProxy() Proxy { return p.forward }
func (p *proxy) Enabled() bool { return p.enabled }
func (p *proxy) SetEnable(enable bool) { p.enabled = enable }
func (p *proxy) Addr() string { return p.addr }
func (p *proxy) Dial(network, addr string) (net.Conn, error) {
return p.forward.Dial(network, addr)

91
rule.go Normal file
View File

@ -0,0 +1,91 @@
package main
import (
"fmt"
"log"
"net"
"os"
"strings"
"github.com/nadoo/conflag"
)
// ruleForwarder, every ruleForwarder points to a rule file
type ruleForwarder struct {
Forward arrFlags
Strategy string
CheckWebSite string
CheckDuration int
Domain arrFlags
IP arrFlags
CIDR arrFlags
name string
sForwarder Proxy
}
// newRuleProxyFromFile .
func newRuleProxyFromFile(ruleFile string) (*ruleForwarder, error) {
p := &ruleForwarder{name: ruleFile}
f := conflag.NewFromFile("rule", ruleFile)
f.Var(&p.Forward, "forward", "forward url, format: SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT[,SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT]")
f.StringVar(&p.Strategy, "strategy", "rr", "forward strategy, default: rr")
f.StringVar(&p.CheckWebSite, "checkwebsite", "www.apple.com:443", "proxy check website address")
f.IntVar(&p.CheckDuration, "checkduration", 30, "proxy check duration(seconds)")
f.Var(&p.Domain, "domain", "domain")
f.Var(&p.IP, "ip", "ip")
f.Var(&p.CIDR, "cidr", "cidr")
err := f.Parse()
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
return nil, err
}
var forwarders []Proxy
for _, chain := range p.Forward {
var forward Proxy
var err error
for _, url := range strings.Split(chain, ",") {
forward, err = ProxyFromURL(url, forward)
if err != nil {
log.Fatal(err)
}
}
forwarders = append(forwarders, forward)
}
forwarder := newStrategyForwarder(p.Strategy, forwarders)
for _, forward := range forwarders {
go check(forward, p.CheckWebSite, p.CheckDuration)
}
p.sForwarder = forwarder
return p, err
}
func (p *ruleForwarder) Addr() string { return "rule forwarder" }
func (p *ruleForwarder) ListenAndServe() {}
func (p *ruleForwarder) Serve(c net.Conn) {}
func (p *ruleForwarder) CurrentProxy() Proxy { return p.sForwarder.CurrentProxy() }
func (p *ruleForwarder) GetProxy(dstAddr string) Proxy {
return p.sForwarder.NextProxy()
}
func (p *ruleForwarder) NextProxy() Proxy {
return p.sForwarder.NextProxy()
}
func (p *ruleForwarder) Enabled() bool { return true }
func (p *ruleForwarder) SetEnable(enable bool) {}
func (p *ruleForwarder) Dial(network, addr string) (net.Conn, error) {
return p.NextProxy().Dial(network, addr)
}

89
rules.go Normal file
View File

@ -0,0 +1,89 @@
package main
import (
"net"
"strings"
)
type rulesForwarder struct {
globalForwarder Proxy
domainMap map[string]Proxy
ipMap map[string]Proxy
cidrMap map[string]Proxy
}
// newRulesForwarder .
func newRulesForwarder(ruleForwarders []*ruleForwarder, globalForwarder Proxy) Proxy {
p := &rulesForwarder{globalForwarder: globalForwarder}
for _, f := range ruleForwarders {
p.domainMap = make(map[string]Proxy)
for _, domain := range f.Domain {
p.domainMap[domain] = f.sForwarder
}
p.ipMap = make(map[string]Proxy)
for _, ip := range f.IP {
p.ipMap[ip] = f.sForwarder
}
p.cidrMap = make(map[string]Proxy)
for _, cidr := range f.CIDR {
p.cidrMap[cidr] = f.sForwarder
}
}
return p
}
func (p *rulesForwarder) Addr() string { return "rule forwarder" }
func (p *rulesForwarder) ListenAndServe() {}
func (p *rulesForwarder) Serve(c net.Conn) {}
func (p *rulesForwarder) CurrentProxy() Proxy { return p.globalForwarder.CurrentProxy() }
func (p *rulesForwarder) GetProxy(dstAddr string) Proxy {
logf("dstAddr: %s", dstAddr)
host, _, err := net.SplitHostPort(dstAddr)
if err != nil {
// TODO: check here
logf("%s", err)
return p.globalForwarder.GetProxy(dstAddr)
}
// find ip
if ip := net.ParseIP(host); ip != nil {
// check cidr
// check ip
if p, ok := p.ipMap[ip.String()]; ok {
return p
}
}
domainParts := strings.Split(host, ".")
length := len(domainParts)
for i := length - 2; i >= 0; i-- {
domain := strings.Join(domainParts[i:length], ".")
// find in domainMap
if p, ok := p.domainMap[domain]; ok {
return p
}
}
return p.globalForwarder.GetProxy(dstAddr)
}
func (p *rulesForwarder) NextProxy() Proxy {
return p.globalForwarder.NextProxy()
}
func (p *rulesForwarder) Enabled() bool { return true }
func (p *rulesForwarder) SetEnable(enable bool) {}
func (p *rulesForwarder) Dial(network, addr string) (net.Conn, error) {
return p.GetProxy(addr).Dial(network, addr)
}

View File

@ -1,9 +1,6 @@
package main
import (
"net"
"time"
)
import "net"
// newStrategyForwarder .
func newStrategyForwarder(strategy string, forwarders []Proxy) Proxy {
@ -46,6 +43,7 @@ func newRRProxy(addr string, forwarders []Proxy) Proxy {
return &rrProxy{forwarders: forwarders}
}
func (p *rrProxy) Addr() string { return "strategy forwarder" }
func (p *rrProxy) ListenAndServe() {}
func (p *rrProxy) Serve(c net.Conn) {}
func (p *rrProxy) CurrentProxy() Proxy { return p.forwarders[p.idx] }
@ -73,10 +71,9 @@ func (p *rrProxy) NextProxy() Proxy {
return p.forwarders[p.idx]
}
func (p *rrProxy) Enabled() bool { return true }
func (p *rrProxy) SetEnable(enable bool) {}
func (p *rrProxy) Check(proxy Proxy, target string, duration time.Duration) {}
func (p *rrProxy) Addr() string { return "" }
func (p *rrProxy) Enabled() bool { return true }
func (p *rrProxy) SetEnable(enable bool) {}
func (p *rrProxy) Dial(network, addr string) (net.Conn, error) {
return p.NextProxy().Dial(network, addr)
}