First Commit
This commit is contained in:
18
ruleset/builtins/cidr.go
Normal file
18
ruleset/builtins/cidr.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func MatchCIDR(ip string, cidr *net.IPNet) bool {
|
||||
ipAddr := net.ParseIP(ip)
|
||||
if ipAddr == nil {
|
||||
return false
|
||||
}
|
||||
return cidr.Contains(ipAddr)
|
||||
}
|
||||
|
||||
func CompileCIDR(cidr string) (*net.IPNet, error) {
|
||||
_, ipNet, err := net.ParseCIDR(cidr)
|
||||
return ipNet, err
|
||||
}
|
||||
128
ruleset/builtins/geo/geo_loader.go
Normal file
128
ruleset/builtins/geo/geo_loader.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package geo
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"git.difuse.io/Difuse/Mellaris/ruleset/builtins/geo/v2geo"
|
||||
)
|
||||
|
||||
const (
|
||||
geoipFilename = "geoip.dat"
|
||||
geoipURL = "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geoip.dat"
|
||||
geositeFilename = "geosite.dat"
|
||||
geositeURL = "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat"
|
||||
|
||||
geoDefaultUpdateInterval = 7 * 24 * time.Hour // 7 days
|
||||
)
|
||||
|
||||
var _ GeoLoader = (*V2GeoLoader)(nil)
|
||||
|
||||
// V2GeoLoader provides the on-demand GeoIP/MatchGeoSite database
|
||||
// loading functionality required by the ACL engine.
|
||||
// Empty filenames = automatic download from built-in URLs.
|
||||
type V2GeoLoader struct {
|
||||
GeoIPFilename string
|
||||
GeoSiteFilename string
|
||||
UpdateInterval time.Duration
|
||||
|
||||
DownloadFunc func(filename, url string)
|
||||
DownloadErrFunc func(err error)
|
||||
|
||||
geoipMap map[string]*v2geo.GeoIP
|
||||
geositeMap map[string]*v2geo.GeoSite
|
||||
}
|
||||
|
||||
func NewDefaultGeoLoader(geoSiteFilename, geoIpFilename string) *V2GeoLoader {
|
||||
return &V2GeoLoader{
|
||||
GeoIPFilename: geoIpFilename,
|
||||
GeoSiteFilename: geoSiteFilename,
|
||||
DownloadFunc: func(filename, url string) {},
|
||||
DownloadErrFunc: func(err error) {},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *V2GeoLoader) shouldDownload(filename string) bool {
|
||||
info, err := os.Stat(filename)
|
||||
if os.IsNotExist(err) {
|
||||
return true
|
||||
}
|
||||
dt := time.Since(info.ModTime())
|
||||
if l.UpdateInterval == 0 {
|
||||
return dt > geoDefaultUpdateInterval
|
||||
} else {
|
||||
return dt > l.UpdateInterval
|
||||
}
|
||||
}
|
||||
|
||||
func (l *V2GeoLoader) download(filename, url string) error {
|
||||
l.DownloadFunc(filename, url)
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
l.DownloadErrFunc(err)
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
l.DownloadErrFunc(err)
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = io.Copy(f, resp.Body)
|
||||
l.DownloadErrFunc(err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (l *V2GeoLoader) LoadGeoIP() (map[string]*v2geo.GeoIP, error) {
|
||||
if l.geoipMap != nil {
|
||||
return l.geoipMap, nil
|
||||
}
|
||||
autoDL := false
|
||||
filename := l.GeoIPFilename
|
||||
if filename == "" {
|
||||
autoDL = true
|
||||
filename = geoipFilename
|
||||
}
|
||||
if autoDL && l.shouldDownload(filename) {
|
||||
err := l.download(filename, geoipURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
m, err := v2geo.LoadGeoIP(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.geoipMap = m
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (l *V2GeoLoader) LoadGeoSite() (map[string]*v2geo.GeoSite, error) {
|
||||
if l.geositeMap != nil {
|
||||
return l.geositeMap, nil
|
||||
}
|
||||
autoDL := false
|
||||
filename := l.GeoSiteFilename
|
||||
if filename == "" {
|
||||
autoDL = true
|
||||
filename = geositeFilename
|
||||
}
|
||||
if autoDL && l.shouldDownload(filename) {
|
||||
err := l.download(filename, geositeURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
m, err := v2geo.LoadGeoSite(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.geositeMap = m
|
||||
return m, nil
|
||||
}
|
||||
113
ruleset/builtins/geo/geo_matcher.go
Normal file
113
ruleset/builtins/geo/geo_matcher.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package geo
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type GeoMatcher struct {
|
||||
geoLoader GeoLoader
|
||||
geoSiteMatcher map[string]hostMatcher
|
||||
siteMatcherLock sync.Mutex
|
||||
geoIpMatcher map[string]hostMatcher
|
||||
ipMatcherLock sync.Mutex
|
||||
}
|
||||
|
||||
func NewGeoMatcher(geoSiteFilename, geoIpFilename string) *GeoMatcher {
|
||||
return &GeoMatcher{
|
||||
geoLoader: NewDefaultGeoLoader(geoSiteFilename, geoIpFilename),
|
||||
geoSiteMatcher: make(map[string]hostMatcher),
|
||||
geoIpMatcher: make(map[string]hostMatcher),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GeoMatcher) MatchGeoIp(ip, condition string) bool {
|
||||
g.ipMatcherLock.Lock()
|
||||
defer g.ipMatcherLock.Unlock()
|
||||
|
||||
matcher, ok := g.geoIpMatcher[condition]
|
||||
if !ok {
|
||||
// GeoIP matcher
|
||||
condition = strings.ToLower(condition)
|
||||
country := condition
|
||||
if len(country) == 0 {
|
||||
return false
|
||||
}
|
||||
gMap, err := g.geoLoader.LoadGeoIP()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
list, ok := gMap[country]
|
||||
if !ok || list == nil {
|
||||
return false
|
||||
}
|
||||
matcher, err = newGeoIPMatcher(list)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
g.geoIpMatcher[condition] = matcher
|
||||
}
|
||||
parseIp := net.ParseIP(ip)
|
||||
if parseIp == nil {
|
||||
return false
|
||||
}
|
||||
ipv4 := parseIp.To4()
|
||||
if ipv4 != nil {
|
||||
return matcher.Match(HostInfo{IPv4: ipv4})
|
||||
}
|
||||
ipv6 := parseIp.To16()
|
||||
if ipv6 != nil {
|
||||
return matcher.Match(HostInfo{IPv6: ipv6})
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (g *GeoMatcher) MatchGeoSite(site, condition string) bool {
|
||||
g.siteMatcherLock.Lock()
|
||||
defer g.siteMatcherLock.Unlock()
|
||||
|
||||
matcher, ok := g.geoSiteMatcher[condition]
|
||||
if !ok {
|
||||
// MatchGeoSite matcher
|
||||
condition = strings.ToLower(condition)
|
||||
name, attrs := parseGeoSiteName(condition)
|
||||
if len(name) == 0 {
|
||||
return false
|
||||
}
|
||||
gMap, err := g.geoLoader.LoadGeoSite()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
list, ok := gMap[name]
|
||||
if !ok || list == nil {
|
||||
return false
|
||||
}
|
||||
matcher, err = newGeositeMatcher(list, attrs)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
g.geoSiteMatcher[condition] = matcher
|
||||
}
|
||||
return matcher.Match(HostInfo{Name: site})
|
||||
}
|
||||
|
||||
func (g *GeoMatcher) LoadGeoSite() error {
|
||||
_, err := g.geoLoader.LoadGeoSite()
|
||||
return err
|
||||
}
|
||||
|
||||
func (g *GeoMatcher) LoadGeoIP() error {
|
||||
_, err := g.geoLoader.LoadGeoIP()
|
||||
return err
|
||||
}
|
||||
|
||||
func parseGeoSiteName(s string) (string, []string) {
|
||||
parts := strings.Split(s, "@")
|
||||
base := strings.TrimSpace(parts[0])
|
||||
attrs := parts[1:]
|
||||
for i := range attrs {
|
||||
attrs[i] = strings.TrimSpace(attrs[i])
|
||||
}
|
||||
return base, attrs
|
||||
}
|
||||
27
ruleset/builtins/geo/interface.go
Normal file
27
ruleset/builtins/geo/interface.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package geo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"git.difuse.io/Difuse/Mellaris/ruleset/builtins/geo/v2geo"
|
||||
)
|
||||
|
||||
type HostInfo struct {
|
||||
Name string
|
||||
IPv4 net.IP
|
||||
IPv6 net.IP
|
||||
}
|
||||
|
||||
func (h HostInfo) String() string {
|
||||
return fmt.Sprintf("%s|%s|%s", h.Name, h.IPv4, h.IPv6)
|
||||
}
|
||||
|
||||
type GeoLoader interface {
|
||||
LoadGeoIP() (map[string]*v2geo.GeoIP, error)
|
||||
LoadGeoSite() (map[string]*v2geo.GeoSite, error)
|
||||
}
|
||||
|
||||
type hostMatcher interface {
|
||||
Match(HostInfo) bool
|
||||
}
|
||||
213
ruleset/builtins/geo/matchers_v2geo.go
Normal file
213
ruleset/builtins/geo/matchers_v2geo.go
Normal file
@@ -0,0 +1,213 @@
|
||||
package geo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"net"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.difuse.io/Difuse/Mellaris/ruleset/builtins/geo/v2geo"
|
||||
)
|
||||
|
||||
var _ hostMatcher = (*geoipMatcher)(nil)
|
||||
|
||||
type geoipMatcher struct {
|
||||
N4 []*net.IPNet // sorted
|
||||
N6 []*net.IPNet // sorted
|
||||
Inverse bool
|
||||
}
|
||||
|
||||
// matchIP tries to match the given IP address with the corresponding IPNets.
|
||||
// Note that this function does NOT handle the Inverse flag.
|
||||
func (m *geoipMatcher) matchIP(ip net.IP) bool {
|
||||
var n []*net.IPNet
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
// N4 stores IPv4 addresses in 4-byte form.
|
||||
// Make sure we use it here too, otherwise bytes.Compare will fail.
|
||||
ip = ip4
|
||||
n = m.N4
|
||||
} else {
|
||||
n = m.N6
|
||||
}
|
||||
left, right := 0, len(n)-1
|
||||
for left <= right {
|
||||
mid := (left + right) / 2
|
||||
if n[mid].Contains(ip) {
|
||||
return true
|
||||
} else if bytes.Compare(n[mid].IP, ip) < 0 {
|
||||
left = mid + 1
|
||||
} else {
|
||||
right = mid - 1
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *geoipMatcher) Match(host HostInfo) bool {
|
||||
if host.IPv4 != nil {
|
||||
if m.matchIP(host.IPv4) {
|
||||
return !m.Inverse
|
||||
}
|
||||
}
|
||||
if host.IPv6 != nil {
|
||||
if m.matchIP(host.IPv6) {
|
||||
return !m.Inverse
|
||||
}
|
||||
}
|
||||
return m.Inverse
|
||||
}
|
||||
|
||||
func newGeoIPMatcher(list *v2geo.GeoIP) (*geoipMatcher, error) {
|
||||
n4 := make([]*net.IPNet, 0)
|
||||
n6 := make([]*net.IPNet, 0)
|
||||
for _, cidr := range list.Cidr {
|
||||
if len(cidr.Ip) == 4 {
|
||||
// IPv4
|
||||
n4 = append(n4, &net.IPNet{
|
||||
IP: cidr.Ip,
|
||||
Mask: net.CIDRMask(int(cidr.Prefix), 32),
|
||||
})
|
||||
} else if len(cidr.Ip) == 16 {
|
||||
// IPv6
|
||||
n6 = append(n6, &net.IPNet{
|
||||
IP: cidr.Ip,
|
||||
Mask: net.CIDRMask(int(cidr.Prefix), 128),
|
||||
})
|
||||
} else {
|
||||
return nil, errors.New("invalid IP length")
|
||||
}
|
||||
}
|
||||
// Sort the IPNets, so we can do binary search later.
|
||||
sort.Slice(n4, func(i, j int) bool {
|
||||
return bytes.Compare(n4[i].IP, n4[j].IP) < 0
|
||||
})
|
||||
sort.Slice(n6, func(i, j int) bool {
|
||||
return bytes.Compare(n6[i].IP, n6[j].IP) < 0
|
||||
})
|
||||
return &geoipMatcher{
|
||||
N4: n4,
|
||||
N6: n6,
|
||||
Inverse: list.InverseMatch,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var _ hostMatcher = (*geositeMatcher)(nil)
|
||||
|
||||
type geositeDomainType int
|
||||
|
||||
const (
|
||||
geositeDomainPlain geositeDomainType = iota
|
||||
geositeDomainRegex
|
||||
geositeDomainRoot
|
||||
geositeDomainFull
|
||||
)
|
||||
|
||||
type geositeDomain struct {
|
||||
Type geositeDomainType
|
||||
Value string
|
||||
Regex *regexp.Regexp
|
||||
Attrs map[string]bool
|
||||
}
|
||||
|
||||
type geositeMatcher struct {
|
||||
Domains []geositeDomain
|
||||
// Attributes are matched using "and" logic - if you have multiple attributes here,
|
||||
// a domain must have all of those attributes to be considered a match.
|
||||
Attrs []string
|
||||
}
|
||||
|
||||
func (m *geositeMatcher) matchDomain(domain geositeDomain, host HostInfo) bool {
|
||||
// Match attributes first
|
||||
if len(m.Attrs) > 0 {
|
||||
if len(domain.Attrs) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, attr := range m.Attrs {
|
||||
if !domain.Attrs[attr] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch domain.Type {
|
||||
case geositeDomainPlain:
|
||||
return strings.Contains(host.Name, domain.Value)
|
||||
case geositeDomainRegex:
|
||||
if domain.Regex != nil {
|
||||
return domain.Regex.MatchString(host.Name)
|
||||
}
|
||||
case geositeDomainFull:
|
||||
return host.Name == domain.Value
|
||||
case geositeDomainRoot:
|
||||
if host.Name == domain.Value {
|
||||
return true
|
||||
}
|
||||
return strings.HasSuffix(host.Name, "."+domain.Value)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *geositeMatcher) Match(host HostInfo) bool {
|
||||
for _, domain := range m.Domains {
|
||||
if m.matchDomain(domain, host) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func newGeositeMatcher(list *v2geo.GeoSite, attrs []string) (*geositeMatcher, error) {
|
||||
domains := make([]geositeDomain, len(list.Domain))
|
||||
for i, domain := range list.Domain {
|
||||
switch domain.Type {
|
||||
case v2geo.Domain_Plain:
|
||||
domains[i] = geositeDomain{
|
||||
Type: geositeDomainPlain,
|
||||
Value: domain.Value,
|
||||
Attrs: domainAttributeToMap(domain.Attribute),
|
||||
}
|
||||
case v2geo.Domain_Regex:
|
||||
regex, err := regexp.Compile(domain.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
domains[i] = geositeDomain{
|
||||
Type: geositeDomainRegex,
|
||||
Regex: regex,
|
||||
Attrs: domainAttributeToMap(domain.Attribute),
|
||||
}
|
||||
case v2geo.Domain_Full:
|
||||
domains[i] = geositeDomain{
|
||||
Type: geositeDomainFull,
|
||||
Value: domain.Value,
|
||||
Attrs: domainAttributeToMap(domain.Attribute),
|
||||
}
|
||||
case v2geo.Domain_RootDomain:
|
||||
domains[i] = geositeDomain{
|
||||
Type: geositeDomainRoot,
|
||||
Value: domain.Value,
|
||||
Attrs: domainAttributeToMap(domain.Attribute),
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("unsupported domain type")
|
||||
}
|
||||
}
|
||||
return &geositeMatcher{
|
||||
Domains: domains,
|
||||
Attrs: attrs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func domainAttributeToMap(attrs []*v2geo.Domain_Attribute) map[string]bool {
|
||||
m := make(map[string]bool)
|
||||
for _, attr := range attrs {
|
||||
// Supposedly there are also int attributes,
|
||||
// but nobody seems to use them, so we treat everything as boolean for now.
|
||||
m[attr.Key] = true
|
||||
}
|
||||
return m
|
||||
}
|
||||
44
ruleset/builtins/geo/v2geo/load.go
Normal file
44
ruleset/builtins/geo/v2geo/load.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package v2geo
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// LoadGeoIP loads a GeoIP data file and converts it to a map.
|
||||
// The keys of the map (country codes) are all normalized to lowercase.
|
||||
func LoadGeoIP(filename string) (map[string]*GeoIP, error) {
|
||||
bs, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var list GeoIPList
|
||||
if err := proto.Unmarshal(bs, &list); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := make(map[string]*GeoIP)
|
||||
for _, entry := range list.Entry {
|
||||
m[strings.ToLower(entry.CountryCode)] = entry
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// LoadGeoSite loads a GeoSite data file and converts it to a map.
|
||||
// The keys of the map (site keys) are all normalized to lowercase.
|
||||
func LoadGeoSite(filename string) (map[string]*GeoSite, error) {
|
||||
bs, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var list GeoSiteList
|
||||
if err := proto.Unmarshal(bs, &list); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := make(map[string]*GeoSite)
|
||||
for _, entry := range list.Entry {
|
||||
m[strings.ToLower(entry.CountryCode)] = entry
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
745
ruleset/builtins/geo/v2geo/v2geo.pb.go
Normal file
745
ruleset/builtins/geo/v2geo/v2geo.pb.go
Normal file
@@ -0,0 +1,745 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc v4.24.4
|
||||
// source: v2geo.proto
|
||||
|
||||
package v2geo
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// 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
|
||||
// The value is a root domain.
|
||||
Domain_RootDomain Domain_Type = 2
|
||||
// The value is a domain.
|
||||
Domain_Full Domain_Type = 3
|
||||
)
|
||||
|
||||
// Enum value maps for Domain_Type.
|
||||
var (
|
||||
Domain_Type_name = map[int32]string{
|
||||
0: "Plain",
|
||||
1: "Regex",
|
||||
2: "RootDomain",
|
||||
3: "Full",
|
||||
}
|
||||
Domain_Type_value = map[string]int32{
|
||||
"Plain": 0,
|
||||
"Regex": 1,
|
||||
"RootDomain": 2,
|
||||
"Full": 3,
|
||||
}
|
||||
)
|
||||
|
||||
func (x Domain_Type) Enum() *Domain_Type {
|
||||
p := new(Domain_Type)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x Domain_Type) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (Domain_Type) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_v2geo_proto_enumTypes[0].Descriptor()
|
||||
}
|
||||
|
||||
func (Domain_Type) Type() protoreflect.EnumType {
|
||||
return &file_v2geo_proto_enumTypes[0]
|
||||
}
|
||||
|
||||
func (x Domain_Type) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Domain_Type.Descriptor instead.
|
||||
func (Domain_Type) EnumDescriptor() ([]byte, []int) {
|
||||
return file_v2geo_proto_rawDescGZIP(), []int{0, 0}
|
||||
}
|
||||
|
||||
// Domain for routing decision.
|
||||
type Domain struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Domain matching type.
|
||||
Type Domain_Type `protobuf:"varint,1,opt,name=type,proto3,enum=Domain_Type" json:"type,omitempty"`
|
||||
// Domain value.
|
||||
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
|
||||
// Attributes of this domain. May be used for filtering.
|
||||
Attribute []*Domain_Attribute `protobuf:"bytes,3,rep,name=attribute,proto3" json:"attribute,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Domain) Reset() {
|
||||
*x = Domain{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_v2geo_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Domain) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Domain) ProtoMessage() {}
|
||||
|
||||
func (x *Domain) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v2geo_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Domain.ProtoReflect.Descriptor instead.
|
||||
func (*Domain) Descriptor() ([]byte, []int) {
|
||||
return file_v2geo_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Domain) GetType() Domain_Type {
|
||||
if x != nil {
|
||||
return x.Type
|
||||
}
|
||||
return Domain_Plain
|
||||
}
|
||||
|
||||
func (x *Domain) GetValue() string {
|
||||
if x != nil {
|
||||
return x.Value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Domain) GetAttribute() []*Domain_Attribute {
|
||||
if x != nil {
|
||||
return x.Attribute
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IP for routing decision, in CIDR form.
|
||||
type CIDR struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// IP address, should be either 4 or 16 bytes.
|
||||
Ip []byte `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"`
|
||||
// Number of leading ones in the network mask.
|
||||
Prefix uint32 `protobuf:"varint,2,opt,name=prefix,proto3" json:"prefix,omitempty"`
|
||||
}
|
||||
|
||||
func (x *CIDR) Reset() {
|
||||
*x = CIDR{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_v2geo_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *CIDR) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CIDR) ProtoMessage() {}
|
||||
|
||||
func (x *CIDR) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v2geo_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CIDR.ProtoReflect.Descriptor instead.
|
||||
func (*CIDR) Descriptor() ([]byte, []int) {
|
||||
return file_v2geo_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *CIDR) GetIp() []byte {
|
||||
if x != nil {
|
||||
return x.Ip
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CIDR) GetPrefix() uint32 {
|
||||
if x != nil {
|
||||
return x.Prefix
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type GeoIP struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
CountryCode string `protobuf:"bytes,1,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"`
|
||||
Cidr []*CIDR `protobuf:"bytes,2,rep,name=cidr,proto3" json:"cidr,omitempty"`
|
||||
InverseMatch bool `protobuf:"varint,3,opt,name=inverse_match,json=inverseMatch,proto3" json:"inverse_match,omitempty"`
|
||||
// resource_hash instruct simplified config converter to load domain from geo file.
|
||||
ResourceHash []byte `protobuf:"bytes,4,opt,name=resource_hash,json=resourceHash,proto3" json:"resource_hash,omitempty"`
|
||||
Code string `protobuf:"bytes,5,opt,name=code,proto3" json:"code,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GeoIP) Reset() {
|
||||
*x = GeoIP{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_v2geo_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GeoIP) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GeoIP) ProtoMessage() {}
|
||||
|
||||
func (x *GeoIP) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v2geo_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GeoIP.ProtoReflect.Descriptor instead.
|
||||
func (*GeoIP) Descriptor() ([]byte, []int) {
|
||||
return file_v2geo_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *GeoIP) GetCountryCode() string {
|
||||
if x != nil {
|
||||
return x.CountryCode
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GeoIP) GetCidr() []*CIDR {
|
||||
if x != nil {
|
||||
return x.Cidr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *GeoIP) GetInverseMatch() bool {
|
||||
if x != nil {
|
||||
return x.InverseMatch
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *GeoIP) GetResourceHash() []byte {
|
||||
if x != nil {
|
||||
return x.ResourceHash
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *GeoIP) GetCode() string {
|
||||
if x != nil {
|
||||
return x.Code
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type GeoIPList struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Entry []*GeoIP `protobuf:"bytes,1,rep,name=entry,proto3" json:"entry,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GeoIPList) Reset() {
|
||||
*x = GeoIPList{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_v2geo_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GeoIPList) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GeoIPList) ProtoMessage() {}
|
||||
|
||||
func (x *GeoIPList) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v2geo_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GeoIPList.ProtoReflect.Descriptor instead.
|
||||
func (*GeoIPList) Descriptor() ([]byte, []int) {
|
||||
return file_v2geo_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *GeoIPList) GetEntry() []*GeoIP {
|
||||
if x != nil {
|
||||
return x.Entry
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GeoSite struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
CountryCode string `protobuf:"bytes,1,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"`
|
||||
Domain []*Domain `protobuf:"bytes,2,rep,name=domain,proto3" json:"domain,omitempty"`
|
||||
// resource_hash instruct simplified config converter to load domain from geo file.
|
||||
ResourceHash []byte `protobuf:"bytes,3,opt,name=resource_hash,json=resourceHash,proto3" json:"resource_hash,omitempty"`
|
||||
Code string `protobuf:"bytes,4,opt,name=code,proto3" json:"code,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GeoSite) Reset() {
|
||||
*x = GeoSite{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_v2geo_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GeoSite) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GeoSite) ProtoMessage() {}
|
||||
|
||||
func (x *GeoSite) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v2geo_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use MatchGeoSite.ProtoReflect.Descriptor instead.
|
||||
func (*GeoSite) Descriptor() ([]byte, []int) {
|
||||
return file_v2geo_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *GeoSite) GetCountryCode() string {
|
||||
if x != nil {
|
||||
return x.CountryCode
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GeoSite) GetDomain() []*Domain {
|
||||
if x != nil {
|
||||
return x.Domain
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *GeoSite) GetResourceHash() []byte {
|
||||
if x != nil {
|
||||
return x.ResourceHash
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *GeoSite) GetCode() string {
|
||||
if x != nil {
|
||||
return x.Code
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type GeoSiteList struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Entry []*GeoSite `protobuf:"bytes,1,rep,name=entry,proto3" json:"entry,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GeoSiteList) Reset() {
|
||||
*x = GeoSiteList{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_v2geo_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GeoSiteList) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GeoSiteList) ProtoMessage() {}
|
||||
|
||||
func (x *GeoSiteList) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v2geo_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GeoSiteList.ProtoReflect.Descriptor instead.
|
||||
func (*GeoSiteList) Descriptor() ([]byte, []int) {
|
||||
return file_v2geo_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *GeoSiteList) GetEntry() []*GeoSite {
|
||||
if x != nil {
|
||||
return x.Entry
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Domain_Attribute struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
// Types that are assignable to TypedValue:
|
||||
//
|
||||
// *Domain_Attribute_BoolValue
|
||||
// *Domain_Attribute_IntValue
|
||||
TypedValue isDomain_Attribute_TypedValue `protobuf_oneof:"typed_value"`
|
||||
}
|
||||
|
||||
func (x *Domain_Attribute) Reset() {
|
||||
*x = Domain_Attribute{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_v2geo_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Domain_Attribute) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Domain_Attribute) ProtoMessage() {}
|
||||
|
||||
func (x *Domain_Attribute) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v2geo_proto_msgTypes[6]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Domain_Attribute.ProtoReflect.Descriptor instead.
|
||||
func (*Domain_Attribute) Descriptor() ([]byte, []int) {
|
||||
return file_v2geo_proto_rawDescGZIP(), []int{0, 0}
|
||||
}
|
||||
|
||||
func (x *Domain_Attribute) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Domain_Attribute) GetTypedValue() isDomain_Attribute_TypedValue {
|
||||
if m != nil {
|
||||
return m.TypedValue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Domain_Attribute) GetBoolValue() bool {
|
||||
if x, ok := x.GetTypedValue().(*Domain_Attribute_BoolValue); ok {
|
||||
return x.BoolValue
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *Domain_Attribute) GetIntValue() int64 {
|
||||
if x, ok := x.GetTypedValue().(*Domain_Attribute_IntValue); ok {
|
||||
return x.IntValue
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type isDomain_Attribute_TypedValue interface {
|
||||
isDomain_Attribute_TypedValue()
|
||||
}
|
||||
|
||||
type Domain_Attribute_BoolValue struct {
|
||||
BoolValue bool `protobuf:"varint,2,opt,name=bool_value,json=boolValue,proto3,oneof"`
|
||||
}
|
||||
|
||||
type Domain_Attribute_IntValue struct {
|
||||
IntValue int64 `protobuf:"varint,3,opt,name=int_value,json=intValue,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*Domain_Attribute_BoolValue) isDomain_Attribute_TypedValue() {}
|
||||
|
||||
func (*Domain_Attribute_IntValue) isDomain_Attribute_TypedValue() {}
|
||||
|
||||
var File_v2geo_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_v2geo_proto_rawDesc = []byte{
|
||||
0x0a, 0x0b, 0x76, 0x32, 0x67, 0x65, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x97, 0x02,
|
||||
0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x20, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0c, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e,
|
||||
0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x12, 0x2f, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x03, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x41, 0x74, 0x74,
|
||||
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
|
||||
0x65, 0x1a, 0x6c, 0x0a, 0x09, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x10,
|
||||
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
|
||||
0x12, 0x1f, 0x0a, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x12, 0x1d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x42, 0x0d, 0x0a, 0x0b, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22,
|
||||
0x36, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x6c, 0x61, 0x69, 0x6e,
|
||||
0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x01, 0x12, 0x0e, 0x0a,
|
||||
0x0a, 0x52, 0x6f, 0x6f, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0x02, 0x12, 0x08, 0x0a,
|
||||
0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x03, 0x22, 0x2e, 0x0a, 0x04, 0x43, 0x49, 0x44, 0x52, 0x12,
|
||||
0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12,
|
||||
0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52,
|
||||
0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0xa3, 0x01, 0x0a, 0x05, 0x47, 0x65, 0x6f, 0x49,
|
||||
0x50, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x64,
|
||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79,
|
||||
0x43, 0x6f, 0x64, 0x65, 0x12, 0x19, 0x0a, 0x04, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x05, 0x2e, 0x43, 0x49, 0x44, 0x52, 0x52, 0x04, 0x63, 0x69, 0x64, 0x72, 0x12,
|
||||
0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x65, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x65, 0x4d,
|
||||
0x61, 0x74, 0x63, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x72, 0x65, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64,
|
||||
0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x29, 0x0a,
|
||||
0x09, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x05, 0x65, 0x6e,
|
||||
0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x06, 0x2e, 0x47, 0x65, 0x6f, 0x49,
|
||||
0x50, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x86, 0x01, 0x0a, 0x07, 0x47, 0x65, 0x6f,
|
||||
0x53, 0x69, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x5f,
|
||||
0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x75, 0x6e,
|
||||
0x74, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1f, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69,
|
||||
0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||
0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||
0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64,
|
||||
0x65, 0x22, 0x2d, 0x0a, 0x0b, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74,
|
||||
0x12, 0x1e, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x08, 0x2e, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79,
|
||||
0x42, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x76, 0x32, 0x67, 0x65, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_v2geo_proto_rawDescOnce sync.Once
|
||||
file_v2geo_proto_rawDescData = file_v2geo_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_v2geo_proto_rawDescGZIP() []byte {
|
||||
file_v2geo_proto_rawDescOnce.Do(func() {
|
||||
file_v2geo_proto_rawDescData = protoimpl.X.CompressGZIP(file_v2geo_proto_rawDescData)
|
||||
})
|
||||
return file_v2geo_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_v2geo_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_v2geo_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||
var file_v2geo_proto_goTypes = []interface{}{
|
||||
(Domain_Type)(0), // 0: Domain.Type
|
||||
(*Domain)(nil), // 1: Domain
|
||||
(*CIDR)(nil), // 2: CIDR
|
||||
(*GeoIP)(nil), // 3: GeoIP
|
||||
(*GeoIPList)(nil), // 4: GeoIPList
|
||||
(*GeoSite)(nil), // 5: MatchGeoSite
|
||||
(*GeoSiteList)(nil), // 6: GeoSiteList
|
||||
(*Domain_Attribute)(nil), // 7: Domain.Attribute
|
||||
}
|
||||
var file_v2geo_proto_depIdxs = []int32{
|
||||
0, // 0: Domain.type:type_name -> Domain.Type
|
||||
7, // 1: Domain.attribute:type_name -> Domain.Attribute
|
||||
2, // 2: GeoIP.cidr:type_name -> CIDR
|
||||
3, // 3: GeoIPList.entry:type_name -> GeoIP
|
||||
1, // 4: MatchGeoSite.domain:type_name -> Domain
|
||||
5, // 5: GeoSiteList.entry:type_name -> MatchGeoSite
|
||||
6, // [6:6] is the sub-list for method output_type
|
||||
6, // [6:6] is the sub-list for method input_type
|
||||
6, // [6:6] is the sub-list for extension type_name
|
||||
6, // [6:6] is the sub-list for extension extendee
|
||||
0, // [0:6] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_v2geo_proto_init() }
|
||||
func file_v2geo_proto_init() {
|
||||
if File_v2geo_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_v2geo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Domain); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_v2geo_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*CIDR); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_v2geo_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GeoIP); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_v2geo_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GeoIPList); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_v2geo_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GeoSite); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_v2geo_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GeoSiteList); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_v2geo_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Domain_Attribute); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_v2geo_proto_msgTypes[6].OneofWrappers = []interface{}{
|
||||
(*Domain_Attribute_BoolValue)(nil),
|
||||
(*Domain_Attribute_IntValue)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_v2geo_proto_rawDesc,
|
||||
NumEnums: 1,
|
||||
NumMessages: 7,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_v2geo_proto_goTypes,
|
||||
DependencyIndexes: file_v2geo_proto_depIdxs,
|
||||
EnumInfos: file_v2geo_proto_enumTypes,
|
||||
MessageInfos: file_v2geo_proto_msgTypes,
|
||||
}.Build()
|
||||
File_v2geo_proto = out.File
|
||||
file_v2geo_proto_rawDesc = nil
|
||||
file_v2geo_proto_goTypes = nil
|
||||
file_v2geo_proto_depIdxs = nil
|
||||
}
|
||||
76
ruleset/builtins/geo/v2geo/v2geo.proto
Normal file
76
ruleset/builtins/geo/v2geo/v2geo.proto
Normal file
@@ -0,0 +1,76 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option go_package = "./v2geo";
|
||||
|
||||
// This file is copied from
|
||||
// https://github.com/v2fly/v2ray-core/blob/master/app/router/routercommon/common.proto
|
||||
// with some modifications.
|
||||
|
||||
// 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;
|
||||
// The value is a root domain.
|
||||
RootDomain = 2;
|
||||
// The value is a domain.
|
||||
Full = 3;
|
||||
}
|
||||
|
||||
// Domain matching type.
|
||||
Type type = 1;
|
||||
|
||||
// Domain value.
|
||||
string value = 2;
|
||||
|
||||
message Attribute {
|
||||
string key = 1;
|
||||
|
||||
oneof typed_value {
|
||||
bool bool_value = 2;
|
||||
int64 int_value = 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Attributes of this domain. May be used for filtering.
|
||||
repeated Attribute attribute = 3;
|
||||
}
|
||||
|
||||
// IP for routing decision, in CIDR form.
|
||||
message CIDR {
|
||||
// IP address, should be either 4 or 16 bytes.
|
||||
bytes ip = 1;
|
||||
|
||||
// Number of leading ones in the network mask.
|
||||
uint32 prefix = 2;
|
||||
}
|
||||
|
||||
message GeoIP {
|
||||
string country_code = 1;
|
||||
repeated CIDR cidr = 2;
|
||||
bool inverse_match = 3;
|
||||
|
||||
// resource_hash instruct simplified config converter to load domain from geo file.
|
||||
bytes resource_hash = 4;
|
||||
string code = 5;
|
||||
}
|
||||
|
||||
message GeoIPList {
|
||||
repeated GeoIP entry = 1;
|
||||
}
|
||||
|
||||
message GeoSite {
|
||||
string country_code = 1;
|
||||
repeated Domain domain = 2;
|
||||
|
||||
// resource_hash instruct simplified config converter to load domain from geo file.
|
||||
bytes resource_hash = 3;
|
||||
string code = 4;
|
||||
}
|
||||
|
||||
message GeoSiteList {
|
||||
repeated GeoSite entry = 1;
|
||||
}
|
||||
379
ruleset/expr.go
Normal file
379
ruleset/expr.go
Normal file
@@ -0,0 +1,379 @@
|
||||
package ruleset
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/expr-lang/expr/builtin"
|
||||
|
||||
"github.com/expr-lang/expr"
|
||||
"github.com/expr-lang/expr/ast"
|
||||
"github.com/expr-lang/expr/conf"
|
||||
"github.com/expr-lang/expr/vm"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"git.difuse.io/Difuse/Mellaris/analyzer"
|
||||
"git.difuse.io/Difuse/Mellaris/modifier"
|
||||
"git.difuse.io/Difuse/Mellaris/ruleset/builtins"
|
||||
"git.difuse.io/Difuse/Mellaris/ruleset/builtins/geo"
|
||||
)
|
||||
|
||||
// ExprRule is the external representation of an expression rule.
|
||||
type ExprRule struct {
|
||||
Name string `yaml:"name"`
|
||||
Action string `yaml:"action"`
|
||||
Log bool `yaml:"log"`
|
||||
Modifier ModifierEntry `yaml:"modifier"`
|
||||
Expr string `yaml:"expr"`
|
||||
}
|
||||
|
||||
type ModifierEntry struct {
|
||||
Name string `yaml:"name"`
|
||||
Args map[string]interface{} `yaml:"args"`
|
||||
}
|
||||
|
||||
func ExprRulesFromYAML(file string) ([]ExprRule, error) {
|
||||
bs, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var rules []ExprRule
|
||||
err = yaml.Unmarshal(bs, &rules)
|
||||
return rules, err
|
||||
}
|
||||
|
||||
// compiledExprRule is the internal, compiled representation of an expression rule.
|
||||
type compiledExprRule struct {
|
||||
Name string
|
||||
Action *Action // fallthrough if nil
|
||||
Log bool
|
||||
ModInstance modifier.Instance
|
||||
Program *vm.Program
|
||||
}
|
||||
|
||||
var _ Ruleset = (*exprRuleset)(nil)
|
||||
|
||||
type exprRuleset struct {
|
||||
Rules []compiledExprRule
|
||||
Ans []analyzer.Analyzer
|
||||
Logger Logger
|
||||
}
|
||||
|
||||
func (r *exprRuleset) Analyzers(info StreamInfo) []analyzer.Analyzer {
|
||||
return r.Ans
|
||||
}
|
||||
|
||||
func (r *exprRuleset) Match(info StreamInfo) MatchResult {
|
||||
env := streamInfoToExprEnv(info)
|
||||
for _, rule := range r.Rules {
|
||||
v, err := vm.Run(rule.Program, env)
|
||||
if err != nil {
|
||||
// Log the error and continue to the next rule.
|
||||
r.Logger.MatchError(info, rule.Name, err)
|
||||
continue
|
||||
}
|
||||
if vBool, ok := v.(bool); ok && vBool {
|
||||
if rule.Log {
|
||||
r.Logger.Log(info, rule.Name)
|
||||
}
|
||||
if rule.Action != nil {
|
||||
return MatchResult{
|
||||
Action: *rule.Action,
|
||||
ModInstance: rule.ModInstance,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// No match
|
||||
return MatchResult{
|
||||
Action: ActionMaybe,
|
||||
}
|
||||
}
|
||||
|
||||
// CompileExprRules compiles a list of expression rules into a ruleset.
|
||||
// It returns an error if any of the rules are invalid, or if any of the analyzers
|
||||
// used by the rules are unknown (not provided in the analyzer list).
|
||||
func CompileExprRules(rules []ExprRule, ans []analyzer.Analyzer, mods []modifier.Modifier, config *BuiltinConfig) (Ruleset, error) {
|
||||
var compiledRules []compiledExprRule
|
||||
fullAnMap := analyzersToMap(ans)
|
||||
fullModMap := modifiersToMap(mods)
|
||||
depAnMap := make(map[string]analyzer.Analyzer)
|
||||
funcMap := buildFunctionMap(config)
|
||||
// Compile all rules and build a map of analyzers that are used by the rules.
|
||||
for _, rule := range rules {
|
||||
if rule.Action == "" && !rule.Log {
|
||||
return nil, fmt.Errorf("rule %q must have at least one of action or log", rule.Name)
|
||||
}
|
||||
var action *Action
|
||||
if rule.Action != "" {
|
||||
a, ok := actionStringToAction(rule.Action)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("rule %q has invalid action %q", rule.Name, rule.Action)
|
||||
}
|
||||
action = &a
|
||||
}
|
||||
visitor := &idVisitor{Variables: make(map[string]bool), Identifiers: make(map[string]bool)}
|
||||
patcher := &idPatcher{FuncMap: funcMap}
|
||||
program, err := expr.Compile(rule.Expr,
|
||||
func(c *conf.Config) {
|
||||
c.Strict = false
|
||||
c.Expect = reflect.Bool
|
||||
c.Visitors = append(c.Visitors, visitor, patcher)
|
||||
for name, f := range funcMap {
|
||||
c.Functions[name] = &builtin.Function{
|
||||
Name: name,
|
||||
Func: f.Func,
|
||||
Types: f.Types,
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("rule %q has invalid expression: %w", rule.Name, err)
|
||||
}
|
||||
if patcher.Err != nil {
|
||||
return nil, fmt.Errorf("rule %q failed to patch expression: %w", rule.Name, patcher.Err)
|
||||
}
|
||||
for name := range visitor.Identifiers {
|
||||
// Skip built-in analyzers & user-defined variables
|
||||
if isBuiltInAnalyzer(name) || visitor.Variables[name] {
|
||||
continue
|
||||
}
|
||||
if f, ok := funcMap[name]; ok {
|
||||
// Built-in function, initialize if necessary
|
||||
if f.InitFunc != nil {
|
||||
if err := f.InitFunc(); err != nil {
|
||||
return nil, fmt.Errorf("rule %q failed to initialize function %q: %w", rule.Name, name, err)
|
||||
}
|
||||
}
|
||||
} else if a, ok := fullAnMap[name]; ok {
|
||||
// Analyzer, add to dependency map
|
||||
depAnMap[name] = a
|
||||
}
|
||||
}
|
||||
cr := compiledExprRule{
|
||||
Name: rule.Name,
|
||||
Action: action,
|
||||
Log: rule.Log,
|
||||
Program: program,
|
||||
}
|
||||
if action != nil && *action == ActionModify {
|
||||
mod, ok := fullModMap[rule.Modifier.Name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("rule %q uses unknown modifier %q", rule.Name, rule.Modifier.Name)
|
||||
}
|
||||
modInst, err := mod.New(rule.Modifier.Args)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("rule %q failed to create modifier instance: %w", rule.Name, err)
|
||||
}
|
||||
cr.ModInstance = modInst
|
||||
}
|
||||
compiledRules = append(compiledRules, cr)
|
||||
}
|
||||
// Convert the analyzer map to a list.
|
||||
var depAns []analyzer.Analyzer
|
||||
for _, a := range depAnMap {
|
||||
depAns = append(depAns, a)
|
||||
}
|
||||
return &exprRuleset{
|
||||
Rules: compiledRules,
|
||||
Ans: depAns,
|
||||
Logger: config.Logger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func streamInfoToExprEnv(info StreamInfo) map[string]interface{} {
|
||||
m := map[string]interface{}{
|
||||
"id": info.ID,
|
||||
"proto": info.Protocol.String(),
|
||||
"ip": map[string]string{
|
||||
"src": info.SrcIP.String(),
|
||||
"dst": info.DstIP.String(),
|
||||
},
|
||||
"port": map[string]uint16{
|
||||
"src": info.SrcPort,
|
||||
"dst": info.DstPort,
|
||||
},
|
||||
}
|
||||
for anName, anProps := range info.Props {
|
||||
if len(anProps) != 0 {
|
||||
// Ignore analyzers with empty properties
|
||||
m[anName] = anProps
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func isBuiltInAnalyzer(name string) bool {
|
||||
switch name {
|
||||
case "id", "proto", "ip", "port":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func actionStringToAction(action string) (Action, bool) {
|
||||
switch strings.ToLower(action) {
|
||||
case "allow":
|
||||
return ActionAllow, true
|
||||
case "block":
|
||||
return ActionBlock, true
|
||||
case "drop":
|
||||
return ActionDrop, true
|
||||
case "modify":
|
||||
return ActionModify, true
|
||||
default:
|
||||
return ActionMaybe, false
|
||||
}
|
||||
}
|
||||
|
||||
// analyzersToMap converts a list of analyzers to a map of name -> analyzer.
|
||||
// This is for easier lookup when compiling rules.
|
||||
func analyzersToMap(ans []analyzer.Analyzer) map[string]analyzer.Analyzer {
|
||||
anMap := make(map[string]analyzer.Analyzer)
|
||||
for _, a := range ans {
|
||||
anMap[a.Name()] = a
|
||||
}
|
||||
return anMap
|
||||
}
|
||||
|
||||
// modifiersToMap converts a list of modifiers to a map of name -> modifier.
|
||||
// This is for easier lookup when compiling rules.
|
||||
func modifiersToMap(mods []modifier.Modifier) map[string]modifier.Modifier {
|
||||
modMap := make(map[string]modifier.Modifier)
|
||||
for _, m := range mods {
|
||||
modMap[m.Name()] = m
|
||||
}
|
||||
return modMap
|
||||
}
|
||||
|
||||
// idVisitor is a visitor that collects all identifiers in an expression.
|
||||
// This is for determining which analyzers are used by the expression.
|
||||
type idVisitor struct {
|
||||
Variables map[string]bool
|
||||
Identifiers map[string]bool
|
||||
}
|
||||
|
||||
func (v *idVisitor) Visit(node *ast.Node) {
|
||||
if varNode, ok := (*node).(*ast.VariableDeclaratorNode); ok {
|
||||
v.Variables[varNode.Name] = true
|
||||
} else if idNode, ok := (*node).(*ast.IdentifierNode); ok {
|
||||
v.Identifiers[idNode.Value] = true
|
||||
}
|
||||
}
|
||||
|
||||
// idPatcher patches the AST during expr compilation, replacing certain values with
|
||||
// their internal representations for better runtime performance.
|
||||
type idPatcher struct {
|
||||
FuncMap map[string]*Function
|
||||
Err error
|
||||
}
|
||||
|
||||
func (p *idPatcher) Visit(node *ast.Node) {
|
||||
switch (*node).(type) {
|
||||
case *ast.CallNode:
|
||||
callNode := (*node).(*ast.CallNode)
|
||||
if callNode.Callee == nil {
|
||||
// Ignore invalid call nodes
|
||||
return
|
||||
}
|
||||
if f, ok := p.FuncMap[callNode.Callee.String()]; ok {
|
||||
if f.PatchFunc != nil {
|
||||
if err := f.PatchFunc(&callNode.Arguments); err != nil {
|
||||
p.Err = err
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Function struct {
|
||||
InitFunc func() error
|
||||
PatchFunc func(args *[]ast.Node) error
|
||||
Func func(params ...any) (any, error)
|
||||
Types []reflect.Type
|
||||
}
|
||||
|
||||
func buildFunctionMap(config *BuiltinConfig) map[string]*Function {
|
||||
geoMatcher := geo.NewGeoMatcher(config.GeoSiteFilename, config.GeoIpFilename)
|
||||
return map[string]*Function{
|
||||
"geoip": {
|
||||
InitFunc: geoMatcher.LoadGeoIP,
|
||||
PatchFunc: nil,
|
||||
Func: func(params ...any) (any, error) {
|
||||
return geoMatcher.MatchGeoIp(params[0].(string), params[1].(string)), nil
|
||||
},
|
||||
Types: []reflect.Type{reflect.TypeOf(geoMatcher.MatchGeoIp)},
|
||||
},
|
||||
"geosite": {
|
||||
InitFunc: geoMatcher.LoadGeoSite,
|
||||
PatchFunc: nil,
|
||||
Func: func(params ...any) (any, error) {
|
||||
return geoMatcher.MatchGeoSite(params[0].(string), params[1].(string)), nil
|
||||
},
|
||||
Types: []reflect.Type{reflect.TypeOf(geoMatcher.MatchGeoSite)},
|
||||
},
|
||||
"cidr": {
|
||||
InitFunc: nil,
|
||||
PatchFunc: func(args *[]ast.Node) error {
|
||||
cidrStringNode, ok := (*args)[1].(*ast.StringNode)
|
||||
if !ok {
|
||||
return fmt.Errorf("cidr: invalid argument type")
|
||||
}
|
||||
cidr, err := builtins.CompileCIDR(cidrStringNode.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
(*args)[1] = &ast.ConstantNode{Value: cidr}
|
||||
return nil
|
||||
},
|
||||
Func: func(params ...any) (any, error) {
|
||||
return builtins.MatchCIDR(params[0].(string), params[1].(*net.IPNet)), nil
|
||||
},
|
||||
Types: []reflect.Type{reflect.TypeOf(builtins.MatchCIDR)},
|
||||
},
|
||||
"lookup": {
|
||||
InitFunc: nil,
|
||||
PatchFunc: func(args *[]ast.Node) error {
|
||||
var serverStr *ast.StringNode
|
||||
if len(*args) > 1 {
|
||||
// Has the optional server argument
|
||||
var ok bool
|
||||
serverStr, ok = (*args)[1].(*ast.StringNode)
|
||||
if !ok {
|
||||
return fmt.Errorf("lookup: invalid argument type")
|
||||
}
|
||||
}
|
||||
r := &net.Resolver{
|
||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
if serverStr != nil {
|
||||
address = serverStr.Value
|
||||
}
|
||||
return config.ProtectedDialContext(ctx, network, address)
|
||||
},
|
||||
}
|
||||
if len(*args) > 1 {
|
||||
(*args)[1] = &ast.ConstantNode{Value: r}
|
||||
} else {
|
||||
*args = append(*args, &ast.ConstantNode{Value: r})
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Func: func(params ...any) (any, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)
|
||||
defer cancel()
|
||||
return params[1].(*net.Resolver).LookupHost(ctx, params[0].(string))
|
||||
},
|
||||
Types: []reflect.Type{
|
||||
reflect.TypeOf((func(string, *net.Resolver) []string)(nil)),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
108
ruleset/interface.go
Normal file
108
ruleset/interface.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package ruleset
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"git.difuse.io/Difuse/Mellaris/analyzer"
|
||||
"git.difuse.io/Difuse/Mellaris/modifier"
|
||||
)
|
||||
|
||||
type Action int
|
||||
|
||||
const (
|
||||
// ActionMaybe indicates that the ruleset hasn't seen anything worth blocking based on
|
||||
// current information, but that may change if volatile fields change in the future.
|
||||
ActionMaybe Action = iota
|
||||
// ActionAllow indicates that the stream should be allowed regardless of future changes.
|
||||
ActionAllow
|
||||
// ActionBlock indicates that the stream should be blocked.
|
||||
ActionBlock
|
||||
// ActionDrop indicates that the current packet should be dropped,
|
||||
// but the stream should be allowed to continue.
|
||||
// Only valid for UDP streams. Equivalent to ActionBlock for TCP streams.
|
||||
ActionDrop
|
||||
// ActionModify indicates that the current packet should be modified,
|
||||
// and the stream should be allowed to continue.
|
||||
// Only valid for UDP streams. Equivalent to ActionMaybe for TCP streams.
|
||||
ActionModify
|
||||
)
|
||||
|
||||
func (a Action) String() string {
|
||||
switch a {
|
||||
case ActionMaybe:
|
||||
return "maybe"
|
||||
case ActionAllow:
|
||||
return "allow"
|
||||
case ActionBlock:
|
||||
return "block"
|
||||
case ActionDrop:
|
||||
return "drop"
|
||||
case ActionModify:
|
||||
return "modify"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
type Protocol int
|
||||
|
||||
func (p Protocol) String() string {
|
||||
switch p {
|
||||
case ProtocolTCP:
|
||||
return "tcp"
|
||||
case ProtocolUDP:
|
||||
return "udp"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
ProtocolTCP Protocol = iota
|
||||
ProtocolUDP
|
||||
)
|
||||
|
||||
type StreamInfo struct {
|
||||
ID int64
|
||||
Protocol Protocol
|
||||
SrcIP, DstIP net.IP
|
||||
SrcPort, DstPort uint16
|
||||
Props analyzer.CombinedPropMap
|
||||
}
|
||||
|
||||
func (i StreamInfo) SrcString() string {
|
||||
return net.JoinHostPort(i.SrcIP.String(), strconv.Itoa(int(i.SrcPort)))
|
||||
}
|
||||
|
||||
func (i StreamInfo) DstString() string {
|
||||
return net.JoinHostPort(i.DstIP.String(), strconv.Itoa(int(i.DstPort)))
|
||||
}
|
||||
|
||||
type MatchResult struct {
|
||||
Action Action
|
||||
ModInstance modifier.Instance
|
||||
}
|
||||
|
||||
type Ruleset interface {
|
||||
// Analyzers returns the list of analyzers to use for a stream.
|
||||
// It must be safe for concurrent use by multiple workers.
|
||||
Analyzers(StreamInfo) []analyzer.Analyzer
|
||||
// Match matches a stream against the ruleset and returns the result.
|
||||
// It must be safe for concurrent use by multiple workers.
|
||||
Match(StreamInfo) MatchResult
|
||||
}
|
||||
|
||||
// Logger is the logging interface for the ruleset.
|
||||
type Logger interface {
|
||||
Log(info StreamInfo, name string)
|
||||
MatchError(info StreamInfo, name string, err error)
|
||||
}
|
||||
|
||||
type BuiltinConfig struct {
|
||||
Logger Logger
|
||||
GeoSiteFilename string
|
||||
GeoIpFilename string
|
||||
ProtectedDialContext func(ctx context.Context, network, address string) (net.Conn, error)
|
||||
}
|
||||
Reference in New Issue
Block a user