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
//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"
"strconv"
"strings"
v2net "v2ray.com/core/common/net"
)
const (
apnicFile = "http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest"
)
type IPEntry struct {
IP []byte
Bits uint32
}
func main() {
resp, err := http.Get(apnicFile)
if err != nil {
@ -31,7 +34,7 @@ func main() {
defer resp.Body.Close()
scanner := bufio.NewScanner(resp.Body)
ipNet := v2net.NewIPNet()
ips := make([]IPEntry, 0, 8192)
for scanner.Scan() {
line := scanner.Text()
line = strings.TrimSpace(line)
@ -47,15 +50,16 @@ func main() {
if err != nil {
continue
}
mask := 32 - int(math.Floor(math.Log2(float64(count))+0.5))
cidr := fmt.Sprintf("%s/%d", ip, mask)
_, t, err := net.ParseCIDR(cidr)
if err != nil {
panic(err)
mask := uint32(math.Floor(math.Log2(float64(count)) + 0.5))
ipBytes := net.ParseIP(ip)
if len(ipBytes) == 0 {
panic("Invalid IP " + ip)
}
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)
if err != nil {
@ -64,20 +68,27 @@ func main() {
defer file.Close()
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, "chinaIPNet *v2net.IPNet")
fmt.Fprintln(file, ")")
fmt.Fprintln(file, "var chinaIPs []*IP")
fmt.Fprintln(file, "func init() {")
fmt.Fprintln(file, "chinaIPNet = v2net.NewIPNetInitialValue(map[uint32]byte {")
for i := 0; i < len(dump); i += 2 {
fmt.Fprintln(file, dump[i], ": ", dump[i+1], ",")
fmt.Fprintln(file, "chinaIPs = []*IP {")
for _, ip := range ips {
fmt.Fprintln(file, "&IP{", formatArray(ip.IP[12:16]), ",", ip.Bits, "},")
}
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"
)
func parseChinaIPRule(data []byte) (*Rule, error) {
func parseChinaIPRule(data []byte) (*RoutingRule, error) {
rawRule := new(JsonRule)
err := json.Unmarshal(data, rawRule)
if err != nil {
log.Error("Router: Invalid router rule: ", 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
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 TestChinaIPJson(t *testing.T) {
assert := assert.On(t)
@ -17,10 +23,12 @@ func TestChinaIPJson(t *testing.T) {
"outboundTag": "x"
}`))
assert.String(rule.Tag).Equals("x")
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()
cond, err := rule.BuildCondition()
assert.Error(err).IsNil()
assert.Bool(cond.Apply(makeDestination("121.14.1.189"))).IsTrue() // sina.com.cn
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
func NewChinaSitesRule(tag string) *Rule {
return &Rule{
Tag: tag,
Condition: chinaSitesConds,
}
}
const (
anySubDomain = "^(.*\\.)?"
dotAm = "\\.am$"
@ -23,7 +16,7 @@ const (
)
var (
chinaSitesConds Condition
chinaSitesDomains []*Domain
)
func init() {
@ -487,15 +480,11 @@ func init() {
anySubDomain + "zuchecdn" + dotCom,
}
conds := make([]Condition, len(regexpDomains))
chinaSitesDomains = make([]*Domain, len(regexpDomains))
for idx, pattern := range regexpDomains {
matcher, err := NewRegexpDomainMatcher(pattern)
if err != nil {
panic(err)
chinaSitesDomains[idx] = &Domain{
Type: Domain_Regex,
Value: pattern,
}
conds[idx] = matcher
}
anyConds := AnyCondition(conds)
chinaSitesConds = &anyConds
}

View File

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

View File

@ -6,9 +6,14 @@ 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 TestChinaSitesJson(t *testing.T) {
assert := assert.On(t)
@ -17,10 +22,12 @@ func TestChinaSitesJson(t *testing.T) {
"outboundTag": "y"
}`))
assert.String(rule.Tag).Equals("y")
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()
cond, err := rule.BuildCondition()
assert.Error(err).IsNil()
assert.Bool(cond.Apply(makeDomainDestination("v.qq.com"))).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
}
func NewCIDRMatcher(ipnet string) (*CIDRMatcher, error) {
_, cidr, err := net.ParseCIDR(ipnet)
if err != nil {
return nil, err
func NewCIDRMatcher(ip []byte, mask uint32) (*CIDRMatcher, error) {
cidr := &net.IPNet{
IP: net.IP(ip),
Mask: net.CIDRMask(int(mask), len(ip)),
}
return &CIDRMatcher{
cidr: cidr,

View File

@ -1,6 +1,9 @@
package rules
import (
"errors"
"net"
v2net "v2ray.com/core/common/net"
)
@ -13,15 +16,70 @@ func (this *Rule) Apply(dest v2net.Destination) bool {
return this.Condition.Apply(dest)
}
type DomainStrategy int
func (this *RoutingRule) BuildCondition() (Condition, error) {
conds := NewConditionChan()
var (
DomainAsIs = DomainStrategy(0)
AlwaysUseIP = DomainStrategy(1)
UseIPIfNonMatch = DomainStrategy(2)
)
if len(this.Domain) > 0 {
anyCond := NewAnyCondition()
for _, domain := range this.Domain {
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 {
Rules []*Rule
DomainStrategy DomainStrategy
if len(this.Ip) > 0 {
ipv4Net := make(map[uint32]byte)
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 (
"encoding/json"
"errors"
"strconv"
"strings"
router "v2ray.com/core/app/router"
@ -18,7 +18,38 @@ type JsonRule struct {
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 {
JsonRule
Domain *collect.StringList `json:"domain"`
@ -31,54 +62,45 @@ func parseFieldRule(msg json.RawMessage) (*Rule, error) {
if err != nil {
return nil, err
}
conds := NewConditionChan()
if rawFieldRule.Domain != nil && rawFieldRule.Domain.Len() > 0 {
anyCond := NewAnyCondition()
for _, rawDomain := range *(rawFieldRule.Domain) {
var matcher Condition
if strings.HasPrefix(rawDomain, "regexp:") {
rawMatcher, err := NewRegexpDomainMatcher(rawDomain[7:])
if err != nil {
return nil, err
}
matcher = rawMatcher
rule := new(RoutingRule)
rule.Tag = rawFieldRule.OutboundTag
if rawFieldRule.Domain != nil {
for _, domain := range *rawFieldRule.Domain {
domainRule := new(Domain)
if strings.HasPrefix(domain, "regexp:") {
domainRule.Type = Domain_Regex
domainRule.Value = domain[7:]
} 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 {
anyCond := NewAnyCondition()
for _, ipStr := range *(rawFieldRule.IP) {
cidrMatcher, err := NewCIDRMatcher(ipStr)
if err != nil {
log.Error("Router: Invalid IP range in router rule: ", err)
return nil, err
if rawFieldRule.IP != nil {
for _, ip := range *rawFieldRule.IP {
ipRule := parseIP(ip)
if ipRule != nil {
rule.Ip = append(rule.Ip, ipRule)
}
anyCond.Add(cidrMatcher)
}
conds.Add(anyCond)
}
if rawFieldRule.Port != nil {
conds.Add(NewPortMatcher(*rawFieldRule.Port))
rule.PortRange = rawFieldRule.Port
}
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{
Tag: rawFieldRule.OutboundTag,
Condition: conds,
}, nil
return rule, nil
}
func ParseRule(msg json.RawMessage) *Rule {
func ParseRule(msg json.RawMessage) *RoutingRule {
rawRule := new(JsonRule)
err := json.Unmarshal(msg, rawRule)
if err != nil {
@ -124,19 +146,19 @@ func init() {
if err := json.Unmarshal(data, jsonConfig); err != nil {
return nil, err
}
config := &RouterRuleConfig{
Rules: make([]*Rule, len(jsonConfig.RuleList)),
DomainStrategy: DomainAsIs,
config := &Config{
Rule: make([]*RoutingRule, len(jsonConfig.RuleList)),
DomainStrategy: Config_AsIs,
}
domainStrategy := strings.ToLower(jsonConfig.DomainStrategy)
if domainStrategy == "alwaysip" {
config.DomainStrategy = AlwaysUseIP
config.DomainStrategy = Config_UseIp
} else if domainStrategy == "ipifnonmatch" {
config.DomainStrategy = UseIPIfNonMatch
config.DomainStrategy = Config_IpIfNonMatch
}
for idx, rawRule := range jsonConfig.RuleList {
rule := ParseRule(rawRule)
config.Rules[idx] = rule
config.Rule[idx] = rule
}
return config, nil
})

View File

@ -24,11 +24,13 @@ func TestDomainRule(t *testing.T) {
"outboundTag": "direct"
}`))
assert.Pointer(rule).IsNotNil()
assert.Bool(rule.Apply(v2net.TCPDestination(v2net.DomainAddress("www.ooxx.com"), 80))).IsTrue()
assert.Bool(rule.Apply(v2net.TCPDestination(v2net.DomainAddress("www.aabb.com"), 80))).IsFalse()
assert.Bool(rule.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80))).IsFalse()
assert.Bool(rule.Apply(v2net.TCPDestination(v2net.DomainAddress("www.12306.cn"), 80))).IsTrue()
assert.Bool(rule.Apply(v2net.TCPDestination(v2net.DomainAddress("www.acn.com"), 80))).IsFalse()
cond, err := rule.BuildCondition()
assert.Error(err).IsNil()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.DomainAddress("www.ooxx.com"), 80))).IsTrue()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.DomainAddress("www.aabb.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) {
@ -44,8 +46,10 @@ func TestIPRule(t *testing.T) {
"outboundTag": "direct"
}`))
assert.Pointer(rule).IsNotNil()
assert.Bool(rule.Apply(v2net.TCPDestination(v2net.DomainAddress("www.ooxx.com"), 80))).IsFalse()
assert.Bool(rule.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{10, 0, 0, 1}), 80))).IsTrue()
assert.Bool(rule.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80))).IsFalse()
assert.Bool(rule.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{192, 0, 0, 1}), 80))).IsTrue()
cond, err := rule.BuildCondition()
assert.Error(err).IsNil()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.DomainAddress("www.ooxx.com"), 80))).IsFalse()
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 {
config *RouterRuleConfig
cache *RoutingTable
dnsServer dns.Server
domainStrategy Config_DomainStrategy
rules []Rule
cache *RoutingTable
dnsServer dns.Server
}
func NewRouter(config *RouterRuleConfig, space app.Space) *Router {
func NewRouter(config *Config, space app.Space) *Router {
r := &Router{
config: config,
cache: NewRoutingTable(),
domainStrategy: config.DomainStrategy,
cache: NewRoutingTable(),
rules: make([]Rule, len(config.Rule)),
}
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) {
log.Error("DNS: Router is not found in the space.")
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) {
for _, rule := range this.config.Rules {
for _, rule := range this.rules {
if rule.Apply(dest) {
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)
ipDests := this.ResolveIP(dest)
if ipDests != nil {
for _, ipDest := range ipDests {
log.Info("Router: Trying IP ", ipDest)
for _, rule := range this.config.Rules {
for _, rule := range this.rules {
if rule.Apply(ipDest) {
return rule.Tag, nil
}
@ -97,7 +109,7 @@ type RouterFactory struct {
}
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() {

View File

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