package router import ( "context" "regexp" "strings" "sync" "time" "v2ray.com/core/common/net" "v2ray.com/core/common/protocol" "v2ray.com/core/proxy" ) type Condition interface { Apply(ctx context.Context) bool } type ConditionChan []Condition func NewConditionChan() *ConditionChan { var condChan ConditionChan = make([]Condition, 0, 8) return &condChan } func (v *ConditionChan) Add(cond Condition) *ConditionChan { *v = append(*v, cond) return v } func (v *ConditionChan) Apply(ctx context.Context) bool { for _, cond := range *v { if !cond.Apply(ctx) { return false } } return true } func (v *ConditionChan) Len() int { return len(*v) } type AnyCondition []Condition func NewAnyCondition() *AnyCondition { var anyCond AnyCondition = make([]Condition, 0, 8) return &anyCond } func (v *AnyCondition) Add(cond Condition) *AnyCondition { *v = append(*v, cond) return v } func (v *AnyCondition) Apply(ctx context.Context) bool { for _, cond := range *v { if cond.Apply(ctx) { return true } } return false } func (v *AnyCondition) Len() int { return len(*v) } type timedResult struct { timestamp time.Time result bool } type CachableDomainMatcher struct { sync.Mutex matchers []domainMatcher cache map[string]timedResult lastScan time.Time } func NewCachableDomainMatcher() *CachableDomainMatcher { return &CachableDomainMatcher{ matchers: make([]domainMatcher, 0, 64), cache: make(map[string]timedResult, 512), } } func (m *CachableDomainMatcher) Add(domain *Domain) error { switch domain.Type { case Domain_Plain: m.matchers = append(m.matchers, NewPlainDomainMatcher(domain.Value)) case Domain_Regex: rm, err := NewRegexpDomainMatcher(domain.Value) if err != nil { return err } m.matchers = append(m.matchers, rm) case Domain_Domain: m.matchers = append(m.matchers, NewSubDomainMatcher(domain.Value)) default: return newError("unknown domain type: ", domain.Type).AtWarning() } return nil } func (m *CachableDomainMatcher) applyInternal(domain string) bool { for _, matcher := range m.matchers { if matcher.Apply(domain) { return true } } return false } type cacheResult int const ( cacheMiss cacheResult = iota cacheHitTrue cacheHitFalse ) func (m *CachableDomainMatcher) findInCache(domain string) cacheResult { m.Lock() defer m.Unlock() r, f := m.cache[domain] if !f { return cacheMiss } r.timestamp = time.Now() m.cache[domain] = r if r.result { return cacheHitTrue } return cacheHitFalse } func (m *CachableDomainMatcher) ApplyDomain(domain string) bool { if len(m.matchers) < 64 { return m.applyInternal(domain) } cr := m.findInCache(domain) if cr == cacheHitTrue { return true } if cr == cacheHitFalse { return false } r := m.applyInternal(domain) m.Lock() defer m.Unlock() m.cache[domain] = timedResult{ result: r, timestamp: time.Now(), } now := time.Now() if len(m.cache) > 256 && now.Sub(m.lastScan)/time.Second > 5 { remove := make([]string, 0, 128) now := time.Now() for k, v := range m.cache { if now.Sub(v.timestamp)/time.Second > 60 { remove = append(remove, k) } } for _, v := range remove { delete(m.cache, v) } m.lastScan = now } return r } func (m *CachableDomainMatcher) Apply(ctx context.Context) bool { dest, ok := proxy.TargetFromContext(ctx) if !ok { return false } if !dest.Address.Family().IsDomain() { return false } return m.ApplyDomain(dest.Address.Domain()) } type domainMatcher interface { Apply(domain string) bool } type PlainDomainMatcher string func NewPlainDomainMatcher(pattern string) PlainDomainMatcher { return PlainDomainMatcher(pattern) } func (v PlainDomainMatcher) Apply(domain string) bool { return strings.Contains(domain, string(v)) } 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 (v *RegexpDomainMatcher) Apply(domain string) bool { return v.pattern.MatchString(strings.ToLower(domain)) } type SubDomainMatcher string func NewSubDomainMatcher(p string) SubDomainMatcher { return SubDomainMatcher(p) } func (m SubDomainMatcher) Apply(domain string) bool { pattern := string(m) if !strings.HasSuffix(domain, pattern) { return false } return len(domain) == len(pattern) || domain[len(domain)-len(pattern)-1] == '.' } type CIDRMatcher struct { cidr *net.IPNet onSource bool } func NewCIDRMatcher(ip []byte, mask uint32, onSource bool) (*CIDRMatcher, error) { cidr := &net.IPNet{ IP: net.IP(ip), Mask: net.CIDRMask(int(mask), len(ip)*8), } return &CIDRMatcher{ cidr: cidr, onSource: onSource, }, nil } func (v *CIDRMatcher) Apply(ctx context.Context) bool { ips := make([]net.IP, 0, 4) if resolver, ok := proxy.ResolvedIPsFromContext(ctx); ok { resolvedIPs := resolver.Resolve() for _, rip := range resolvedIPs { if !rip.Family().IsIPv6() { continue } ips = append(ips, rip.IP()) } } var dest net.Destination var ok bool if v.onSource { dest, ok = proxy.SourceFromContext(ctx) } else { dest, ok = proxy.TargetFromContext(ctx) } if ok && dest.Address.Family().IsIPv6() { ips = append(ips, dest.Address.IP()) } for _, ip := range ips { if v.cidr.Contains(ip) { return true } } return false } type IPv4Matcher struct { ipv4net *net.IPNetTable onSource bool } func NewIPv4Matcher(ipnet *net.IPNetTable, onSource bool) *IPv4Matcher { return &IPv4Matcher{ ipv4net: ipnet, onSource: onSource, } } func (v *IPv4Matcher) Apply(ctx context.Context) bool { ips := make([]net.IP, 0, 4) if resolver, ok := proxy.ResolvedIPsFromContext(ctx); ok { resolvedIPs := resolver.Resolve() for _, rip := range resolvedIPs { if !rip.Family().IsIPv4() { continue } ips = append(ips, rip.IP()) } } var dest net.Destination var ok bool if v.onSource { dest, ok = proxy.SourceFromContext(ctx) } else { dest, ok = proxy.TargetFromContext(ctx) } if ok && dest.Address.Family().IsIPv4() { ips = append(ips, dest.Address.IP()) } for _, ip := range ips { if v.ipv4net.Contains(ip) { return true } } return false } type PortMatcher struct { port net.PortRange } func NewPortMatcher(portRange net.PortRange) *PortMatcher { return &PortMatcher{ port: portRange, } } func (v *PortMatcher) Apply(ctx context.Context) bool { dest, ok := proxy.TargetFromContext(ctx) if !ok { return false } return v.port.Contains(dest.Port) } type NetworkMatcher struct { network *net.NetworkList } func NewNetworkMatcher(network *net.NetworkList) *NetworkMatcher { return &NetworkMatcher{ network: network, } } func (v *NetworkMatcher) Apply(ctx context.Context) bool { dest, ok := proxy.TargetFromContext(ctx) if !ok { return false } return v.network.HasNetwork(dest.Network) } type UserMatcher struct { user []string } func NewUserMatcher(users []string) *UserMatcher { usersCopy := make([]string, 0, len(users)) for _, user := range users { if len(user) > 0 { usersCopy = append(usersCopy, user) } } return &UserMatcher{ user: usersCopy, } } func (v *UserMatcher) Apply(ctx context.Context) bool { user := protocol.UserFromContext(ctx) if user == nil { return false } for _, u := range v.user { if u == user.Email { return true } } return false } type InboundTagMatcher struct { tags []string } func NewInboundTagMatcher(tags []string) *InboundTagMatcher { tagsCopy := make([]string, 0, len(tags)) for _, tag := range tags { if len(tag) > 0 { tagsCopy = append(tagsCopy, tag) } } return &InboundTagMatcher{ tags: tagsCopy, } } func (v *InboundTagMatcher) Apply(ctx context.Context) bool { tag, ok := proxy.InboundTagFromContext(ctx) if !ok { return false } for _, t := range v.tags { if t == tag { return true } } return false }