mirror of
https://gitclone.com/github.com/MetaCubeX/Clash.Meta
synced 2025-02-23 07:13:17 +08:00
feat: add IP-ASN
rule
This commit is contained in:
parent
7ad37ca0e3
commit
44d8a14629
@ -14,8 +14,11 @@ import (
|
|||||||
"github.com/metacubex/mihomo/log"
|
"github.com/metacubex/mihomo/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var initGeoSite bool
|
var (
|
||||||
var initGeoIP int
|
initGeoSite bool
|
||||||
|
initGeoIP int
|
||||||
|
initASN bool
|
||||||
|
)
|
||||||
|
|
||||||
func InitGeoSite() error {
|
func InitGeoSite() error {
|
||||||
if _, err := os.Stat(C.Path.GeoSite()); os.IsNotExist(err) {
|
if _, err := os.Stat(C.Path.GeoSite()); os.IsNotExist(err) {
|
||||||
@ -113,7 +116,7 @@ func InitGeoIP() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if initGeoIP != 2 {
|
if initGeoIP != 2 {
|
||||||
if !mmdb.Verify() {
|
if !mmdb.Verify(C.Path.MMDB()) {
|
||||||
log.Warnln("MMDB invalid, remove and download")
|
log.Warnln("MMDB invalid, remove and download")
|
||||||
if err := os.Remove(C.Path.MMDB()); err != nil {
|
if err := os.Remove(C.Path.MMDB()); err != nil {
|
||||||
return fmt.Errorf("can't remove invalid MMDB: %s", err.Error())
|
return fmt.Errorf("can't remove invalid MMDB: %s", err.Error())
|
||||||
@ -126,3 +129,27 @@ func InitGeoIP() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func InitASN() error {
|
||||||
|
if _, err := os.Stat(C.Path.ASN()); os.IsNotExist(err) {
|
||||||
|
log.Infoln("Can't find ASN.mmdb, start download")
|
||||||
|
if err := mmdb.DownloadASN(C.Path.ASN()); err != nil {
|
||||||
|
return fmt.Errorf("can't download ASN.mmdb: %s", err.Error())
|
||||||
|
}
|
||||||
|
log.Infoln("Download ASN.mmdb finish")
|
||||||
|
initASN = false
|
||||||
|
}
|
||||||
|
if !initASN {
|
||||||
|
if !mmdb.Verify(C.Path.ASN()) {
|
||||||
|
log.Warnln("ASN invalid, remove and download")
|
||||||
|
if err := os.Remove(C.Path.ASN()); err != nil {
|
||||||
|
return fmt.Errorf("can't remove invalid ASN: %s", err.Error())
|
||||||
|
}
|
||||||
|
if err := mmdb.DownloadASN(C.Path.ASN()); err != nil {
|
||||||
|
return fmt.Errorf("can't download ASN: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initASN = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -25,56 +25,58 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
reader Reader
|
IPreader IPReader
|
||||||
once sync.Once
|
ASNreader ASNReader
|
||||||
|
IPonce sync.Once
|
||||||
|
ASNonce sync.Once
|
||||||
)
|
)
|
||||||
|
|
||||||
func LoadFromBytes(buffer []byte) {
|
func LoadFromBytes(buffer []byte) {
|
||||||
once.Do(func() {
|
IPonce.Do(func() {
|
||||||
mmdb, err := maxminddb.FromBytes(buffer)
|
mmdb, err := maxminddb.FromBytes(buffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("Can't load mmdb: %s", err.Error())
|
log.Fatalln("Can't load mmdb: %s", err.Error())
|
||||||
}
|
}
|
||||||
reader = Reader{Reader: mmdb}
|
IPreader = IPReader{Reader: mmdb}
|
||||||
switch mmdb.Metadata.DatabaseType {
|
switch mmdb.Metadata.DatabaseType {
|
||||||
case "sing-geoip":
|
case "sing-geoip":
|
||||||
reader.databaseType = typeSing
|
IPreader.databaseType = typeSing
|
||||||
case "Meta-geoip0":
|
case "Meta-geoip0":
|
||||||
reader.databaseType = typeMetaV0
|
IPreader.databaseType = typeMetaV0
|
||||||
default:
|
default:
|
||||||
reader.databaseType = typeMaxmind
|
IPreader.databaseType = typeMaxmind
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func Verify() bool {
|
func Verify(path string) bool {
|
||||||
instance, err := maxminddb.Open(C.Path.MMDB())
|
instance, err := maxminddb.Open(path)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
instance.Close()
|
instance.Close()
|
||||||
}
|
}
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Instance() Reader {
|
func IPInstance() IPReader {
|
||||||
once.Do(func() {
|
IPonce.Do(func() {
|
||||||
mmdbPath := C.Path.MMDB()
|
mmdbPath := C.Path.MMDB()
|
||||||
log.Infoln("Load MMDB file: %s", mmdbPath)
|
log.Infoln("Load MMDB file: %s", mmdbPath)
|
||||||
mmdb, err := maxminddb.Open(mmdbPath)
|
mmdb, err := maxminddb.Open(mmdbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("Can't load MMDB: %s", err.Error())
|
log.Fatalln("Can't load MMDB: %s", err.Error())
|
||||||
}
|
}
|
||||||
reader = Reader{Reader: mmdb}
|
IPreader = IPReader{Reader: mmdb}
|
||||||
switch mmdb.Metadata.DatabaseType {
|
switch mmdb.Metadata.DatabaseType {
|
||||||
case "sing-geoip":
|
case "sing-geoip":
|
||||||
reader.databaseType = typeSing
|
IPreader.databaseType = typeSing
|
||||||
case "Meta-geoip0":
|
case "Meta-geoip0":
|
||||||
reader.databaseType = typeMetaV0
|
IPreader.databaseType = typeMetaV0
|
||||||
default:
|
default:
|
||||||
reader.databaseType = typeMaxmind
|
IPreader.databaseType = typeMaxmind
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return reader
|
return IPreader
|
||||||
}
|
}
|
||||||
|
|
||||||
func DownloadMMDB(path string) (err error) {
|
func DownloadMMDB(path string) (err error) {
|
||||||
@ -96,6 +98,43 @@ func DownloadMMDB(path string) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Reload() {
|
func ASNInstance() ASNReader {
|
||||||
mihomoOnce.Reset(&once)
|
ASNonce.Do(func() {
|
||||||
|
ASNPath := C.Path.ASN()
|
||||||
|
log.Infoln("Load ASN file: %s", ASNPath)
|
||||||
|
asn, err := maxminddb.Open(ASNPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Can't load ASN: %s", err.Error())
|
||||||
|
}
|
||||||
|
ASNreader = ASNReader{Reader: asn}
|
||||||
|
})
|
||||||
|
|
||||||
|
return ASNreader
|
||||||
|
}
|
||||||
|
|
||||||
|
func DownloadASN(path string) (err error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
|
||||||
|
defer cancel()
|
||||||
|
resp, err := mihomoHttp.HttpRequest(ctx, C.ASNUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
_, err = io.Copy(f, resp.Body)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReloadIP() {
|
||||||
|
mihomoOnce.Reset(&IPonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReloadASN() {
|
||||||
|
mihomoOnce.Reset(&ASNonce)
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,14 @@ package mmdb
|
|||||||
import "github.com/oschwald/maxminddb-golang"
|
import "github.com/oschwald/maxminddb-golang"
|
||||||
|
|
||||||
func InstallOverride(override *maxminddb.Reader) {
|
func InstallOverride(override *maxminddb.Reader) {
|
||||||
newReader := Reader{Reader: override}
|
newReader := IPReader{Reader: override}
|
||||||
switch override.Metadata.DatabaseType {
|
switch override.Metadata.DatabaseType {
|
||||||
case "sing-geoip":
|
case "sing-geoip":
|
||||||
reader.databaseType = typeSing
|
IPreader.databaseType = typeSing
|
||||||
case "Meta-geoip0":
|
case "Meta-geoip0":
|
||||||
reader.databaseType = typeMetaV0
|
IPreader.databaseType = typeMetaV0
|
||||||
default:
|
default:
|
||||||
reader.databaseType = typeMaxmind
|
IPreader.databaseType = typeMaxmind
|
||||||
}
|
}
|
||||||
reader = newReader
|
IPreader = newReader
|
||||||
}
|
}
|
||||||
|
@ -14,12 +14,21 @@ type geoip2Country struct {
|
|||||||
} `maxminddb:"country"`
|
} `maxminddb:"country"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Reader struct {
|
type IPReader struct {
|
||||||
*maxminddb.Reader
|
*maxminddb.Reader
|
||||||
databaseType
|
databaseType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r Reader) LookupCode(ipAddress net.IP) []string {
|
type ASNReader struct {
|
||||||
|
*maxminddb.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
type ASNResult struct {
|
||||||
|
AutonomousSystemNumber uint32 `maxminddb:"autonomous_system_number"`
|
||||||
|
AutonomousSystemOrganization string `maxminddb:"autonomous_system_organization"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r IPReader) LookupCode(ipAddress net.IP) []string {
|
||||||
switch r.databaseType {
|
switch r.databaseType {
|
||||||
case typeMaxmind:
|
case typeMaxmind:
|
||||||
var country geoip2Country
|
var country geoip2Country
|
||||||
@ -56,3 +65,9 @@ func (r Reader) LookupCode(ipAddress net.IP) []string {
|
|||||||
panic(fmt.Sprint("unknown geoip database type:", r.databaseType))
|
panic(fmt.Sprint("unknown geoip database type:", r.databaseType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r ASNReader) LookupASN(ip net.IP) ASNResult {
|
||||||
|
var result ASNResult
|
||||||
|
r.Lookup(ip, &result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
@ -348,6 +348,7 @@ type RawConfig struct {
|
|||||||
type GeoXUrl struct {
|
type GeoXUrl struct {
|
||||||
GeoIp string `yaml:"geoip" json:"geoip"`
|
GeoIp string `yaml:"geoip" json:"geoip"`
|
||||||
Mmdb string `yaml:"mmdb" json:"mmdb"`
|
Mmdb string `yaml:"mmdb" json:"mmdb"`
|
||||||
|
ASN string `yaml:"asn" json:"asn"`
|
||||||
GeoSite string `yaml:"geosite" json:"geosite"`
|
GeoSite string `yaml:"geosite" json:"geosite"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,6 +496,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
|
|||||||
},
|
},
|
||||||
GeoXUrl: GeoXUrl{
|
GeoXUrl: GeoXUrl{
|
||||||
Mmdb: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb",
|
Mmdb: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb",
|
||||||
|
ASN: "https://github.com/xishang0128/geoip/releases/download/latest/GeoLite2-ASN.mmdb",
|
||||||
GeoIp: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat",
|
GeoIp: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat",
|
||||||
GeoSite: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat",
|
GeoSite: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat",
|
||||||
},
|
},
|
||||||
@ -620,6 +622,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
|
|||||||
C.GeoIpUrl = cfg.GeoXUrl.GeoIp
|
C.GeoIpUrl = cfg.GeoXUrl.GeoIp
|
||||||
C.GeoSiteUrl = cfg.GeoXUrl.GeoSite
|
C.GeoSiteUrl = cfg.GeoXUrl.GeoSite
|
||||||
C.MmdbUrl = cfg.GeoXUrl.Mmdb
|
C.MmdbUrl = cfg.GeoXUrl.Mmdb
|
||||||
|
C.ASNUrl = cfg.GeoXUrl.ASN
|
||||||
C.GeodataMode = cfg.GeodataMode
|
C.GeodataMode = cfg.GeodataMode
|
||||||
C.UA = cfg.GlobalUA
|
C.UA = cfg.GlobalUA
|
||||||
if cfg.KeepAliveInterval != 0 {
|
if cfg.KeepAliveInterval != 0 {
|
||||||
|
@ -34,7 +34,7 @@ func UpdateGeoDatabases() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
defer mmdb.Reload()
|
defer mmdb.ReloadIP()
|
||||||
data, err := downloadForBytes(C.MmdbUrl)
|
data, err := downloadForBytes(C.MmdbUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't download MMDB database file: %w", err)
|
return fmt.Errorf("can't download MMDB database file: %w", err)
|
||||||
@ -46,12 +46,31 @@ func UpdateGeoDatabases() error {
|
|||||||
}
|
}
|
||||||
_ = instance.Close()
|
_ = instance.Close()
|
||||||
|
|
||||||
mmdb.Instance().Reader.Close() // mmdb is loaded with mmap, so it needs to be closed before overwriting the file
|
mmdb.IPInstance().Reader.Close() // mmdb is loaded with mmap, so it needs to be closed before overwriting the file
|
||||||
if err = saveFile(data, C.Path.MMDB()); err != nil {
|
if err = saveFile(data, C.Path.MMDB()); err != nil {
|
||||||
return fmt.Errorf("can't save MMDB database file: %w", err)
|
return fmt.Errorf("can't save MMDB database file: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if C.ASNEnable {
|
||||||
|
defer mmdb.ReloadASN()
|
||||||
|
data, err := downloadForBytes(C.ASNUrl)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't download ASN database file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
instance, err := maxminddb.FromBytes(data)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid ASN database file: %s", err)
|
||||||
|
}
|
||||||
|
_ = instance.Close()
|
||||||
|
|
||||||
|
mmdb.ASNInstance().Reader.Close()
|
||||||
|
if err = saveFile(data, C.Path.ASN()); err != nil {
|
||||||
|
return fmt.Errorf("can't save ASN database file: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data, err := downloadForBytes(C.GeoSiteUrl)
|
data, err := downloadForBytes(C.GeoSiteUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't download GeoSite database file: %w", err)
|
return fmt.Errorf("can't download GeoSite database file: %w", err)
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package constant
|
package constant
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
ASNEnable bool
|
||||||
GeodataMode bool
|
GeodataMode bool
|
||||||
GeoAutoUpdate bool
|
GeoAutoUpdate bool
|
||||||
GeoUpdateInterval int
|
GeoUpdateInterval int
|
||||||
GeoIpUrl string
|
GeoIpUrl string
|
||||||
MmdbUrl string
|
MmdbUrl string
|
||||||
GeoSiteUrl string
|
GeoSiteUrl string
|
||||||
|
ASNUrl string
|
||||||
)
|
)
|
||||||
|
@ -133,7 +133,8 @@ type Metadata struct {
|
|||||||
Type Type `json:"type"`
|
Type Type `json:"type"`
|
||||||
SrcIP netip.Addr `json:"sourceIP"`
|
SrcIP netip.Addr `json:"sourceIP"`
|
||||||
DstIP netip.Addr `json:"destinationIP"`
|
DstIP netip.Addr `json:"destinationIP"`
|
||||||
DstGeoIP []string `json:"destinationGeoIP"` // can be nil if never queried, empty slice if got no result
|
DstGeoIP []string `json:"destinationGeoIP"` // can be nil if never queried, empty slice if got no result
|
||||||
|
DstIPASN string `json:"destinationIPASN"`
|
||||||
SrcPort uint16 `json:"sourcePort,string"` // `,string` is used to compatible with old version json output
|
SrcPort uint16 `json:"sourcePort,string"` // `,string` is used to compatible with old version json output
|
||||||
DstPort uint16 `json:"destinationPort,string"` // `,string` is used to compatible with old version json output
|
DstPort uint16 `json:"destinationPort,string"` // `,string` is used to compatible with old version json output
|
||||||
InIP netip.Addr `json:"inboundIP"`
|
InIP netip.Addr `json:"inboundIP"`
|
||||||
|
@ -15,6 +15,7 @@ const Name = "mihomo"
|
|||||||
var (
|
var (
|
||||||
GeositeName = "GeoSite.dat"
|
GeositeName = "GeoSite.dat"
|
||||||
GeoipName = "GeoIP.dat"
|
GeoipName = "GeoIP.dat"
|
||||||
|
ASNName = "ASN.mmdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Path is used to get the configuration path
|
// Path is used to get the configuration path
|
||||||
@ -112,6 +113,25 @@ func (p *path) MMDB() string {
|
|||||||
return P.Join(p.homeDir, "geoip.metadb")
|
return P.Join(p.homeDir, "geoip.metadb")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *path) ASN() string {
|
||||||
|
files, err := os.ReadDir(p.homeDir)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
for _, fi := range files {
|
||||||
|
if fi.IsDir() {
|
||||||
|
// 目录则直接跳过
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
if strings.EqualFold(fi.Name(), "ASN.mmdb") {
|
||||||
|
ASNName = fi.Name()
|
||||||
|
return P.Join(p.homeDir, fi.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return P.Join(p.homeDir, ASNName)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *path) OldCache() string {
|
func (p *path) OldCache() string {
|
||||||
return P.Join(p.homeDir, ".cache")
|
return P.Join(p.homeDir, ".cache")
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ const (
|
|||||||
GEOSITE
|
GEOSITE
|
||||||
GEOIP
|
GEOIP
|
||||||
IPCIDR
|
IPCIDR
|
||||||
|
IPASN
|
||||||
SrcIPCIDR
|
SrcIPCIDR
|
||||||
IPSuffix
|
IPSuffix
|
||||||
SrcIPSuffix
|
SrcIPSuffix
|
||||||
@ -49,6 +50,8 @@ func (rt RuleType) String() string {
|
|||||||
return "GeoIP"
|
return "GeoIP"
|
||||||
case IPCIDR:
|
case IPCIDR:
|
||||||
return "IPCIDR"
|
return "IPCIDR"
|
||||||
|
case IPASN:
|
||||||
|
return "IPASN"
|
||||||
case SrcIPCIDR:
|
case SrcIPCIDR:
|
||||||
return "SrcIPCIDR"
|
return "SrcIPCIDR"
|
||||||
case IPSuffix:
|
case IPSuffix:
|
||||||
|
@ -24,7 +24,7 @@ var geoIPMatcher *router.GeoIPMatcher
|
|||||||
|
|
||||||
func (gf *geoipFilter) Match(ip netip.Addr) bool {
|
func (gf *geoipFilter) Match(ip netip.Addr) bool {
|
||||||
if !C.GeodataMode {
|
if !C.GeodataMode {
|
||||||
codes := mmdb.Instance().LookupCode(ip.AsSlice())
|
codes := mmdb.IPInstance().LookupCode(ip.AsSlice())
|
||||||
for _, code := range codes {
|
for _, code := range codes {
|
||||||
if !strings.EqualFold(code, gf.code) && !ip.IsPrivate() {
|
if !strings.EqualFold(code, gf.code) && !ip.IsPrivate() {
|
||||||
return true
|
return true
|
||||||
|
@ -674,6 +674,8 @@ proxies: # socks5
|
|||||||
type: hysteria2
|
type: hysteria2
|
||||||
server: server.com
|
server: server.com
|
||||||
port: 443
|
port: 443
|
||||||
|
# ports: 1000,2000-3000,5000 # port 不可省略
|
||||||
|
# hop-interval: 15
|
||||||
# up和down均不写或为0则使用BBR流控
|
# up和down均不写或为0则使用BBR流控
|
||||||
# up: "30 Mbps" # 若不写单位,默认为 Mbps
|
# up: "30 Mbps" # 若不写单位,默认为 Mbps
|
||||||
# down: "200 Mbps" # 若不写单位,默认为 Mbps
|
# down: "200 Mbps" # 若不写单位,默认为 Mbps
|
||||||
@ -767,6 +769,18 @@ proxies: # socks5
|
|||||||
# protocol-param: "#"
|
# protocol-param: "#"
|
||||||
# udp: true
|
# udp: true
|
||||||
|
|
||||||
|
- name: "ssh-out"
|
||||||
|
type: ssh
|
||||||
|
|
||||||
|
server: 127.0.0.1
|
||||||
|
port: 22
|
||||||
|
username: root
|
||||||
|
password: password
|
||||||
|
privateKey: path
|
||||||
|
|
||||||
|
# dns出站会将请求劫持到内部dns模块,所有请求均在内部处理
|
||||||
|
- name: "dns-out"
|
||||||
|
type: dns
|
||||||
proxy-groups:
|
proxy-groups:
|
||||||
# 代理链,目前relay可以支持udp的只有vmess/vless/trojan/ss/ssr/tuic
|
# 代理链,目前relay可以支持udp的只有vmess/vless/trojan/ss/ssr/tuic
|
||||||
# wireguard目前不支持在relay中使用,请使用proxy中的dialer-proxy配置项
|
# wireguard目前不支持在relay中使用,请使用proxy中的dialer-proxy配置项
|
||||||
@ -885,6 +899,8 @@ rule-providers:
|
|||||||
type: file
|
type: file
|
||||||
rules:
|
rules:
|
||||||
- RULE-SET,rule1,REJECT
|
- RULE-SET,rule1,REJECT
|
||||||
|
- IP-ASN,1,PROXY
|
||||||
|
- DOMAIN-REGEX,^abc,DIRECT
|
||||||
- DOMAIN-SUFFIX,baidu.com,DIRECT
|
- DOMAIN-SUFFIX,baidu.com,DIRECT
|
||||||
- DOMAIN-KEYWORD,google,ss1
|
- DOMAIN-KEYWORD,google,ss1
|
||||||
- IP-CIDR,1.1.1.1/32,ss1
|
- IP-CIDR,1.1.1.1/32,ss1
|
||||||
|
@ -52,7 +52,7 @@ func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) {
|
|||||||
if metadata.DstGeoIP != nil {
|
if metadata.DstGeoIP != nil {
|
||||||
return false, g.adapter
|
return false, g.adapter
|
||||||
}
|
}
|
||||||
metadata.DstGeoIP = mmdb.Instance().LookupCode(ip.AsSlice())
|
metadata.DstGeoIP = mmdb.IPInstance().LookupCode(ip.AsSlice())
|
||||||
for _, code := range metadata.DstGeoIP {
|
for _, code := range metadata.DstGeoIP {
|
||||||
if g.country == code {
|
if g.country == code {
|
||||||
return true, g.adapter
|
return true, g.adapter
|
||||||
|
67
rules/common/ipasn.go
Normal file
67
rules/common/ipasn.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/metacubex/mihomo/component/geodata"
|
||||||
|
"github.com/metacubex/mihomo/component/mmdb"
|
||||||
|
C "github.com/metacubex/mihomo/constant"
|
||||||
|
"github.com/metacubex/mihomo/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ASN struct {
|
||||||
|
*Base
|
||||||
|
asn string
|
||||||
|
adapter string
|
||||||
|
noResolveIP bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ASN) Match(metadata *C.Metadata) (bool, string) {
|
||||||
|
ip := metadata.DstIP
|
||||||
|
if !ip.IsValid() {
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
result := mmdb.ASNInstance().LookupASN(ip.AsSlice())
|
||||||
|
|
||||||
|
asnNumber := strconv.FormatUint(uint64(result.AutonomousSystemNumber), 10)
|
||||||
|
metadata.DstIPASN = asnNumber + " " + result.AutonomousSystemOrganization
|
||||||
|
|
||||||
|
match := a.asn == asnNumber
|
||||||
|
return match, a.adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ASN) RuleType() C.RuleType {
|
||||||
|
return C.IPASN
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ASN) Adapter() string {
|
||||||
|
return a.adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ASN) Payload() string {
|
||||||
|
return a.asn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ASN) ShouldResolveIP() bool {
|
||||||
|
return !a.noResolveIP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ASN) GetASN() string {
|
||||||
|
return a.asn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIPASN(asn string, adapter string, noResolveIP bool) (*ASN, error) {
|
||||||
|
C.ASNEnable = true
|
||||||
|
if err := geodata.InitASN(); err != nil {
|
||||||
|
log.Errorln("can't initial ASN: %s", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ASN{
|
||||||
|
Base: &Base{},
|
||||||
|
asn: asn,
|
||||||
|
adapter: adapter,
|
||||||
|
noResolveIP: noResolveIP,
|
||||||
|
}, nil
|
||||||
|
}
|
@ -27,6 +27,9 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string]
|
|||||||
case "IP-CIDR", "IP-CIDR6":
|
case "IP-CIDR", "IP-CIDR6":
|
||||||
noResolve := RC.HasNoResolve(params)
|
noResolve := RC.HasNoResolve(params)
|
||||||
parsed, parseErr = RC.NewIPCIDR(payload, target, RC.WithIPCIDRNoResolve(noResolve))
|
parsed, parseErr = RC.NewIPCIDR(payload, target, RC.WithIPCIDRNoResolve(noResolve))
|
||||||
|
case "IP-ASN":
|
||||||
|
noResolve := RC.HasNoResolve(params)
|
||||||
|
parsed, parseErr = RC.NewIPASN(payload, target, noResolve)
|
||||||
case "SRC-IP-CIDR":
|
case "SRC-IP-CIDR":
|
||||||
parsed, parseErr = RC.NewIPCIDR(payload, target, RC.WithIPCIDRSourceIP(true), RC.WithIPCIDRNoResolve(true))
|
parsed, parseErr = RC.NewIPCIDR(payload, target, RC.WithIPCIDRSourceIP(true), RC.WithIPCIDRNoResolve(true))
|
||||||
case "IP-SUFFIX":
|
case "IP-SUFFIX":
|
||||||
|
Loading…
Reference in New Issue
Block a user