Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support routingA #202

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ type Config struct {
Forwards []string
Strategy rule.Strategy

RuleFiles []string
RulesDir string
RuleFiles []string
RulesDir string
RoutingAFile string

DNS string
DNSConfig dns.Config
Expand Down Expand Up @@ -56,6 +57,7 @@ func parseConfig() *Config {

flag.StringSliceUniqVar(&conf.RuleFiles, "rulefile", nil, "rule file path")
flag.StringVar(&conf.RulesDir, "rules-dir", "", "rule file folder")
flag.StringVar(&conf.RoutingAFile, "routingA", "", "routingA file path")

// dns configs
flag.StringVar(&conf.DNS, "dns", "", "local dns server listen address")
Expand Down Expand Up @@ -118,6 +120,16 @@ func parseConfig() *Config {
}
}

if conf.RoutingAFile != "" {
if !path.IsAbs(conf.RoutingAFile) {
conf.RoutingAFile = path.Join(flag.ConfDir(), conf.RoutingAFile)
}
conf.Strategy.RoutingA, err = rule.ParseRoutingA(conf.RoutingAFile)
if err != nil {
log.Fatal(err)
}
}

return conf
}

Expand Down
2 changes: 1 addition & 1 deletion dns/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func (c *Client) exchange(qname string, reqBytes []byte, preferTCP bool) (

// use tcp to connect upstream server default
network = "tcp"
dialer := c.proxy.NextDialer(qname + ":53")
dialer := c.proxy.NextDialer(network, qname+":53")

// if we are resolving the dialer's domain, then use Direct to avoid denpency loop
// TODO: dialer.Addr() == "REJECT", tricky
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/nadoo/conflag v0.2.3
github.com/nadoo/ipset v0.3.0
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/v2rayA/routingA v0.0.0-20201204065601-aef348ea7aa1
github.com/xtaci/kcp-go/v5 v5.6.1
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392
golang.org/x/mod v0.4.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ github.com/dgryski/go-rc2 v0.0.0-20150621095337-8a9021637152/go.mod h1:I9fhc/EvS
github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521 h1:fBHFH+Y/GPGFGo7LIrErQc3p2MeAhoIQNgaxPWYsSxk=
github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:ucvhdsUCE3TH0LoLRb6ShHiJl8e39dGlx6A4g/ujlow=
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
github.com/gocarina/gocsv v0.0.0-20201103164230-b291445e0dd2 h1:DTpqi8htDqlk4dGMxZ3+7BVX2OoMki9akiCHWQpSXfA=
github.com/gocarina/gocsv v0.0.0-20201103164230-b291445e0dd2/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
Expand Down Expand Up @@ -73,6 +75,8 @@ github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/u-root/u-root v7.0.0+incompatible h1:u+KSS04pSxJGI5E7WE4Bs9+Zd75QjFv+REkjy/aoAc8=
github.com/u-root/u-root v7.0.0+incompatible/go.mod h1:RYkpo8pTHrNjW08opNd/U6p/RJE7K0D8fXO0d47+3YY=
github.com/v2rayA/routingA v0.0.0-20201204065601-aef348ea7aa1 h1:PITT+h0KRoBQwifYJr9vLqkhPH0UyJTonP+sIa5yGa4=
github.com/v2rayA/routingA v0.0.0-20201204065601-aef348ea7aa1/go.mod h1:V86dy1jY9VJKMrEinUjUQTclZaQQv+nIXBuFCSf67A4=
github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI=
github.com/xtaci/kcp-go/v5 v5.6.1/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
Expand Down
2 changes: 1 addition & 1 deletion proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type Proxy interface {
DialUDP(network, addr string) (pc net.PacketConn, dialer UDPDialer, writeTo net.Addr, err error)

// Get the dialer by dstAddr.
NextDialer(dstAddr string) Dialer
NextDialer(network, dstAddr string) Dialer

// Record records result while using the dialer from proxy.
Record(dialer Dialer, success bool)
Expand Down
2 changes: 1 addition & 1 deletion proxy/ss/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (s *SS) Serve(c net.Conn) {
}

network := "tcp"
dialer := s.proxy.NextDialer(tgt.String())
dialer := s.proxy.NextDialer(network, tgt.String())

rc, err := dialer.Dial(network, tgt.String())
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions proxy/trojan/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func (s *Trojan) Serve(c net.Conn) {
}

network := "tcp"
dialer := s.proxy.NextDialer(target.String())
dialer := s.proxy.NextDialer(network, target.String())

if cmd == socks.CmdUDPAssociate {
// there is no upstream proxy, just serve it
Expand Down Expand Up @@ -137,7 +137,7 @@ func (s *Trojan) Serve(c net.Conn) {

func (s *Trojan) serveFallback(c net.Conn, tgt string, headBuf *bytes.Buffer) {
// TODO: should we access fallback directly or via proxy?
dialer := s.proxy.NextDialer(tgt)
dialer := s.proxy.NextDialer("tcp", tgt)
rc, err := dialer.Dial("tcp", tgt)
if err != nil {
log.F("[trojan-fallback] %s <-> %s via %s, error in dial: %v", c.RemoteAddr(), tgt, dialer.Addr(), err)
Expand Down
4 changes: 2 additions & 2 deletions proxy/vless/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (s *VLess) Serve(c net.Conn) {
c = NewServerConn(c)

network := "tcp"
dialer := s.proxy.NextDialer(target)
dialer := s.proxy.NextDialer(network, target)

if cmd == CmdUDP {
// there is no upstream proxy, just serve it
Expand Down Expand Up @@ -94,7 +94,7 @@ func (s *VLess) Serve(c net.Conn) {

func (s *VLess) serveFallback(c net.Conn, tgt string, headBuf *bytes.Buffer) {
// TODO: should we access fallback directly or via proxy?
dialer := s.proxy.NextDialer(tgt)
dialer := s.proxy.NextDialer("tcp", tgt)
rc, err := dialer.Dial("tcp", tgt)
if err != nil {
log.F("[vless-fallback] %s <-> %s via %s, error in dial: %v", c.RemoteAddr(), tgt, dialer.Addr(), err)
Expand Down
22 changes: 20 additions & 2 deletions rule/config.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package rule

import (
"github.com/v2rayA/routingA"
"io/ioutil"
"os"
"path/filepath"
"strings"

"github.com/nadoo/conflag"
Expand Down Expand Up @@ -35,11 +37,12 @@ type Strategy struct {
DialTimeout int
RelayTimeout int
IntFace string
RoutingA *RoutingA
}

// NewConfFromFile returns a new config from file.
func NewConfFromFile(ruleFile string) (*Config, error) {
p := &Config{Name: ruleFile}
p := &Config{Name: getFilename(ruleFile)}

f := conflag.NewFromFile("rule", ruleFile)
f.StringSliceUniqVar(&p.Forward, "forward", nil, "forward url, format: SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS[,SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS]")
Expand All @@ -65,7 +68,6 @@ func NewConfFromFile(ruleFile string) (*Config, error) {
if err != nil {
return nil, err
}

return p, err
}

Expand All @@ -88,3 +90,19 @@ func ListDir(dirPth string, suffix string) (files []string, err error) {
}
return files, nil
}

func getFilename(ruleFile string) string {
return strings.TrimSuffix(filepath.Base(ruleFile), filepath.Ext(ruleFile))
}

func ParseRoutingA(filename string) (*RoutingA, error) {
b, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
ra, err := routingA.Parse(string(b))
if err != nil {
return nil, err
}
return NewRoutingA(ra)
}
38 changes: 38 additions & 0 deletions rule/internal/matcher/cidrMatcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package matcher

import (
"github.com/nadoo/glider/rule/internal/trie"
"strconv"
"strings"
)

type CIDRMatcher trie.Trie

// TODO: ipv6
func NewCIDRMatcher(CIDRs []string) *CIDRMatcher {
dict := make([]string, 0, len(CIDRs))
for _, CIDR := range CIDRs {
grp := strings.SplitN(CIDR, "/", 2)
if len(grp) == 1 {
grp = append(grp, "32")
}
l, _ := strconv.Atoi(grp[1])
arr := strings.Split(grp[0], ".")
var builder strings.Builder
for _, sec := range arr {
itg, _ := strconv.Atoi(sec)
tmp := strconv.FormatInt(int64(itg), 2)
builder.WriteString(strings.Repeat("0", 8-len(tmp)))
builder.WriteString(tmp)
if builder.Len() >= l {
break
}
}
dict = append(dict, builder.String()[:l])
}
return (*CIDRMatcher)(trie.New(dict))
}

func (m CIDRMatcher) Match(t interface{}) bool {
return (*trie.Trie)(&m).Match(t.(string)) != ""
}
46 changes: 46 additions & 0 deletions rule/internal/matcher/domainMatcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package matcher

import "strings"

type SuffixDomainTree map[string]interface{}

func NewSuffixDomainTree(domains []string) *SuffixDomainTree {
var (
tree = make(map[string]interface{})
m = tree
ok bool
)
for _, d := range domains {
m = tree
fields := strings.Split(d, ".")
for i := len(fields) - 1; i >= 0; i-- {
var t interface{}
if t, ok = m[fields[i]]; ok {
m = t.(map[string]interface{})
} else {
m[fields[i]] = make(map[string]interface{})
m = m[fields[i]].(map[string]interface{})
}
}
m[".end"] = struct{}{}
}
return (*SuffixDomainTree)(&tree)
}

func (m SuffixDomainTree) Match(t interface{}) bool {
fields := strings.Split(t.(string), ".")
mm := m
for i := len(fields) - 1; i >= 0; i-- {
var tt interface{}
var ok bool
if tt, ok = mm[fields[i]]; ok {
mm = tt.(map[string]interface{})
if _, ok := mm[".end"]; ok {
return true
}
} else {
return false
}
}
return false
}
29 changes: 29 additions & 0 deletions rule/internal/matcher/integerMatcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package matcher

import (
"github.com/nadoo/glider/log"
"strconv"
)

type IntegerMatcher map[int]struct{}

func NewIntegerMatcher(ports []string) *IntegerMatcher {
m := make(map[int]struct{})
for _, port := range ports {
p, err := strconv.Atoi(port)
if err != nil {
log.F("invalid port: ", port)
continue
}
m[p] = struct{}{}
}
if len(m) == 0 {
return nil
}
return (*IntegerMatcher)(&m)
}

func (m IntegerMatcher) Match(t interface{}) bool {
_, ok := m[t.(int)]
return ok
}
5 changes: 5 additions & 0 deletions rule/internal/matcher/matcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package matcher

type Matcher interface {
Match(t interface{}) bool
}
28 changes: 28 additions & 0 deletions rule/internal/matcher/networkMatcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package matcher

import "github.com/nadoo/glider/log"

const (
TCP = iota
UDP
)

type NetworkMatcher [2]bool

func NewNetworkMatcher(networks []string) *NetworkMatcher {
var bucket [2]bool
for _, n := range networks {
if n == "tcp" {
bucket[TCP] = true
} else if n == "udp" {
bucket[UDP] = true
} else {
log.F("invalid network: ", n)
}
}
return (*NetworkMatcher)(&bucket)
}

func (m NetworkMatcher) Match(t interface{}) bool {
return m[t.(int)]
}
19 changes: 19 additions & 0 deletions rule/internal/matcher/stringMatcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package matcher

type StringMatcher map[string]struct{}

func NewStringMatcher(app []string) *StringMatcher {
m := make(map[string]struct{})
for _, name := range app {
m[name] = struct{}{}
}
if len(m) == 0 {
return nil
}
return (*StringMatcher)(&m)
}

func (m StringMatcher) Match(t interface{}) bool {
_, ok := m[t.(string)]
return ok
}
Loading