github.com/Uhtred009/v2ray-core-1@v4.31.2+incompatible/app/router/condition_geoip.go (about)

     1  // +build !confonly
     2  
     3  package router
     4  
     5  import (
     6  	"encoding/binary"
     7  	"sort"
     8  
     9  	"v2ray.com/core/common/net"
    10  )
    11  
    12  type ipv6 struct {
    13  	a uint64
    14  	b uint64
    15  }
    16  
    17  type GeoIPMatcher struct {
    18  	countryCode string
    19  	ip4         []uint32
    20  	prefix4     []uint8
    21  	ip6         []ipv6
    22  	prefix6     []uint8
    23  }
    24  
    25  func normalize4(ip uint32, prefix uint8) uint32 {
    26  	return (ip >> (32 - prefix)) << (32 - prefix)
    27  }
    28  
    29  func normalize6(ip ipv6, prefix uint8) ipv6 {
    30  	if prefix <= 64 {
    31  		ip.a = (ip.a >> (64 - prefix)) << (64 - prefix)
    32  		ip.b = 0
    33  	} else {
    34  		ip.b = (ip.b >> (128 - prefix)) << (128 - prefix)
    35  	}
    36  	return ip
    37  }
    38  
    39  func (m *GeoIPMatcher) Init(cidrs []*CIDR) error {
    40  	ip4Count := 0
    41  	ip6Count := 0
    42  
    43  	for _, cidr := range cidrs {
    44  		ip := cidr.Ip
    45  		switch len(ip) {
    46  		case 4:
    47  			ip4Count++
    48  		case 16:
    49  			ip6Count++
    50  		default:
    51  			return newError("unexpect ip length: ", len(ip))
    52  		}
    53  	}
    54  
    55  	cidrList := CIDRList(cidrs)
    56  	sort.Sort(&cidrList)
    57  
    58  	m.ip4 = make([]uint32, 0, ip4Count)
    59  	m.prefix4 = make([]uint8, 0, ip4Count)
    60  	m.ip6 = make([]ipv6, 0, ip6Count)
    61  	m.prefix6 = make([]uint8, 0, ip6Count)
    62  
    63  	for _, cidr := range cidrs {
    64  		ip := cidr.Ip
    65  		prefix := uint8(cidr.Prefix)
    66  		switch len(ip) {
    67  		case 4:
    68  			m.ip4 = append(m.ip4, normalize4(binary.BigEndian.Uint32(ip), prefix))
    69  			m.prefix4 = append(m.prefix4, prefix)
    70  		case 16:
    71  			ip6 := ipv6{
    72  				a: binary.BigEndian.Uint64(ip[0:8]),
    73  				b: binary.BigEndian.Uint64(ip[8:16]),
    74  			}
    75  			ip6 = normalize6(ip6, prefix)
    76  
    77  			m.ip6 = append(m.ip6, ip6)
    78  			m.prefix6 = append(m.prefix6, prefix)
    79  		}
    80  	}
    81  
    82  	return nil
    83  }
    84  
    85  func (m *GeoIPMatcher) match4(ip uint32) bool {
    86  	if len(m.ip4) == 0 {
    87  		return false
    88  	}
    89  
    90  	if ip < m.ip4[0] {
    91  		return false
    92  	}
    93  
    94  	size := uint32(len(m.ip4))
    95  	l := uint32(0)
    96  	r := size
    97  	for l < r {
    98  		x := ((l + r) >> 1)
    99  		if ip < m.ip4[x] {
   100  			r = x
   101  			continue
   102  		}
   103  
   104  		nip := normalize4(ip, m.prefix4[x])
   105  		if nip == m.ip4[x] {
   106  			return true
   107  		}
   108  
   109  		l = x + 1
   110  	}
   111  
   112  	return l > 0 && normalize4(ip, m.prefix4[l-1]) == m.ip4[l-1]
   113  }
   114  
   115  func less6(a ipv6, b ipv6) bool {
   116  	return a.a < b.a || (a.a == b.a && a.b < b.b)
   117  }
   118  
   119  func (m *GeoIPMatcher) match6(ip ipv6) bool {
   120  	if len(m.ip6) == 0 {
   121  		return false
   122  	}
   123  
   124  	if less6(ip, m.ip6[0]) {
   125  		return false
   126  	}
   127  
   128  	size := uint32(len(m.ip6))
   129  	l := uint32(0)
   130  	r := size
   131  	for l < r {
   132  		x := (l + r) / 2
   133  		if less6(ip, m.ip6[x]) {
   134  			r = x
   135  			continue
   136  		}
   137  
   138  		if normalize6(ip, m.prefix6[x]) == m.ip6[x] {
   139  			return true
   140  		}
   141  
   142  		l = x + 1
   143  	}
   144  
   145  	return l > 0 && normalize6(ip, m.prefix6[l-1]) == m.ip6[l-1]
   146  }
   147  
   148  // Match returns true if the given ip is included by the GeoIP.
   149  func (m *GeoIPMatcher) Match(ip net.IP) bool {
   150  	switch len(ip) {
   151  	case 4:
   152  		return m.match4(binary.BigEndian.Uint32(ip))
   153  	case 16:
   154  		return m.match6(ipv6{
   155  			a: binary.BigEndian.Uint64(ip[0:8]),
   156  			b: binary.BigEndian.Uint64(ip[8:16]),
   157  		})
   158  	default:
   159  		return false
   160  	}
   161  }
   162  
   163  // GeoIPMatcherContainer is a container for GeoIPMatchers. It keeps unique copies of GeoIPMatcher by country code.
   164  type GeoIPMatcherContainer struct {
   165  	matchers []*GeoIPMatcher
   166  }
   167  
   168  // Add adds a new GeoIP set into the container.
   169  // If the country code of GeoIP is not empty, GeoIPMatcherContainer will try to find an existing one, instead of adding a new one.
   170  func (c *GeoIPMatcherContainer) Add(geoip *GeoIP) (*GeoIPMatcher, error) {
   171  	if len(geoip.CountryCode) > 0 {
   172  		for _, m := range c.matchers {
   173  			if m.countryCode == geoip.CountryCode {
   174  				return m, nil
   175  			}
   176  		}
   177  	}
   178  
   179  	m := &GeoIPMatcher{
   180  		countryCode: geoip.CountryCode,
   181  	}
   182  	if err := m.Init(geoip.Cidr); err != nil {
   183  		return nil, err
   184  	}
   185  	if len(geoip.CountryCode) > 0 {
   186  		c.matchers = append(c.matchers, m)
   187  	}
   188  	return m, nil
   189  }
   190  
   191  var (
   192  	globalGeoIPContainer GeoIPMatcherContainer
   193  )