diff --git a/app/router/rules/config/json/fieldrule.go b/app/router/rules/config/json/fieldrule.go index 575c40fcb..caeaf0a32 100644 --- a/app/router/rules/config/json/fieldrule.go +++ b/app/router/rules/config/json/fieldrule.go @@ -10,24 +10,73 @@ import ( v2netjson "github.com/v2ray/v2ray-core/common/net/json" ) +type StringList []string + +func NewStringList(str ...string) *StringList { + list := StringList(str) + return &list +} + +func (this *StringList) UnmarshalJSON(data []byte) error { + var strList []string + err := json.Unmarshal(data, &strList) + if err == nil { + *this = make([]string, len(strList)) + copy(*this, strList) + return nil + } + + var str string + err = json.Unmarshal(data, &str) + if err == nil { + *this = make([]string, 0, 1) + *this = append(*this, str) + return nil + } + + return errors.New("Failed to unmarshal string list: " + string(data)) +} + +func (this *StringList) Len() int { + return len([]string(*this)) +} + type FieldRule struct { Rule - Domain string - IP *net.IPNet + Domain *StringList + IP []*net.IPNet Port v2net.PortRange Network v2net.NetworkList } func (this *FieldRule) Apply(dest v2net.Destination) bool { address := dest.Address() - if len(this.Domain) > 0 { - if !address.IsDomain() || !strings.Contains(address.Domain(), this.Domain) { + if this.Domain != nil && this.Domain.Len() > 0 { + if !address.IsDomain() { + return false + } + foundMatch := false + for _, domain := range *this.Domain { + if strings.Contains(address.Domain(), domain) { + foundMatch = true + } + } + if !foundMatch { return false } } - if this.IP != nil { - if !(address.IsIPv4() || address.IsIPv6()) || !this.IP.Contains(address.IP()) { + if this.IP != nil && len(this.IP) > 0 { + if !(address.IsIPv4() || address.IsIPv6()) { + return false + } + foundMatch := false + for _, ipnet := range this.IP { + if ipnet.Contains(address.IP()) { + foundMatch = true + } + } + if !foundMatch { return false } } @@ -51,8 +100,8 @@ func (this *FieldRule) Apply(dest v2net.Destination) bool { func (this *FieldRule) UnmarshalJSON(data []byte) error { type RawFieldRule struct { Rule - Domain string `json:"domain"` - IP string `json:"ip"` + Domain *StringList `json:"domain"` + IP *StringList `json:"ip"` Port *v2netjson.PortRange `json:"port"` Network *v2netjson.NetworkList `json:"network"` } @@ -65,17 +114,20 @@ func (this *FieldRule) UnmarshalJSON(data []byte) error { this.OutboundTag = rawFieldRule.OutboundTag hasField := false - if len(rawFieldRule.Domain) > 0 { + if rawFieldRule.Domain != nil && rawFieldRule.Domain.Len() > 0 { this.Domain = rawFieldRule.Domain hasField = true } - if len(rawFieldRule.IP) > 0 { - _, ipNet, err := net.ParseCIDR(rawFieldRule.IP) - if err != nil { - return errors.New("Invalid IP range in router rule: " + err.Error()) + if rawFieldRule.IP != nil && rawFieldRule.IP.Len() > 0 { + this.IP = make([]*net.IPNet, 0, rawFieldRule.IP.Len()) + for _, ipStr := range *(rawFieldRule.IP) { + _, ipNet, err := net.ParseCIDR(ipStr) + if err != nil { + return errors.New("Invalid IP range in router rule: " + err.Error()) + } + this.IP = append(this.IP, ipNet) } - this.IP = ipNet hasField = true } if rawFieldRule.Port != nil { diff --git a/app/router/rules/config/json/fieldrule_test.go b/app/router/rules/config/json/fieldrule_test.go index 5caa67db3..176804dde 100644 --- a/app/router/rules/config/json/fieldrule_test.go +++ b/app/router/rules/config/json/fieldrule_test.go @@ -1,6 +1,7 @@ package json import ( + "encoding/json" "testing" v2net "github.com/v2ray/v2ray-core/common/net" @@ -8,11 +9,31 @@ import ( "github.com/v2ray/v2ray-core/testing/unit" ) +func TestStringListParsingList(t *testing.T) { + assert := unit.Assert(t) + + rawJson := `["a", "b", "c", "d"]` + var strList StringList + err := json.Unmarshal([]byte(rawJson), &strList) + assert.Error(err).IsNil() + assert.Int(strList.Len()).Equals(4) +} + +func TestStringListParsingString(t *testing.T) { + assert := unit.Assert(t) + + rawJson := `"abcd"` + var strList StringList + err := json.Unmarshal([]byte(rawJson), &strList) + assert.Error(err).IsNil() + assert.Int(strList.Len()).Equals(1) +} + func TestDomainMatching(t *testing.T) { assert := unit.Assert(t) rule := &FieldRule{ - Domain: "v2ray.com", + Domain: NewStringList("v2ray.com"), } dest := v2net.NewTCPDestination(v2net.DomainAddress("www.v2ray.com", 80)) assert.Bool(rule.Apply(dest)).IsTrue() @@ -62,10 +83,23 @@ func TestDomainNotMatching(t *testing.T) { rawJson := `{ "type": "field", - "domain": "google.com", + "domain": ["google.com", "v2ray.com"], "tag": "test" }` rule := parseRule([]byte(rawJson)) - dest := v2net.NewTCPDestination(v2net.IPAddress([]byte{10, 0, 0, 1}, 79)) + dest := v2net.NewTCPDestination(v2net.IPAddress([]byte{10, 0, 0, 1}, 80)) + assert.Bool(rule.Apply(dest)).IsFalse() +} + +func TestDomainNotMatchingDomain(t *testing.T) { + assert := unit.Assert(t) + + rawJson := `{ + "type": "field", + "domain": ["google.com", "v2ray.com"], + "tag": "test" + }` + rule := parseRule([]byte(rawJson)) + dest := v2net.NewTCPDestination(v2net.DomainAddress("baidu.com", 80)) assert.Bool(rule.Apply(dest)).IsFalse() } diff --git a/release/config/vpoint_socks_vmess.json b/release/config/vpoint_socks_vmess.json index cc43d31eb..e443682b8 100644 --- a/release/config/vpoint_socks_vmess.json +++ b/release/config/vpoint_socks_vmess.json @@ -39,77 +39,23 @@ "rules": [ { "type": "field", - "ip": "0.0.0.0/8", - "outboundTag": "direct" - }, - { - "type": "field", - "ip": "10.0.0.0/8", - "outboundTag": "direct" - }, - { - "type": "field", - "ip": "100.64.0.0/10", - "outboundTag": "direct" - }, - { - "type": "field", - "ip": "127.0.0.0/8", - "outboundTag": "direct" - }, - { - "type": "field", - "ip": "169.254.0.0/16", - "outboundTag": "direct" - }, - { - "type": "field", - "ip": "172.16.0.0/12", - "outboundTag": "direct" - }, - { - "type": "field", - "ip": "192.0.0.0/24", - "outboundTag": "direct" - }, - { - "type": "field", - "ip": "192.0.2.0/24", - "outboundTag": "direct" - }, - { - "type": "field", - "ip": "192.168.0.0/16", - "outboundTag": "direct" - }, - { - "type": "field", - "ip": "198.18.0.0/15", - "outboundTag": "direct" - }, - { - "type": "field", - "ip": "198.51.100.0/24", - "outboundTag": "direct" - }, - { - "type": "field", - "ip": "203.0.113.0/24", - "outboundTag": "direct" - }, - { - "type": "field", - "ip": "::1/128", - "outboundTag": "direct" - }, - { - "type": "field", - "ip": "fc00::/7", - "outboundTag": "direct" - }, - { - "type": "field", - "ip": "fe80::/10", + "ip": [ + "0.0.0.0/8", + "10.0.0.0/8", + "100.64.0.0/10", + "127.0.0.0/8", + "169.254.0.0/16", + "172.16.0.0/12", + "192.0.0.0/24", + "192.0.2.0/24", + "192.168.0.0/16", + "198.18.0.0/15", + "198.51.100.0/24", + "203.0.113.0/24", + "::1/128", + "fc00::/7", + "fe80::/10" + ], "outboundTag": "direct" } ]