1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2025-01-02 23:47:07 -05:00

protobuf for router

This commit is contained in:
Darien Raymond 2016-10-11 23:02:44 +02:00
parent ab3173039b
commit 63f3108737
No known key found for this signature in database
GPG Key ID: 7251FFA14BB18169
18 changed files with 8142 additions and 7765 deletions

View File

@ -1,10 +1,3 @@
package rules package rules
//go:generate go run chinaip_gen.go //go:generate go run chinaip_gen.go
func NewChinaIPRule(tag string) *Rule {
return &Rule{
Tag: tag,
Condition: NewIPv4Matcher(chinaIPNet),
}
}

View File

@ -12,14 +12,17 @@ import (
"os" "os"
"strconv" "strconv"
"strings" "strings"
v2net "v2ray.com/core/common/net"
) )
const ( const (
apnicFile = "http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest" apnicFile = "http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest"
) )
type IPEntry struct {
IP []byte
Bits uint32
}
func main() { func main() {
resp, err := http.Get(apnicFile) resp, err := http.Get(apnicFile)
if err != nil { if err != nil {
@ -31,7 +34,7 @@ func main() {
defer resp.Body.Close() defer resp.Body.Close()
scanner := bufio.NewScanner(resp.Body) scanner := bufio.NewScanner(resp.Body)
ipNet := v2net.NewIPNet() ips := make([]IPEntry, 0, 8192)
for scanner.Scan() { for scanner.Scan() {
line := scanner.Text() line := scanner.Text()
line = strings.TrimSpace(line) line = strings.TrimSpace(line)
@ -47,15 +50,16 @@ func main() {
if err != nil { if err != nil {
continue continue
} }
mask := 32 - int(math.Floor(math.Log2(float64(count))+0.5)) mask := uint32(math.Floor(math.Log2(float64(count)) + 0.5))
cidr := fmt.Sprintf("%s/%d", ip, mask) ipBytes := net.ParseIP(ip)
_, t, err := net.ParseCIDR(cidr) if len(ipBytes) == 0 {
if err != nil { panic("Invalid IP " + ip)
panic(err)
} }
ipNet.Add(t) ips = append(ips, IPEntry{
IP: []byte(ipBytes),
Bits: mask,
})
} }
dump := ipNet.Serialize()
file, err := os.OpenFile("chinaip_init.go", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) file, err := os.OpenFile("chinaip_init.go", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
if err != nil { if err != nil {
@ -64,20 +68,27 @@ func main() {
defer file.Close() defer file.Close()
fmt.Fprintln(file, "package rules") fmt.Fprintln(file, "package rules")
fmt.Fprintln(file, "import (")
fmt.Fprintln(file, "v2net \"v2ray.com/core/common/net\"")
fmt.Fprintln(file, ")")
fmt.Fprintln(file, "var (") fmt.Fprintln(file, "var chinaIPs []*IP")
fmt.Fprintln(file, "chinaIPNet *v2net.IPNet")
fmt.Fprintln(file, ")")
fmt.Fprintln(file, "func init() {") fmt.Fprintln(file, "func init() {")
fmt.Fprintln(file, "chinaIPNet = v2net.NewIPNetInitialValue(map[uint32]byte {") fmt.Fprintln(file, "chinaIPs = []*IP {")
for i := 0; i < len(dump); i += 2 { for _, ip := range ips {
fmt.Fprintln(file, dump[i], ": ", dump[i+1], ",") fmt.Fprintln(file, "&IP{", formatArray(ip.IP[12:16]), ",", ip.Bits, "},")
} }
fmt.Fprintln(file, "})") fmt.Fprintln(file, "}")
fmt.Fprintln(file, "}") fmt.Fprintln(file, "}")
} }
func formatArray(a []byte) string {
r := "[]byte{"
for idx, v := range a {
if idx > 0 {
r += ","
}
r += fmt.Sprintf("%d", v)
}
r += "}"
return r
}

File diff suppressed because it is too large Load Diff

View File

@ -8,12 +8,15 @@ import (
"v2ray.com/core/common/log" "v2ray.com/core/common/log"
) )
func parseChinaIPRule(data []byte) (*Rule, error) { func parseChinaIPRule(data []byte) (*RoutingRule, error) {
rawRule := new(JsonRule) rawRule := new(JsonRule)
err := json.Unmarshal(data, rawRule) err := json.Unmarshal(data, rawRule)
if err != nil { if err != nil {
log.Error("Router: Invalid router rule: ", err) log.Error("Router: Invalid router rule: ", err)
return nil, err return nil, err
} }
return NewChinaIPRule(rawRule.OutboundTag), nil return &RoutingRule{
Tag: rawRule.OutboundTag,
Ip: chinaIPs,
}, nil
} }

View File

@ -3,12 +3,18 @@
package rules_test package rules_test
import ( import (
"net"
"testing" "testing"
. "v2ray.com/core/app/router/rules" . "v2ray.com/core/app/router/rules"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/testing/assert" "v2ray.com/core/testing/assert"
) )
func makeDestination(ip string) v2net.Destination {
return v2net.TCPDestination(v2net.IPAddress(net.ParseIP(ip)), 80)
}
func TestChinaIPJson(t *testing.T) { func TestChinaIPJson(t *testing.T) {
assert := assert.On(t) assert := assert.On(t)
@ -17,10 +23,12 @@ func TestChinaIPJson(t *testing.T) {
"outboundTag": "x" "outboundTag": "x"
}`)) }`))
assert.String(rule.Tag).Equals("x") assert.String(rule.Tag).Equals("x")
assert.Bool(rule.Apply(makeDestination("121.14.1.189"))).IsTrue() // sina.com.cn cond, err := rule.BuildCondition()
assert.Bool(rule.Apply(makeDestination("101.226.103.106"))).IsTrue() // qq.com assert.Error(err).IsNil()
assert.Bool(rule.Apply(makeDestination("115.239.210.36"))).IsTrue() // image.baidu.com assert.Bool(cond.Apply(makeDestination("121.14.1.189"))).IsTrue() // sina.com.cn
assert.Bool(rule.Apply(makeDestination("120.135.126.1"))).IsTrue() assert.Bool(cond.Apply(makeDestination("101.226.103.106"))).IsTrue() // qq.com
assert.Bool(cond.Apply(makeDestination("115.239.210.36"))).IsTrue() // image.baidu.com
assert.Bool(cond.Apply(makeDestination("120.135.126.1"))).IsTrue()
assert.Bool(rule.Apply(makeDestination("8.8.8.8"))).IsFalse() assert.Bool(cond.Apply(makeDestination("8.8.8.8"))).IsFalse()
} }

View File

@ -1,27 +0,0 @@
package rules_test
import (
"net"
"testing"
. "v2ray.com/core/app/router/rules"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/testing/assert"
)
func makeDestination(ip string) v2net.Destination {
return v2net.TCPDestination(v2net.IPAddress(net.ParseIP(ip)), 80)
}
func TestChinaIP(t *testing.T) {
assert := assert.On(t)
rule := NewChinaIPRule("tag")
assert.Bool(rule.Apply(makeDestination("121.14.1.189"))).IsTrue() // sina.com.cn
assert.Bool(rule.Apply(makeDestination("101.226.103.106"))).IsTrue() // qq.com
assert.Bool(rule.Apply(makeDestination("115.239.210.36"))).IsTrue() // image.baidu.com
assert.Bool(rule.Apply(makeDestination("120.135.126.1"))).IsTrue()
assert.Bool(rule.Apply(makeDestination("101.201.173.126"))).IsTrue()
assert.Bool(rule.Apply(makeDestination("8.8.8.8"))).IsFalse()
}

View File

@ -1,12 +1,5 @@
package rules package rules
func NewChinaSitesRule(tag string) *Rule {
return &Rule{
Tag: tag,
Condition: chinaSitesConds,
}
}
const ( const (
anySubDomain = "^(.*\\.)?" anySubDomain = "^(.*\\.)?"
dotAm = "\\.am$" dotAm = "\\.am$"
@ -23,7 +16,7 @@ const (
) )
var ( var (
chinaSitesConds Condition chinaSitesDomains []*Domain
) )
func init() { func init() {
@ -487,15 +480,11 @@ func init() {
anySubDomain + "zuchecdn" + dotCom, anySubDomain + "zuchecdn" + dotCom,
} }
conds := make([]Condition, len(regexpDomains)) chinaSitesDomains = make([]*Domain, len(regexpDomains))
for idx, pattern := range regexpDomains { for idx, pattern := range regexpDomains {
matcher, err := NewRegexpDomainMatcher(pattern) chinaSitesDomains[idx] = &Domain{
if err != nil { Type: Domain_Regex,
panic(err) Value: pattern,
} }
conds[idx] = matcher
} }
anyConds := AnyCondition(conds)
chinaSitesConds = &anyConds
} }

View File

@ -7,15 +7,15 @@ import (
"v2ray.com/core/common/log" "v2ray.com/core/common/log"
) )
func parseChinaSitesRule(data []byte) (*Rule, error) { func parseChinaSitesRule(data []byte) (*RoutingRule, error) {
rawRule := new(JsonRule) rawRule := new(JsonRule)
err := json.Unmarshal(data, rawRule) err := json.Unmarshal(data, rawRule)
if err != nil { if err != nil {
log.Error("Router: Invalid router rule: ", err) log.Error("Router: Invalid router rule: ", err)
return nil, err return nil, err
} }
return &Rule{ return &RoutingRule{
Tag: rawRule.OutboundTag, Tag: rawRule.OutboundTag,
Condition: chinaSitesConds, Domain: chinaSitesDomains,
}, nil }, nil
} }

View File

@ -6,9 +6,14 @@ import (
"testing" "testing"
. "v2ray.com/core/app/router/rules" . "v2ray.com/core/app/router/rules"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/testing/assert" "v2ray.com/core/testing/assert"
) )
func makeDomainDestination(domain string) v2net.Destination {
return v2net.TCPDestination(v2net.DomainAddress(domain), 80)
}
func TestChinaSitesJson(t *testing.T) { func TestChinaSitesJson(t *testing.T) {
assert := assert.On(t) assert := assert.On(t)
@ -17,10 +22,12 @@ func TestChinaSitesJson(t *testing.T) {
"outboundTag": "y" "outboundTag": "y"
}`)) }`))
assert.String(rule.Tag).Equals("y") assert.String(rule.Tag).Equals("y")
assert.Bool(rule.Apply(makeDomainDestination("v.qq.com"))).IsTrue() cond, err := rule.BuildCondition()
assert.Bool(rule.Apply(makeDomainDestination("www.163.com"))).IsTrue() assert.Error(err).IsNil()
assert.Bool(rule.Apply(makeDomainDestination("ngacn.cc"))).IsTrue() assert.Bool(cond.Apply(makeDomainDestination("v.qq.com"))).IsTrue()
assert.Bool(rule.Apply(makeDomainDestination("12306.cn"))).IsTrue() assert.Bool(cond.Apply(makeDomainDestination("www.163.com"))).IsTrue()
assert.Bool(cond.Apply(makeDomainDestination("ngacn.cc"))).IsTrue()
assert.Bool(cond.Apply(makeDomainDestination("12306.cn"))).IsTrue()
assert.Bool(rule.Apply(makeDomainDestination("v2ray.com"))).IsFalse() assert.Bool(cond.Apply(makeDomainDestination("v2ray.com"))).IsFalse()
} }

View File

@ -1,25 +0,0 @@
package rules_test
import (
"testing"
. "v2ray.com/core/app/router/rules"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/testing/assert"
)
func makeDomainDestination(domain string) v2net.Destination {
return v2net.TCPDestination(v2net.DomainAddress(domain), 80)
}
func TestChinaSites(t *testing.T) {
assert := assert.On(t)
rule := NewChinaSitesRule("tag")
assert.Bool(rule.Apply(makeDomainDestination("v.qq.com"))).IsTrue()
assert.Bool(rule.Apply(makeDomainDestination("www.163.com"))).IsTrue()
assert.Bool(rule.Apply(makeDomainDestination("ngacn.cc"))).IsTrue()
assert.Bool(rule.Apply(makeDomainDestination("12306.cn"))).IsTrue()
assert.Bool(rule.Apply(makeDomainDestination("v2ray.com"))).IsFalse()
}

View File

@ -106,10 +106,10 @@ type CIDRMatcher struct {
cidr *net.IPNet cidr *net.IPNet
} }
func NewCIDRMatcher(ipnet string) (*CIDRMatcher, error) { func NewCIDRMatcher(ip []byte, mask uint32) (*CIDRMatcher, error) {
_, cidr, err := net.ParseCIDR(ipnet) cidr := &net.IPNet{
if err != nil { IP: net.IP(ip),
return nil, err Mask: net.CIDRMask(int(mask), len(ip)),
} }
return &CIDRMatcher{ return &CIDRMatcher{
cidr: cidr, cidr: cidr,

View File

@ -1,6 +1,9 @@
package rules package rules
import ( import (
"errors"
"net"
v2net "v2ray.com/core/common/net" v2net "v2ray.com/core/common/net"
) )
@ -13,15 +16,70 @@ func (this *Rule) Apply(dest v2net.Destination) bool {
return this.Condition.Apply(dest) return this.Condition.Apply(dest)
} }
type DomainStrategy int func (this *RoutingRule) BuildCondition() (Condition, error) {
conds := NewConditionChan()
var ( if len(this.Domain) > 0 {
DomainAsIs = DomainStrategy(0) anyCond := NewAnyCondition()
AlwaysUseIP = DomainStrategy(1) for _, domain := range this.Domain {
UseIPIfNonMatch = DomainStrategy(2) if domain.Type == Domain_Plain {
) anyCond.Add(NewPlainDomainMatcher(domain.Value))
} else {
matcher, err := NewRegexpDomainMatcher(domain.Value)
if err != nil {
return nil, err
}
anyCond.Add(matcher)
}
}
conds.Add(anyCond)
}
type RouterRuleConfig struct { if len(this.Ip) > 0 {
Rules []*Rule ipv4Net := make(map[uint32]byte)
DomainStrategy DomainStrategy ipv6Cond := NewAnyCondition()
hasIpv6 := false
for _, ip := range this.Ip {
switch len(ip.Ip) {
case net.IPv4len:
k := (uint32(ip.Ip[0]) << 24) + (uint32(ip.Ip[1]) << 16) + (uint32(ip.Ip[2]) << 8) + uint32(ip.Ip[3])
ipv4Net[k] = byte(32 - ip.UnmatchingBits)
case net.IPv6len:
hasIpv6 = true
matcher, err := NewCIDRMatcher(ip.Ip, uint32(32)-ip.UnmatchingBits)
if err != nil {
return nil, err
}
ipv6Cond.Add(matcher)
default:
return nil, errors.New("Router: Invalid IP length.")
}
}
if len(ipv4Net) > 0 && hasIpv6 {
cond := NewAnyCondition()
cond.Add(NewIPv4Matcher(v2net.NewIPNetInitialValue(ipv4Net)))
cond.Add(ipv6Cond)
conds.Add(cond)
} else if len(ipv4Net) > 0 {
conds.Add(NewIPv4Matcher(v2net.NewIPNetInitialValue(ipv4Net)))
} else if hasIpv6 {
conds.Add(ipv6Cond)
}
}
if this.PortRange != nil {
conds.Add(NewPortMatcher(*this.PortRange))
}
if this.NetworkList != nil {
conds.Add(NewNetworkMatcher(this.NetworkList))
}
if conds.Len() == 0 {
return nil, errors.New("Router: This rule has no effective fields.")
}
return conds, nil
} }

View File

@ -0,0 +1,213 @@
// Code generated by protoc-gen-go.
// source: v2ray.com/core/app/router/rules/config.proto
// DO NOT EDIT!
/*
Package rules is a generated protocol buffer package.
It is generated from these files:
v2ray.com/core/app/router/rules/config.proto
It has these top-level messages:
Domain
IP
RoutingRule
Config
*/
package rules
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import v2ray_core_common_net "v2ray.com/core/common/net"
import v2ray_core_common_net1 "v2ray.com/core/common/net"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// Type of domain value.
type Domain_Type int32
const (
// The value is used as is.
Domain_Plain Domain_Type = 0
// The value is used as a regular expression.
Domain_Regex Domain_Type = 1
)
var Domain_Type_name = map[int32]string{
0: "Plain",
1: "Regex",
}
var Domain_Type_value = map[string]int32{
"Plain": 0,
"Regex": 1,
}
func (x Domain_Type) String() string {
return proto.EnumName(Domain_Type_name, int32(x))
}
func (Domain_Type) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} }
type Config_DomainStrategy int32
const (
Config_AsIs Config_DomainStrategy = 0
Config_UseIp Config_DomainStrategy = 1
Config_IpIfNonMatch Config_DomainStrategy = 2
)
var Config_DomainStrategy_name = map[int32]string{
0: "AsIs",
1: "UseIp",
2: "IpIfNonMatch",
}
var Config_DomainStrategy_value = map[string]int32{
"AsIs": 0,
"UseIp": 1,
"IpIfNonMatch": 2,
}
func (x Config_DomainStrategy) String() string {
return proto.EnumName(Config_DomainStrategy_name, int32(x))
}
func (Config_DomainStrategy) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{3, 0} }
// Domain for routing decision.
type Domain struct {
// Domain matching type.
Type Domain_Type `protobuf:"varint,1,opt,name=type,enum=v2ray.core.app.router.rules.Domain_Type" json:"type,omitempty"`
// Domain value.
Value string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"`
}
func (m *Domain) Reset() { *m = Domain{} }
func (m *Domain) String() string { return proto.CompactTextString(m) }
func (*Domain) ProtoMessage() {}
func (*Domain) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
// IP for routing decision.
type IP struct {
// IP address, should be either 4 or 16 bytes.
Ip []byte `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"`
// Number of right-most bits in IP matching that is allowed.
// Single IP address like 127.0.0.1 should use unmatching_bits = 0.
// CIDR 10.0.0.0/8 should use unmatching_bits = 32-8 = 24.
UnmatchingBits uint32 `protobuf:"varint,2,opt,name=unmatching_bits,json=unmatchingBits" json:"unmatching_bits,omitempty"`
}
func (m *IP) Reset() { *m = IP{} }
func (m *IP) String() string { return proto.CompactTextString(m) }
func (*IP) ProtoMessage() {}
func (*IP) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
type RoutingRule struct {
Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"`
Domain []*Domain `protobuf:"bytes,2,rep,name=domain" json:"domain,omitempty"`
Ip []*IP `protobuf:"bytes,3,rep,name=ip" json:"ip,omitempty"`
PortRange *v2ray_core_common_net.PortRange `protobuf:"bytes,4,opt,name=port_range,json=portRange" json:"port_range,omitempty"`
NetworkList *v2ray_core_common_net1.NetworkList `protobuf:"bytes,5,opt,name=network_list,json=networkList" json:"network_list,omitempty"`
}
func (m *RoutingRule) Reset() { *m = RoutingRule{} }
func (m *RoutingRule) String() string { return proto.CompactTextString(m) }
func (*RoutingRule) ProtoMessage() {}
func (*RoutingRule) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (m *RoutingRule) GetDomain() []*Domain {
if m != nil {
return m.Domain
}
return nil
}
func (m *RoutingRule) GetIp() []*IP {
if m != nil {
return m.Ip
}
return nil
}
func (m *RoutingRule) GetPortRange() *v2ray_core_common_net.PortRange {
if m != nil {
return m.PortRange
}
return nil
}
func (m *RoutingRule) GetNetworkList() *v2ray_core_common_net1.NetworkList {
if m != nil {
return m.NetworkList
}
return nil
}
type Config struct {
DomainStrategy Config_DomainStrategy `protobuf:"varint,1,opt,name=domain_strategy,json=domainStrategy,enum=v2ray.core.app.router.rules.Config_DomainStrategy" json:"domain_strategy,omitempty"`
Rule []*RoutingRule `protobuf:"bytes,2,rep,name=rule" json:"rule,omitempty"`
}
func (m *Config) Reset() { *m = Config{} }
func (m *Config) String() string { return proto.CompactTextString(m) }
func (*Config) ProtoMessage() {}
func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
func (m *Config) GetRule() []*RoutingRule {
if m != nil {
return m.Rule
}
return nil
}
func init() {
proto.RegisterType((*Domain)(nil), "v2ray.core.app.router.rules.Domain")
proto.RegisterType((*IP)(nil), "v2ray.core.app.router.rules.IP")
proto.RegisterType((*RoutingRule)(nil), "v2ray.core.app.router.rules.RoutingRule")
proto.RegisterType((*Config)(nil), "v2ray.core.app.router.rules.Config")
proto.RegisterEnum("v2ray.core.app.router.rules.Domain_Type", Domain_Type_name, Domain_Type_value)
proto.RegisterEnum("v2ray.core.app.router.rules.Config_DomainStrategy", Config_DomainStrategy_name, Config_DomainStrategy_value)
}
func init() { proto.RegisterFile("v2ray.com/core/app/router/rules/config.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 472 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x52, 0x4f, 0x8b, 0xd5, 0x3e,
0x14, 0xfd, 0xb5, 0xef, 0x0f, 0xbf, 0x77, 0xdf, 0xb3, 0x53, 0x82, 0x8b, 0x32, 0x0a, 0x53, 0xaa,
0x30, 0x5d, 0x48, 0x0a, 0x15, 0x71, 0xa1, 0x22, 0x3e, 0x75, 0x51, 0xd0, 0xa1, 0x44, 0xdd, 0xe8,
0xe2, 0x91, 0xe9, 0x64, 0x6a, 0xb0, 0x4d, 0x42, 0x9a, 0x8e, 0x3e, 0xbf, 0x87, 0xdf, 0xce, 0x0f,
0x23, 0x4d, 0x3a, 0xcc, 0x8c, 0x30, 0xc5, 0xdd, 0xbd, 0xe1, 0x9c, 0x93, 0x7b, 0xef, 0x39, 0xf0,
0xe8, 0x22, 0xd7, 0x74, 0x8f, 0x2b, 0xd9, 0x66, 0x95, 0xd4, 0x2c, 0xa3, 0x4a, 0x65, 0x5a, 0xf6,
0x86, 0xe9, 0x4c, 0xf7, 0x0d, 0xeb, 0xb2, 0x4a, 0x8a, 0x73, 0x5e, 0x63, 0xa5, 0xa5, 0x91, 0xe8,
0xde, 0x25, 0x5a, 0x33, 0x4c, 0x95, 0xc2, 0x0e, 0x89, 0x2d, 0xf2, 0xf0, 0xe1, 0x5f, 0x52, 0x95,
0x6c, 0x5b, 0x29, 0x32, 0xc1, 0x4c, 0xa6, 0xa4, 0x36, 0x4e, 0xe2, 0xf0, 0xf8, 0x76, 0x94, 0x60,
0xe6, 0xbb, 0xd4, 0xdf, 0x1c, 0x30, 0xf9, 0x09, 0xcb, 0x37, 0xb2, 0xa5, 0x5c, 0xa0, 0xe7, 0x30,
0x37, 0x7b, 0xc5, 0x22, 0x2f, 0xf6, 0xd2, 0x20, 0x4f, 0xf1, 0xc4, 0x10, 0xd8, 0x51, 0xf0, 0xc7,
0xbd, 0x62, 0xc4, 0xb2, 0xd0, 0x5d, 0x58, 0x5c, 0xd0, 0xa6, 0x67, 0x91, 0x1f, 0x7b, 0xe9, 0x8a,
0xb8, 0x26, 0xb9, 0x0f, 0xf3, 0x01, 0x83, 0x56, 0xb0, 0x28, 0x1b, 0xca, 0x45, 0xf8, 0xdf, 0x50,
0x12, 0x56, 0xb3, 0x1f, 0xa1, 0x97, 0xbc, 0x00, 0xbf, 0x28, 0x51, 0x00, 0x3e, 0x57, 0xf6, 0xd7,
0x0d, 0xf1, 0xb9, 0x42, 0xc7, 0x70, 0xd0, 0x8b, 0x96, 0x9a, 0xea, 0x2b, 0x17, 0xf5, 0xee, 0x94,
0x9b, 0xce, 0x6a, 0xde, 0x21, 0xc1, 0xd5, 0xf3, 0x96, 0x9b, 0x2e, 0xf9, 0xe5, 0xc3, 0x9a, 0xc8,
0xde, 0x70, 0x51, 0x93, 0xbe, 0x61, 0x28, 0x84, 0x99, 0xa1, 0xb5, 0x55, 0x5a, 0x91, 0xa1, 0x44,
0xcf, 0x60, 0x79, 0x66, 0x27, 0x8d, 0xfc, 0x78, 0x96, 0xae, 0xf3, 0x07, 0xff, 0xb0, 0x14, 0x19,
0x29, 0x28, 0xb3, 0x73, 0xcd, 0x2c, 0xf1, 0x68, 0x92, 0x58, 0x94, 0x76, 0xf0, 0x97, 0x00, 0x83,
0x03, 0x3b, 0x4d, 0x45, 0xcd, 0xa2, 0x79, 0xec, 0xa5, 0xeb, 0x3c, 0xbe, 0x4e, 0x74, 0x26, 0x60,
0xc1, 0x0c, 0x2e, 0xa5, 0x36, 0x64, 0xc0, 0x91, 0x95, 0xba, 0x2c, 0xd1, 0x5b, 0xd8, 0x8c, 0xe6,
0xec, 0x1a, 0xde, 0x99, 0x68, 0x61, 0x25, 0x92, 0x5b, 0x24, 0x4e, 0x1c, 0xf4, 0x1d, 0xef, 0x0c,
0x59, 0x8b, 0xab, 0x26, 0xf9, 0xed, 0xc1, 0xf2, 0xb5, 0xcd, 0x13, 0xfa, 0x02, 0x07, 0x6e, 0x9b,
0x5d, 0x67, 0x34, 0x35, 0xac, 0xde, 0x8f, 0xf6, 0xe6, 0x93, 0x0b, 0x39, 0xf6, 0x78, 0x90, 0x0f,
0x23, 0x93, 0x04, 0x67, 0x37, 0xfa, 0x21, 0x30, 0x03, 0x7c, 0xbc, 0xed, 0x74, 0x60, 0xae, 0xf9,
0x44, 0x2c, 0x2b, 0x79, 0x0a, 0xc1, 0x4d, 0x7d, 0xf4, 0x3f, 0xcc, 0x5f, 0x75, 0x45, 0xe7, 0x32,
0xf2, 0xa9, 0x63, 0x85, 0x0a, 0x3d, 0x14, 0xc2, 0xa6, 0x50, 0xc5, 0xf9, 0x89, 0x14, 0xef, 0x07,
0xef, 0x43, 0x7f, 0xfb, 0x04, 0x8e, 0x2a, 0xd9, 0x4e, 0xfd, 0xb6, 0x5d, 0xbb, 0x05, 0xca, 0x21,
0xe1, 0x9f, 0x17, 0xf6, 0xed, 0x74, 0x69, 0xf3, 0xfe, 0xf8, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff,
0x00, 0xa8, 0x7d, 0x12, 0x8b, 0x03, 0x00, 0x00,
}

View File

@ -0,0 +1,55 @@
syntax = "proto3";
package v2ray.core.app.router.rules;
option go_package = "rules";
option java_package = "com.v2ray.core.app.router.rules";
option java_outer_classname = "ConfigProto";
import "v2ray.com/core/common/net/port.proto";
import "v2ray.com/core/common/net/network.proto";
// Domain for routing decision.
message Domain {
// Type of domain value.
enum Type {
// The value is used as is.
Plain = 0;
// The value is used as a regular expression.
Regex = 1;
}
// Domain matching type.
Type type = 1;
// Domain value.
string value = 2;
}
// IP for routing decision.
message IP {
// IP address, should be either 4 or 16 bytes.
bytes ip = 1;
// Number of right-most bits in IP matching that is allowed.
// Single IP address like 127.0.0.1 should use unmatching_bits = 0.
// CIDR 10.0.0.0/8 should use unmatching_bits = 32-8 = 24.
uint32 unmatching_bits = 2;
}
message RoutingRule {
string tag = 1;
repeated Domain domain = 2;
repeated IP ip = 3;
v2ray.core.common.net.PortRange port_range = 4;
v2ray.core.common.net.NetworkList network_list = 5;
}
message Config {
enum DomainStrategy {
AsIs = 0;
UseIp = 1;
IpIfNonMatch = 2;
}
DomainStrategy domain_strategy = 1;
repeated RoutingRule rule = 2;
}

View File

@ -4,7 +4,7 @@ package rules
import ( import (
"encoding/json" "encoding/json"
"errors" "strconv"
"strings" "strings"
router "v2ray.com/core/app/router" router "v2ray.com/core/app/router"
@ -18,7 +18,38 @@ type JsonRule struct {
OutboundTag string `json:"outboundTag"` OutboundTag string `json:"outboundTag"`
} }
func parseFieldRule(msg json.RawMessage) (*Rule, error) { func parseIP(s string) *IP {
var addr, mask string
i := strings.Index(s, "/")
if i < 0 {
addr = s
} else {
addr = s[:i]
mask = s[i+1:]
}
ip := v2net.ParseAddress(addr)
if !ip.Family().Either(v2net.AddressFamilyIPv4, v2net.AddressFamilyIPv6) {
return nil
}
bits := uint32(32)
if len(mask) > 0 {
bits64, err := strconv.ParseUint(mask, 10, 32)
if err != nil {
return nil
}
bits = uint32(bits64)
}
if bits > 32 {
log.Warning("Router: invalid network mask: ", bits)
return nil
}
return &IP{
Ip: []byte(ip.IP()),
UnmatchingBits: 32 - bits,
}
}
func parseFieldRule(msg json.RawMessage) (*RoutingRule, error) {
type RawFieldRule struct { type RawFieldRule struct {
JsonRule JsonRule
Domain *collect.StringList `json:"domain"` Domain *collect.StringList `json:"domain"`
@ -31,54 +62,45 @@ func parseFieldRule(msg json.RawMessage) (*Rule, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
conds := NewConditionChan()
if rawFieldRule.Domain != nil && rawFieldRule.Domain.Len() > 0 { rule := new(RoutingRule)
anyCond := NewAnyCondition() rule.Tag = rawFieldRule.OutboundTag
for _, rawDomain := range *(rawFieldRule.Domain) {
var matcher Condition if rawFieldRule.Domain != nil {
if strings.HasPrefix(rawDomain, "regexp:") { for _, domain := range *rawFieldRule.Domain {
rawMatcher, err := NewRegexpDomainMatcher(rawDomain[7:]) domainRule := new(Domain)
if err != nil { if strings.HasPrefix(domain, "regexp:") {
return nil, err domainRule.Type = Domain_Regex
} domainRule.Value = domain[7:]
matcher = rawMatcher
} else { } else {
matcher = NewPlainDomainMatcher(rawDomain) domainRule.Type = Domain_Plain
domainRule.Value = domain
} }
anyCond.Add(matcher) rule.Domain = append(rule.Domain, domainRule)
} }
conds.Add(anyCond)
} }
if rawFieldRule.IP != nil && rawFieldRule.IP.Len() > 0 { if rawFieldRule.IP != nil {
anyCond := NewAnyCondition() for _, ip := range *rawFieldRule.IP {
for _, ipStr := range *(rawFieldRule.IP) { ipRule := parseIP(ip)
cidrMatcher, err := NewCIDRMatcher(ipStr) if ipRule != nil {
if err != nil { rule.Ip = append(rule.Ip, ipRule)
log.Error("Router: Invalid IP range in router rule: ", err)
return nil, err
} }
anyCond.Add(cidrMatcher)
} }
conds.Add(anyCond)
} }
if rawFieldRule.Port != nil { if rawFieldRule.Port != nil {
conds.Add(NewPortMatcher(*rawFieldRule.Port)) rule.PortRange = rawFieldRule.Port
} }
if rawFieldRule.Network != nil { if rawFieldRule.Network != nil {
conds.Add(NewNetworkMatcher(rawFieldRule.Network)) rule.NetworkList = rawFieldRule.Network
} }
if conds.Len() == 0 {
return nil, errors.New("Router: This rule has no effective fields.") return rule, nil
}
return &Rule{
Tag: rawFieldRule.OutboundTag,
Condition: conds,
}, nil
} }
func ParseRule(msg json.RawMessage) *Rule { func ParseRule(msg json.RawMessage) *RoutingRule {
rawRule := new(JsonRule) rawRule := new(JsonRule)
err := json.Unmarshal(msg, rawRule) err := json.Unmarshal(msg, rawRule)
if err != nil { if err != nil {
@ -124,19 +146,19 @@ func init() {
if err := json.Unmarshal(data, jsonConfig); err != nil { if err := json.Unmarshal(data, jsonConfig); err != nil {
return nil, err return nil, err
} }
config := &RouterRuleConfig{ config := &Config{
Rules: make([]*Rule, len(jsonConfig.RuleList)), Rule: make([]*RoutingRule, len(jsonConfig.RuleList)),
DomainStrategy: DomainAsIs, DomainStrategy: Config_AsIs,
} }
domainStrategy := strings.ToLower(jsonConfig.DomainStrategy) domainStrategy := strings.ToLower(jsonConfig.DomainStrategy)
if domainStrategy == "alwaysip" { if domainStrategy == "alwaysip" {
config.DomainStrategy = AlwaysUseIP config.DomainStrategy = Config_UseIp
} else if domainStrategy == "ipifnonmatch" { } else if domainStrategy == "ipifnonmatch" {
config.DomainStrategy = UseIPIfNonMatch config.DomainStrategy = Config_IpIfNonMatch
} }
for idx, rawRule := range jsonConfig.RuleList { for idx, rawRule := range jsonConfig.RuleList {
rule := ParseRule(rawRule) rule := ParseRule(rawRule)
config.Rules[idx] = rule config.Rule[idx] = rule
} }
return config, nil return config, nil
}) })

View File

@ -24,11 +24,13 @@ func TestDomainRule(t *testing.T) {
"outboundTag": "direct" "outboundTag": "direct"
}`)) }`))
assert.Pointer(rule).IsNotNil() assert.Pointer(rule).IsNotNil()
assert.Bool(rule.Apply(v2net.TCPDestination(v2net.DomainAddress("www.ooxx.com"), 80))).IsTrue() cond, err := rule.BuildCondition()
assert.Bool(rule.Apply(v2net.TCPDestination(v2net.DomainAddress("www.aabb.com"), 80))).IsFalse() assert.Error(err).IsNil()
assert.Bool(rule.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80))).IsFalse() assert.Bool(cond.Apply(v2net.TCPDestination(v2net.DomainAddress("www.ooxx.com"), 80))).IsTrue()
assert.Bool(rule.Apply(v2net.TCPDestination(v2net.DomainAddress("www.12306.cn"), 80))).IsTrue() assert.Bool(cond.Apply(v2net.TCPDestination(v2net.DomainAddress("www.aabb.com"), 80))).IsFalse()
assert.Bool(rule.Apply(v2net.TCPDestination(v2net.DomainAddress("www.acn.com"), 80))).IsFalse() assert.Bool(cond.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80))).IsFalse()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.DomainAddress("www.12306.cn"), 80))).IsTrue()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.DomainAddress("www.acn.com"), 80))).IsFalse()
} }
func TestIPRule(t *testing.T) { func TestIPRule(t *testing.T) {
@ -44,8 +46,10 @@ func TestIPRule(t *testing.T) {
"outboundTag": "direct" "outboundTag": "direct"
}`)) }`))
assert.Pointer(rule).IsNotNil() assert.Pointer(rule).IsNotNil()
assert.Bool(rule.Apply(v2net.TCPDestination(v2net.DomainAddress("www.ooxx.com"), 80))).IsFalse() cond, err := rule.BuildCondition()
assert.Bool(rule.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{10, 0, 0, 1}), 80))).IsTrue() assert.Error(err).IsNil()
assert.Bool(rule.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80))).IsFalse() assert.Bool(cond.Apply(v2net.TCPDestination(v2net.DomainAddress("www.ooxx.com"), 80))).IsFalse()
assert.Bool(rule.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{192, 0, 0, 1}), 80))).IsTrue() assert.Bool(cond.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{10, 0, 0, 1}), 80))).IsTrue()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80))).IsFalse()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{192, 0, 0, 1}), 80))).IsTrue()
} }

