github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/app/router/condition_geoip.go (about) 1 package router 2 3 import ( 4 "net/netip" 5 6 "go4.org/netipx" 7 8 "github.com/v2fly/v2ray-core/v5/app/router/routercommon" 9 "github.com/v2fly/v2ray-core/v5/common/net" 10 ) 11 12 type GeoIPMatcher struct { 13 countryCode string 14 reverseMatch bool 15 ip4 *netipx.IPSet 16 ip6 *netipx.IPSet 17 } 18 19 func (m *GeoIPMatcher) Init(cidrs []*routercommon.CIDR) error { 20 var builder4, builder6 netipx.IPSetBuilder 21 for _, cidr := range cidrs { 22 netaddrIP, ok := netip.AddrFromSlice(cidr.GetIp()) 23 if !ok { 24 return newError("invalid IP address ", cidr) 25 } 26 netaddrIP = netaddrIP.Unmap() 27 ipPrefix := netip.PrefixFrom(netaddrIP, int(cidr.GetPrefix())) 28 29 switch { 30 case netaddrIP.Is4(): 31 builder4.AddPrefix(ipPrefix) 32 case netaddrIP.Is6(): 33 builder6.AddPrefix(ipPrefix) 34 } 35 } 36 37 var err error 38 m.ip4, err = builder4.IPSet() 39 if err != nil { 40 return err 41 } 42 m.ip6, err = builder6.IPSet() 43 if err != nil { 44 return err 45 } 46 47 return nil 48 } 49 50 func (m *GeoIPMatcher) SetReverseMatch(isReverseMatch bool) { 51 m.reverseMatch = isReverseMatch 52 } 53 54 func (m *GeoIPMatcher) match4(ip net.IP) bool { 55 nip, ok := netipx.FromStdIP(ip) 56 if !ok { 57 return false 58 } 59 return m.ip4.Contains(nip) 60 } 61 62 func (m *GeoIPMatcher) match6(ip net.IP) bool { 63 nip, ok := netipx.FromStdIP(ip) 64 if !ok { 65 return false 66 } 67 return m.ip6.Contains(nip) 68 } 69 70 // Match returns true if the given ip is included by the GeoIP. 71 func (m *GeoIPMatcher) Match(ip net.IP) bool { 72 isMatched := false 73 switch len(ip) { 74 case net.IPv4len: 75 isMatched = m.match4(ip) 76 case net.IPv6len: 77 isMatched = m.match6(ip) 78 } 79 if m.reverseMatch { 80 return !isMatched 81 } 82 return isMatched 83 } 84 85 // GeoIPMatcherContainer is a container for GeoIPMatchers. It keeps unique copies of GeoIPMatcher by country code. 86 type GeoIPMatcherContainer struct { 87 matchers []*GeoIPMatcher 88 } 89 90 // Add adds a new GeoIP set into the container. 91 // If the country code of GeoIP is not empty, GeoIPMatcherContainer will try to find an existing one, instead of adding a new one. 92 func (c *GeoIPMatcherContainer) Add(geoip *routercommon.GeoIP) (*GeoIPMatcher, error) { 93 if geoip.CountryCode != "" { 94 for _, m := range c.matchers { 95 if m.countryCode == geoip.CountryCode && m.reverseMatch == geoip.InverseMatch { 96 return m, nil 97 } 98 } 99 } 100 101 m := &GeoIPMatcher{ 102 countryCode: geoip.CountryCode, 103 reverseMatch: geoip.InverseMatch, 104 } 105 if err := m.Init(geoip.Cidr); err != nil { 106 return nil, err 107 } 108 if geoip.CountryCode != "" { 109 c.matchers = append(c.matchers, m) 110 } 111 return m, nil 112 } 113 114 var globalGeoIPContainer GeoIPMatcherContainer