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