github.com/kelleygo/clashcore@v1.0.2/rules/common/geoip.go (about) 1 package common 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/kelleygo/clashcore/component/geodata" 8 "github.com/kelleygo/clashcore/component/geodata/router" 9 "github.com/kelleygo/clashcore/component/mmdb" 10 "github.com/kelleygo/clashcore/component/resolver" 11 C "github.com/kelleygo/clashcore/constant" 12 "github.com/kelleygo/clashcore/log" 13 ) 14 15 type GEOIP struct { 16 *Base 17 country string 18 adapter string 19 noResolveIP bool 20 isSourceIP bool 21 geoIPMatcher *router.GeoIPMatcher 22 recodeSize int 23 } 24 25 var _ C.Rule = (*GEOIP)(nil) 26 27 func (g *GEOIP) RuleType() C.RuleType { 28 if g.isSourceIP { 29 return C.SrcGEOIP 30 } 31 return C.GEOIP 32 } 33 34 func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) { 35 ip := metadata.DstIP 36 if g.isSourceIP { 37 ip = metadata.SrcIP 38 } 39 if !ip.IsValid() { 40 return false, "" 41 } 42 43 if g.country == "lan" { 44 return ip.IsPrivate() || 45 ip.IsUnspecified() || 46 ip.IsLoopback() || 47 ip.IsMulticast() || 48 ip.IsLinkLocalUnicast() || 49 resolver.IsFakeBroadcastIP(ip), g.adapter 50 } 51 52 for _, code := range metadata.DstGeoIP { 53 if g.country == code { 54 return true, g.adapter 55 } 56 } 57 58 if !C.GeodataMode { 59 if g.isSourceIP { 60 codes := mmdb.IPInstance().LookupCode(ip.AsSlice()) 61 for _, code := range codes { 62 if g.country == code { 63 return true, g.adapter 64 } 65 } 66 return false, g.adapter 67 } 68 69 if metadata.DstGeoIP != nil { 70 return false, g.adapter 71 } 72 metadata.DstGeoIP = mmdb.IPInstance().LookupCode(ip.AsSlice()) 73 for _, code := range metadata.DstGeoIP { 74 if g.country == code { 75 return true, g.adapter 76 } 77 } 78 return false, g.adapter 79 } 80 81 match := g.geoIPMatcher.Match(ip) 82 if match && !g.isSourceIP { 83 metadata.DstGeoIP = append(metadata.DstGeoIP, g.country) 84 } 85 return match, g.adapter 86 } 87 88 func (g *GEOIP) Adapter() string { 89 return g.adapter 90 } 91 92 func (g *GEOIP) Payload() string { 93 return g.country 94 } 95 96 func (g *GEOIP) ShouldResolveIP() bool { 97 return !g.noResolveIP 98 } 99 100 func (g *GEOIP) GetCountry() string { 101 return g.country 102 } 103 104 func (g *GEOIP) GetIPMatcher() *router.GeoIPMatcher { 105 return g.geoIPMatcher 106 } 107 108 func (g *GEOIP) GetRecodeSize() int { 109 return g.recodeSize 110 } 111 112 func NewGEOIP(country string, adapter string, isSrc, noResolveIP bool) (*GEOIP, error) { 113 if err := geodata.InitGeoIP(); err != nil { 114 log.Errorln("can't initial GeoIP: %s", err) 115 return nil, err 116 } 117 country = strings.ToLower(country) 118 119 if !C.GeodataMode || country == "lan" { 120 geoip := &GEOIP{ 121 Base: &Base{}, 122 country: country, 123 adapter: adapter, 124 noResolveIP: noResolveIP, 125 isSourceIP: isSrc, 126 } 127 return geoip, nil 128 } 129 130 geoIPMatcher, size, err := geodata.LoadGeoIPMatcher(country) 131 if err != nil { 132 return nil, fmt.Errorf("[GeoIP] %w", err) 133 } 134 135 log.Infoln("Start initial GeoIP rule %s => %s, records: %d", country, adapter, size) 136 geoip := &GEOIP{ 137 Base: &Base{}, 138 country: country, 139 adapter: adapter, 140 noResolveIP: noResolveIP, 141 isSourceIP: isSrc, 142 geoIPMatcher: geoIPMatcher, 143 recodeSize: size, 144 } 145 return geoip, nil 146 }