You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
281 lines
7.6 KiB
Lua
281 lines
7.6 KiB
Lua
#!/usr/bin/env lua
|
|
local ngx = require('ngx')
|
|
|
|
local setmetatable = setmetatable
|
|
local byte = string.byte
|
|
local match = string.match
|
|
local rawget = rawget
|
|
local cjson = require "cjson"
|
|
|
|
local ok, new_tab = pcall(require, "table.new")
|
|
if not ok or type(new_tab) ~= "function" then
|
|
new_tab = function(narr, nrec) return {} end
|
|
end
|
|
|
|
local _M = new_tab(0, 54)
|
|
|
|
local mt = { __index = _M }
|
|
|
|
_M._VERSION = '0.01'
|
|
|
|
-- 测试TOKEN 及 地址
|
|
local token = "cc87f3c77747bccbaaee35006da1ebb65e0bad57"
|
|
local ipIpUrl = "http://freeapi.ipip.net/"
|
|
|
|
-- 切割字符串
|
|
local function split(s, p)
|
|
local rt = {}
|
|
string.gsub(s, '[^' .. p .. ']+', function(w) table.insert(rt, w) end)
|
|
return rt
|
|
end
|
|
|
|
-- 拓展
|
|
string.split = function(str, pattern)
|
|
pattern = pattern or "[^%s]+"
|
|
if pattern:len() == 0 then pattern = "[^%s]+" end
|
|
local parts = { __index = table.insert }
|
|
setmetatable(parts, parts)
|
|
str:gsub(pattern, parts)
|
|
setmetatable(parts, nil)
|
|
parts.__index = nil
|
|
return parts
|
|
end
|
|
|
|
|
|
-- 转成整型
|
|
local function bit32lshift(b, disp)
|
|
return (b * 2 ^ disp) % 2 ^ 32
|
|
end
|
|
|
|
-- 转换ip
|
|
local function byteToUint32(a, b, c, d)
|
|
local _int = 0
|
|
if a then
|
|
_int = _int + bit32lshift(a, 24)
|
|
end
|
|
_int = _int + bit32lshift(b, 16)
|
|
_int = _int + bit32lshift(c, 8)
|
|
_int = _int + d
|
|
if _int >= 0 then
|
|
return _int
|
|
else
|
|
return _int + math.pow(2, 32)
|
|
end
|
|
end
|
|
|
|
-- 返回数据模版
|
|
local response_template = {
|
|
"country", -- // 国家
|
|
"city", -- // 省会或直辖市(国内)
|
|
"region", -- // 地区或城市 (国内)
|
|
"place", -- // 学校或单位 (国内)
|
|
"operator", -- // 运营商字段(只有购买了带有运营商版本的数据库才会有)
|
|
"latitude", -- // 纬度 (每日版本提供)
|
|
"longitude", -- // 经度 (每日版本提供)
|
|
"timeZone", -- // 时区一, 可能不存在 (每日版本提供)
|
|
"timeZoneCode", -- // 时区二, 可能不存在 (每日版本提供)
|
|
"administrativeAreaCode", -- // 中国行政区划代码 (每日版本提供)
|
|
"internationalPhoneCode", -- // 国际电话代码 (每日版本提供)
|
|
"countryTwoDigitCode", -- // 国家二位代码 (每日版本提供)
|
|
"worldContinentCode" -- // 世界大洲代码 (每日版本提供)
|
|
}
|
|
|
|
-- 转成 table类型
|
|
local function toTable(location)
|
|
local response = {}
|
|
for k, v in ipairs(location) do
|
|
response[response_template[k]] = v
|
|
end
|
|
|
|
return response
|
|
end
|
|
|
|
-- 发送请求
|
|
local function sendRequest(url, method, body, headers)
|
|
local http = require "resty.http"
|
|
local httpc = http.new()
|
|
local res, err = httpc:request_uri(url, {
|
|
method = method,
|
|
body = body,
|
|
headers = headers
|
|
})
|
|
|
|
if not res then
|
|
ngx.log(ngx.ERR, "failed to request: " .. err .. url)
|
|
return nil, err
|
|
end
|
|
|
|
if 200 ~= res.status then
|
|
ngx.log(ngx.ERR, res.status)
|
|
return nil, res.status
|
|
end
|
|
|
|
return res.body
|
|
end
|
|
|
|
-- 初始化
|
|
function _M.new(self, address, token)
|
|
return setmetatable({ _ipAddress = address, _token = token, _ipBinaryFilePath = require("config.app").ip_binary_file_path }, mt)
|
|
end
|
|
|
|
|
|
-- 从文件获取地区信息
|
|
function _M.ipLocation(self, ipstr)
|
|
local ipBinaryFilePath = rawget(self, "_ipBinaryFilePath")
|
|
if not ipBinaryFilePath then
|
|
ngx.log(ngx.ERR, ipBinaryFilePath)
|
|
return nil, " file ptah not initialized"
|
|
end
|
|
|
|
local ip1, ip2, ip3, ip4 = match(ipstr, "(%d+).(%d+).(%d+).(%d+)")
|
|
local ip_uint32 = byteToUint32(ip1, ip2, ip3, ip4)
|
|
local file = io.open(ipBinaryFilePath)
|
|
if file == nil then
|
|
return nil
|
|
end
|
|
|
|
local str = file:read(4)
|
|
local offset_len = byteToUint32(byte(str, 1), byte(str, 2), byte(str, 3), byte(str, 4))
|
|
|
|
local indexBuffer = file:read(offset_len - 4)
|
|
|
|
local tmp_offset = ip1 * 4
|
|
local start_len = byteToUint32(byte(indexBuffer, tmp_offset + 4), byte(indexBuffer, tmp_offset + 3), byte(indexBuffer, tmp_offset + 2), byte(indexBuffer, tmp_offset + 1))
|
|
|
|
local max_comp_len = offset_len - 1028
|
|
local start = start_len * 8 + 1024 + 1
|
|
local index_offset = -1
|
|
local index_length = -1
|
|
while start < max_comp_len do
|
|
local find_uint32 = byteToUint32(byte(indexBuffer, start), byte(indexBuffer, start + 1), byte(indexBuffer, start + 2), byte(indexBuffer, start + 3))
|
|
if ip_uint32 <= find_uint32 then
|
|
index_offset = byteToUint32(0, byte(indexBuffer, start + 6), byte(indexBuffer, start + 5), byte(indexBuffer, start + 4))
|
|
index_length = byte(indexBuffer, start + 7)
|
|
break
|
|
end
|
|
start = start + 8
|
|
end
|
|
|
|
if index_offset == -1 or index_length == -1 then
|
|
return nil
|
|
end
|
|
|
|
local offset = offset_len + index_offset - 1024
|
|
|
|
file:seek("set", offset)
|
|
|
|
return file:read(index_length)
|
|
end
|
|
|
|
-- 获取所有信息
|
|
function _M.location(self)
|
|
local ipAddress = rawget(self, "_ipAddress")
|
|
if not ipAddress then
|
|
return nil, "not initialized"
|
|
end
|
|
|
|
local address = self:ipLocation(ipAddress)
|
|
if not address then
|
|
ngx.log(ngx.ERR, { "ip address data nil" })
|
|
return nil, "ip address data nil"
|
|
end
|
|
|
|
if type(address) == "string" then
|
|
return toTable(split(address, "%s+"))
|
|
end
|
|
|
|
return address
|
|
end
|
|
|
|
-- 通过api获取
|
|
function _M.locationApi(self, sid, uid)
|
|
local ipAddress = rawget(self, "_ipAddress")
|
|
if not ipAddress then
|
|
return nil, "not initialized"
|
|
end
|
|
|
|
local _token = rawget(self, "_token")
|
|
|
|
local myToken = (_token and _token) or token
|
|
|
|
local sign, err = ngx.md5("addr=" .. ipAddress .. "&token=" .. myToken)
|
|
|
|
if not sign then
|
|
return nil, err
|
|
end
|
|
|
|
local url = ipIpUrl .. "find"
|
|
|
|
local headers = {
|
|
["Token"] = myToken
|
|
}
|
|
|
|
local params = "addr=" .. ipAddress .. "&sid=" .. sid .. "&uid=" .. uid .. "&sig=" .. sign
|
|
|
|
local body, err = sendRequest(url, "GET", params, headers)
|
|
|
|
if not body or #body < 1 then
|
|
return nil, err
|
|
end
|
|
|
|
-- local body = [[{"ret":"ok","data":["中国","天津","天津","","鹏博士","39.128399","117.185112","Asia/Shanghai","UTC+8","120000","86","CN","AP"]}]]
|
|
local response = cjson.decode(body)
|
|
|
|
if not response.data then
|
|
return response
|
|
end
|
|
|
|
return toTable(response.data)
|
|
end
|
|
|
|
-- 通过免费的api获取
|
|
function _M.locationApiFree(self)
|
|
local ipAddress = rawget(self, "_ipAddress")
|
|
if not ipAddress then
|
|
return nil, "not initialized"
|
|
end
|
|
|
|
local url = ipIpUrl .. ipAddress
|
|
|
|
local headers = {
|
|
["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
|
["Cache-Control"] = "no-cache",
|
|
["Connection"] = "keep-alive",
|
|
["User-Agent"] = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
|
|
["Content-Type"] = "application/x-www-form-urlencoded",
|
|
}
|
|
|
|
local body, err = sendRequest(url, "GET", "", headers)
|
|
|
|
if not body then
|
|
return nil, err
|
|
end
|
|
|
|
return toTable(cjson.decode(body))
|
|
end
|
|
|
|
-- 获取当前可访问状态
|
|
function _M.apiStatus(self, token)
|
|
if not token then
|
|
local token = rawget(self, "_token")
|
|
if not token then
|
|
return nil, "not initialized"
|
|
end
|
|
end
|
|
|
|
local url = ipIpUrl .. "find_status"
|
|
|
|
local headers = {
|
|
["Token"] = token
|
|
}
|
|
|
|
local body, err = sendRequest(url, "GET", "", headers)
|
|
|
|
if not body then
|
|
return nil, err
|
|
end
|
|
|
|
return cjson.decode(body)
|
|
end
|
|
|
|
return _M |