mirror of https://github.com/v2ray/v2ray-core
198 lines
4.2 KiB
Go
198 lines
4.2 KiB
Go
![]() |
package json
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
"net"
|
||
![]() |
"regexp"
|
||
![]() |
"strings"
|
||
|
|
||
|
v2net "github.com/v2ray/v2ray-core/common/net"
|
||
|
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 DomainMatcher interface {
|
||
|
Match(domain string) bool
|
||
|
}
|
||
|
|
||
|
type PlainDomainMatcher struct {
|
||
|
pattern string
|
||
|
}
|
||
|
|
||
![]() |
func NewPlainDomainMatcher(pattern string) *PlainDomainMatcher {
|
||
|
return &PlainDomainMatcher{
|
||
|
pattern: strings.ToLower(pattern),
|
||
|
}
|
||
|
}
|
||
|
|
||
![]() |
func (this *PlainDomainMatcher) Match(domain string) bool {
|
||
![]() |
return strings.Contains(this.pattern, strings.ToLower(domain))
|
||
![]() |
}
|
||
|
|
||
|
type RegexpDomainMatcher struct {
|
||
|
pattern *regexp.Regexp
|
||
|
}
|
||
|
|
||
|
func NewRegexpDomainMatcher(pattern string) (*RegexpDomainMatcher, error) {
|
||
|
r, err := regexp.Compile(pattern)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return &RegexpDomainMatcher{
|
||
|
pattern: r,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
func (this *RegexpDomainMatcher) Match(domain string) bool {
|
||
![]() |
return this.pattern.MatchString(strings.ToLower(domain))
|
||
![]() |
}
|
||
|
|
||
![]() |
type FieldRule struct {
|
||
|
Rule
|
||
![]() |
Domain []DomainMatcher
|
||
![]() |
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() {
|
||
|
return false
|
||
|
}
|
||
|
foundMatch := false
|
||
![]() |
for _, domain := range this.Domain {
|
||
|
if domain.Match(address.Domain()) {
|
||
![]() |
foundMatch = true
|
||
![]() |
break
|
||
![]() |
}
|
||
|
}
|
||
|
if !foundMatch {
|
||
![]() |
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
![]() |
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
|
||
![]() |
break
|
||
![]() |
}
|
||
|
}
|
||
|
if !foundMatch {
|
||
![]() |
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if this.Port != nil {
|
||
|
port := address.Port()
|
||
![]() |
if port.Value() < this.Port.From().Value() || port.Value() > this.Port.To().Value() {
|
||
![]() |
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if this.Network != nil {
|
||
|
if !this.Network.HasNetwork(v2net.Network(dest.Network())) {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func (this *FieldRule) UnmarshalJSON(data []byte) error {
|
||
|
type RawFieldRule struct {
|
||
|
Rule
|
||
![]() |
Domain *StringList `json:"domain"`
|
||
|
IP *StringList `json:"ip"`
|
||
![]() |
Port *v2netjson.PortRange `json:"port"`
|
||
|
Network *v2netjson.NetworkList `json:"network"`
|
||
![]() |
}
|
||
|
rawFieldRule := RawFieldRule{}
|
||
|
err := json.Unmarshal(data, &rawFieldRule)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
this.Type = rawFieldRule.Type
|
||
|
this.OutboundTag = rawFieldRule.OutboundTag
|
||
![]() |
|
||
|
hasField := false
|
||
![]() |
if rawFieldRule.Domain != nil && rawFieldRule.Domain.Len() > 0 {
|
||
![]() |
this.Domain = make([]DomainMatcher, rawFieldRule.Domain.Len())
|
||
|
for idx, rawDomain := range *(rawFieldRule.Domain) {
|
||
|
var matcher DomainMatcher
|
||
|
if strings.HasPrefix(rawDomain, "regexp:") {
|
||
|
rawMatcher, err := NewRegexpDomainMatcher(rawDomain[7:])
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
matcher = rawMatcher
|
||
|
} else {
|
||
![]() |
matcher = NewPlainDomainMatcher(rawDomain)
|
||
![]() |
}
|
||
|
this.Domain[idx] = matcher
|
||
|
}
|
||
![]() |
hasField = true
|
||
|
}
|
||
|
|
||
![]() |
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)
|
||
![]() |
}
|
||
|
hasField = true
|
||
![]() |
}
|
||
|
if rawFieldRule.Port != nil {
|
||
|
this.Port = rawFieldRule.Port
|
||
![]() |
hasField = true
|
||
![]() |
}
|
||
|
if rawFieldRule.Network != nil {
|
||
|
this.Network = rawFieldRule.Network
|
||
![]() |
hasField = true
|
||
|
}
|
||
|
if !hasField {
|
||
|
return errors.New("This rule has no effective fields.")
|
||
![]() |
}
|
||
|
return nil
|
||
|
}
|