github.com/v2fly/v2ray-core/v4@v4.45.2/app/router/condition_geoip.go (about)

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