View File

@ -16,17 +16,29 @@ var (
) )
type Router struct { type Router struct {
config *RouterRuleConfig domainStrategy Config_DomainStrategy
cache *RoutingTable rules []Rule
dnsServer dns.Server cache *RoutingTable
dnsServer dns.Server
} }
func NewRouter(config *RouterRuleConfig, space app.Space) *Router { func NewRouter(config *Config, space app.Space) *Router {
r := &Router{ r := &Router{
config: config, domainStrategy: config.DomainStrategy,
cache: NewRoutingTable(), cache: NewRoutingTable(),
rules: make([]Rule, len(config.Rule)),
} }
space.InitializeApplication(func() error { space.InitializeApplication(func() error {
for idx, rule := range config.Rule {
r.rules[idx].Tag = rule.Tag
cond, err := rule.BuildCondition()
if err != nil {
return err
}
r.rules[idx].Condition = cond
}
if !space.HasApp(dns.APP_ID) { if !space.HasApp(dns.APP_ID) {
log.Error("DNS: Router is not found in the space.") log.Error("DNS: Router is not found in the space.")
return app.ErrMissingApplication return app.ErrMissingApplication
@ -59,18 +71,18 @@ func (this *Router) ResolveIP(dest v2net.Destination) []v2net.Destination {
} }
func (this *Router) takeDetourWithoutCache(dest v2net.Destination) (string, error) { func (this *Router) takeDetourWithoutCache(dest v2net.Destination) (string, error) {
for _, rule := range this.config.Rules { for _, rule := range this.rules {
if rule.Apply(dest) { if rule.Apply(dest) {
return rule.Tag, nil return rule.Tag, nil
} }
} }
if this.config.DomainStrategy == UseIPIfNonMatch && dest.Address.Family().IsDomain() { if this.domainStrategy == Config_IpIfNonMatch && dest.Address.Family().IsDomain() {
log.Info("Router: Looking up IP for ", dest) log.Info("Router: Looking up IP for ", dest)
ipDests := this.ResolveIP(dest) ipDests := this.ResolveIP(dest)
if ipDests != nil { if ipDests != nil {
for _, ipDest := range ipDests { for _, ipDest := range ipDests {
log.Info("Router: Trying IP ", ipDest) log.Info("Router: Trying IP ", ipDest)
for _, rule := range this.config.Rules { for _, rule := range this.rules {
if rule.Apply(ipDest) { if rule.Apply(ipDest) {
return rule.Tag, nil return rule.Tag, nil
} }
@ -97,7 +109,7 @@ type RouterFactory struct {
} }
func (this *RouterFactory) Create(rawConfig interface{}, space app.Space) (router.Router, error) { func (this *RouterFactory) Create(rawConfig interface{}, space app.Space) (router.Router, error) {
return NewRouter(rawConfig.(*RouterRuleConfig), space), nil return NewRouter(rawConfig.(*Config), space), nil
} }
func init() { func init() {

View File

@ -17,11 +17,13 @@ import (
func TestSimpleRouter(t *testing.T) { func TestSimpleRouter(t *testing.T) {
assert := assert.On(t) assert := assert.On(t)
config := &RouterRuleConfig{ config := &Config{
Rules: []*Rule{ Rule: []*RoutingRule{
{ {
Tag: "test", Tag: "test",
Condition: NewNetworkMatcher(v2net.Network_TCP.AsList()), NetworkList: &v2net.NetworkList{
Network: []v2net.Network{v2net.Network_TCP},
},
}, },
}, },
} }