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