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