class IpLocatorCn::QQWry
Constants
- CITY_DIRECTLY
- IP_ENTRY_BYTES
- ISP
- PROVINCES
Attributes
debug[R]
first_ip_pos[R]
last_ip_pos[R]
total_ips[R]
Public Class Methods
new(dat_path: nil, live_dat: false, debug: false)
click to toggle source
# File lib/ip_locator_cn/qqwry.rb, line 11 def initialize(dat_path: nil, live_dat: false, debug: false) @debug = debug if live_dat live_load_dat parse_dat_info elsif dat_path @io = File.open(dat_path, 'rb') parse_dat_info end end
Public Instance Methods
extract_location(str)
click to toggle source
提取 四川省成都市高新区 => 中国 四川 成都市 高新区
# File lib/ip_locator_cn/qqwry.rb, line 111 def extract_location(str) collection = {} seperator_sheng = '省' seperator_shi = '市' seperator_xian = '县' seperator_qu = '区' is_city_directly = false # 省份 if str.include?(seperator_sheng) collection[:province], str = str.split(seperator_sheng, 2) else province = (PROVINCES + CITY_DIRECTLY).find { |x| str.start_with?(x) } if province collection[:province] = province is_city_directly = CITY_DIRECTLY.include?(province) str = str[province.length..-1] str = str[1..-1] if str.start_with?(seperator_shi) # 上海市浦东区 => [上海, 市浦东区] => [上海, 浦东区] end end return { country: str } unless collection.key?(:province) # 市 if str.include?(seperator_shi) collection[:city], str = str.split(seperator_shi, 2) collection[:city] += seperator_shi end # 县 if str.include?(seperator_xian) collection[:county], str = str.split(seperator_xian, 2) collection[:county] += seperator_xian end # 区 if !collection.key?(:county) && str.include?(seperator_qu) collection[:county], str = str.split(seperator_qu, 2) collection[:county] += seperator_qu if is_city_directly collection[:city] = collection[:county] collection.delete(:county) end end collection[:country] = '中国' collection end
location_from_ip(ip)
click to toggle source
# File lib/ip_locator_cn/qqwry.rb, line 53 def location_from_ip(ip) info = {} pos = locate_ip_pos(ip) log "pos is #{pos}" # 用户IP所在范围的开始地址 seek(pos) begin_ip = long2ip(read_ip) log "begin_ip is #{begin_ip}" # 用户IP所在范围的结束地址 offset = getlong3 seek(offset) endip = long2ip(read_ip) log "endip is #{endip}" log "offset is #{offset}" byte = read(1) # 标志字节 case byte.ord when 1 # 标志字节为1,表示国家和区域信息都被同时重定向 country_offset = getlong3 # 重定向地址 seek(country_offset) byte = read(1) # 标志字节 case byte.ord when 2 seek(getlong3) info[:country] = getstring seek(country_offset + 4) info[:area] = getarea else info[:country] = getstring(byte) info[:area] = getarea end when 2 # 标志字节为2,表示国家信息被重定向 seek(getlong3) info[:country] = getstring seek(offset + 8) info[:area] = getarea else info[:country] = getstring(byte) info[:area] = getarea end info[:country] = encoding_convert(info[:country]) info[:area] = encoding_convert(info[:area]) log "country is #{info[:country]}" log "area is #{info[:area]}" if info[:country] == ' CZ88.NET' || info[:country] == '纯真网络' info[:country] = 'Unknown' end info[:area] = '' if info[:area] == ' CZ88.NET' info end
resolve(ip)
click to toggle source
return: <Hash> result result 输入的ip result 国家 如 中国 result 省份信息 如 河北省 result 市区 如 邢台市 result 郡县 如 威县 result 运营商 如 联通 result 最完整的信息 如 中国河北省邢台市威县新科网吧(北外街) result 解析之前的原始信息 result 解析之前的原始信息
# File lib/ip_locator_cn/qqwry.rb, line 32 def resolve(ip) location = location_from_ip(ip) extract_location(location[:country]).tap do |result| result[:ip] = ip result[:country] ||= '' result[:province] ||= '' result[:city] ||= '' result[:county] ||= '' result[:isp] = get_isp(location[:area]) || '' result[:area] = [ result[:country], result[:province], result[:city], result[:county], location[:area] ].join result[:origin_country] = location[:country] result[:origin_area] = location[:area] end end
Private Instance Methods
download(url)
click to toggle source
# File lib/ip_locator_cn/qqwry.rb, line 162 def download(url) log "downloading #{url}" open(url) end
encoding_convert(str)
click to toggle source
# File lib/ip_locator_cn/qqwry.rb, line 281 def encoding_convert(str) ec = Encoding::Converter.new('GBK', 'UTF-8') result = ec.convert(str) ec.finish result end
get_isp(str)
click to toggle source
# File lib/ip_locator_cn/qqwry.rb, line 190 def get_isp(str) ISP.find { |x| str.include?(x) } end
getarea()
click to toggle source
# File lib/ip_locator_cn/qqwry.rb, line 268 def getarea char = read(1) case char.ord when 0 return '' when 1, 2 seek(getlong3) return getstring else return getstring(char) end end
getlong3()
click to toggle source
# File lib/ip_locator_cn/qqwry.rb, line 241 def getlong3 (read(3) + "\0").unpack('V')[0] end
getlong4()
click to toggle source
# File lib/ip_locator_cn/qqwry.rb, line 237 def getlong4 read(4).unpack('V')[0] end
getstring(data = '')
click to toggle source
# File lib/ip_locator_cn/qqwry.rb, line 258 def getstring(data = '') char = read(1) while char.ord > 0 data += char char = read(1) end data end
ip2long(ip)
click to toggle source
# File lib/ip_locator_cn/qqwry.rb, line 245 def ip2long(ip) parts = ip.split('.').map(&:to_i) ipparts2long(parts) end
ipparts2long(parts)
click to toggle source
# File lib/ip_locator_cn/qqwry.rb, line 250 def ipparts2long(parts) 0.upto(3).map { |i| parts[i] << ((3 - i) * 8) }.sum end
live_load_dat()
click to toggle source
# File lib/ip_locator_cn/qqwry.rb, line 167 def live_load_dat require 'open-uri' (copywrite = download(IpLocatorCn.copywrite_url).read) && nil (qqwry = download(IpLocatorCn.live_data_url).read.bytes) && nil key = copywrite.unpack('V6')[5] log "qqwry decoding key is #{key}" (0...0x200).each do |i| key *= 0x805 key += 1 key &= 0xFF qqwry[i] ^= key end (qqwry = Zlib::Inflate.inflate(qqwry.pack('C*'))) && nil @io = StringIO.new(qqwry) end
locate_ip_pos(ip)
click to toggle source
b-tree search ip from qqwry.dat
# File lib/ip_locator_cn/qqwry.rb, line 195 def locate_ip_pos(ip) x = ip2long(ip) min = 0 max = total_ips while min <= max # b-tree search i = ((min + max) / 2).floor pos = first_ip_pos + i * IP_ENTRY_BYTES seek(pos) # get middle ip y = read_ip if x < y # user ip < middle ip max = i - 1 else # user ip > middle ip, read next ip entry read(3) y = read_ip if x > y # user ip > next ip min = i + 1 else return pos end end end -1 end
log(msg)
click to toggle source
# File lib/ip_locator_cn/qqwry.rb, line 288 def log(msg) puts "[#{Time.now}] => #{msg}" if debug end
long2ip(n)
click to toggle source
# File lib/ip_locator_cn/qqwry.rb, line 254 def long2ip(n) 0.upto(3).map { |i| (n >> ((3 - i) * 8)) & 0xFF }.join('.') end
parse_dat_info()
click to toggle source
# File lib/ip_locator_cn/qqwry.rb, line 183 def parse_dat_info @first_ip_pos = getlong4 @last_ip_pos = getlong4 @total_ips = (last_ip_pos - first_ip_pos) / IP_ENTRY_BYTES log "total ip ranges: #{total_ips}" end
read(n)
click to toggle source
# File lib/ip_locator_cn/qqwry.rb, line 229 def read(n) @io.read(n) end
read_ip()
click to toggle source
# File lib/ip_locator_cn/qqwry.rb, line 233 def read_ip ipparts2long(read(4).reverse.bytes) end
seek(n)
click to toggle source
# File lib/ip_locator_cn/qqwry.rb, line 225 def seek(n) @io.seek(n) end