From dbc9df520e6969962d5edf4e839696d62f9b9045 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Wed, 28 Dec 2016 11:19:29 +0300 Subject: [PATCH 1/8] Add. `safe` mode to mmdb object --- mmdb/init.lua | 53 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/mmdb/init.lua b/mmdb/init.lua index 3dcc654..ba250aa 100644 --- a/mmdb/init.lua +++ b/mmdb/init.lua @@ -14,11 +14,19 @@ local geodb_mt = { local data_types = {} local getters = {} -local function open_db(filename) - local fd = assert(io.open(filename, "rb")) +local function fail(safe, ...) + if safe then return nil, ... end + error(..., 2) +end + +local function open_db(safe, filename) + local fd, err = io.open(filename, "rb") + if not fd then return fail(safe, err) end + local contents, err = fd:read("*a") fd:close() - assert(contents, err) + + if not contents then return fail(safe, err) end local start_metadata do -- Find data section seperator; at most it's 128kb from the end @@ -29,11 +37,12 @@ local function open_db(filename) start_metadata = e + 1 end if start_metadata == nil then - error("Invalid MaxMind Database") + return fail(safe, "Invalid MaxMind Database") end end local self = setmetatable({ + safe = not not safe; contents = contents; start_metadata = start_metadata; data = nil; @@ -47,7 +56,7 @@ local function open_db(filename) local getter = getters[data.record_size] if getter == nil then - error("Unsupported record size: " .. data.record_size) + return self:fail("Unsupported record size: " .. tostring(data.record_size)) end self.left, self.right, self.record_length = getter.left, getter.right, getter.record_length @@ -60,6 +69,10 @@ local function open_db(filename) return self end +function geodb_methods:fail(...) + return fail(self.safe, ...) +end + function geodb_methods:read_data(base, offset) local control_byte = self.contents:byte(base + offset) offset = offset + 1 @@ -320,10 +333,10 @@ getters[32] = { function geodb_methods:search(bits, node) node = node or 0 local seen = { [node] = true } - for _, direction in ipairs(bits) do + for i = 1, #bits do local offset = node * self.record_length + 1 local record_value - if direction then + if bits[i] then record_value = self:right(offset) else record_value = self:left(offset) @@ -368,12 +381,15 @@ end local function ipv4_to_bit_array(str) local o1, o2, o3, o4 = str:match("(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)") - assert(o1, "invalid IPv4 address") + if not o1 then return "invalid IPv4 address" end o1 = tonumber(o1, 10) o2 = tonumber(o2, 10) o3 = tonumber(o3, 10) o4 = tonumber(o4, 10) - assert(o1 <= 255 and o2 <= 255 and o3 <= 255 and o4 <= 255, "invalid IPv4 address") + if not (o1 <= 255 and o2 <= 255 and o3 <= 255 and o4 <= 255) then + return nil, "invalid IPv4 address" + end + return { math.floor(o1 / 128) % 2 == 1; math.floor(o1 / 64) % 2 == 1; @@ -411,7 +427,9 @@ local function ipv4_to_bit_array(str) end function geodb_methods:search_ipv4(str) - return select(2, self:search(ipv4_to_bit_array(str), self.ipv4_start)) + local bits, err = ipv4_to_bit_array(str) + if not bits then return self:fail(err) end + return select(2, self:search(bits, self.ipv4_start)) end local function ipv6_split(str) @@ -420,7 +438,7 @@ local function ipv6_split(str) for u16 in str:gmatch("(%x%x?%x?%x?):?") do n = n + 1 u16 = tonumber(u16, 16) - assert(u16, "invalid IPv6 address") + if not u16 then return nil, "invalid IPv6 address" end components[n] = u16 end return components, n @@ -429,9 +447,11 @@ end local function ipv6_to_bit_array(str) local a, b = str:match("^([%x:]-)::([%x:]*)$") local components, n = ipv6_split(a or str) + if not components then return nil, n end if a ~= nil then local end_components, m = ipv6_split(b) - assert(m+n <= 7, "invalid IPv6 address") + if not end_components then return nil, m end + if m+n > 7 then return nil, "invalid IPv6 address" end for i = n+1, 8-m do components[i] = 0 end @@ -439,7 +459,7 @@ local function ipv6_to_bit_array(str) components[i] = end_components[i-8+m] end else - assert(n == 8, "invalid IPv6 address") + if n ~= 8 then return nil, "invalid IPv6 address" end end -- Now components is an array of 16bit components local bits = {} @@ -453,9 +473,12 @@ local function ipv6_to_bit_array(str) end function geodb_methods:search_ipv6(str) - return select(2, self:search(ipv6_to_bit_array(str))) + local bits, err = ipv6_to_bit_array(str) + if not bits then return self:fail(err) end + return select(2, self:search(bits)) end return { - open = open_db; + open = function(...) return open_db(false, ...) end; + open_safe = function(...) return open_db(true, ...) end; } From 610fc82ddbfa37169aa4b620054e49a224616f7d Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Fri, 30 Dec 2016 10:56:52 +0300 Subject: [PATCH 2/8] Fix. return correct error for `invalid IPv4` Update. Do not export `fail` function Update. Code style. --- mmdb/init.lua | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/mmdb/init.lua b/mmdb/init.lua index ba250aa..260d283 100644 --- a/mmdb/init.lua +++ b/mmdb/init.lua @@ -21,12 +21,16 @@ end local function open_db(safe, filename) local fd, err = io.open(filename, "rb") - if not fd then return fail(safe, err) end + if not fd then + return fail(safe, err) + end local contents, err = fd:read("*a") fd:close() - if not contents then return fail(safe, err) end + if not contents then + return fail(safe, err) + end local start_metadata do -- Find data section seperator; at most it's 128kb from the end @@ -56,7 +60,7 @@ local function open_db(safe, filename) local getter = getters[data.record_size] if getter == nil then - return self:fail("Unsupported record size: " .. tostring(data.record_size)) + return fail(self.safe, "Unsupported record size: " .. tostring(data.record_size)) end self.left, self.right, self.record_length = getter.left, getter.right, getter.record_length @@ -69,10 +73,6 @@ local function open_db(safe, filename) return self end -function geodb_methods:fail(...) - return fail(self.safe, ...) -end - function geodb_methods:read_data(base, offset) local control_byte = self.contents:byte(base + offset) offset = offset + 1 @@ -381,7 +381,9 @@ end local function ipv4_to_bit_array(str) local o1, o2, o3, o4 = str:match("(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)") - if not o1 then return "invalid IPv4 address" end + if not o1 then + return nil, "invalid IPv4 address" + end o1 = tonumber(o1, 10) o2 = tonumber(o2, 10) o3 = tonumber(o3, 10) @@ -428,7 +430,9 @@ end function geodb_methods:search_ipv4(str) local bits, err = ipv4_to_bit_array(str) - if not bits then return self:fail(err) end + if not bits then + return fail(self.safe, err) + end return select(2, self:search(bits, self.ipv4_start)) end @@ -459,7 +463,9 @@ local function ipv6_to_bit_array(str) components[i] = end_components[i-8+m] end else - if n ~= 8 then return nil, "invalid IPv6 address" end + if n ~= 8 then + return nil, "invalid IPv6 address" + end end -- Now components is an array of 16bit components local bits = {} @@ -474,7 +480,9 @@ end function geodb_methods:search_ipv6(str) local bits, err = ipv6_to_bit_array(str) - if not bits then return self:fail(err) end + if not bits then + return fail(self.safe, err) + end return select(2, self:search(bits)) end From f22d865f9a8a0dd652233d23b101a3d9e19887b7 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Fri, 30 Dec 2016 11:50:09 +0300 Subject: [PATCH 3/8] Update code style --- mmdb/init.lua | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/mmdb/init.lua b/mmdb/init.lua index 260d283..beb83e8 100644 --- a/mmdb/init.lua +++ b/mmdb/init.lua @@ -442,7 +442,9 @@ local function ipv6_split(str) for u16 in str:gmatch("(%x%x?%x?%x?):?") do n = n + 1 u16 = tonumber(u16, 16) - if not u16 then return nil, "invalid IPv6 address" end + if not u16 then + return nil, "invalid IPv6 address" + end components[n] = u16 end return components, n @@ -451,11 +453,17 @@ end local function ipv6_to_bit_array(str) local a, b = str:match("^([%x:]-)::([%x:]*)$") local components, n = ipv6_split(a or str) - if not components then return nil, n end + if not components then + return nil, n + end if a ~= nil then local end_components, m = ipv6_split(b) - if not end_components then return nil, m end - if m+n > 7 then return nil, "invalid IPv6 address" end + if not end_components then + return nil, m + end + if m+n > 7 then + return nil, "invalid IPv6 address" + end for i = n+1, 8-m do components[i] = 0 end From c69fa0b1fb804c20bd9996a4828d94d705ec5dff Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Fri, 30 Dec 2016 12:13:50 +0300 Subject: [PATCH 4/8] Fix. Remove unrelated changes. --- mmdb/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mmdb/init.lua b/mmdb/init.lua index beb83e8..201b6f6 100644 --- a/mmdb/init.lua +++ b/mmdb/init.lua @@ -333,10 +333,10 @@ getters[32] = { function geodb_methods:search(bits, node) node = node or 0 local seen = { [node] = true } - for i = 1, #bits do + for _, direction in ipairs(bits) do local offset = node * self.record_length + 1 local record_value - if bits[i] then + if direction then record_value = self:right(offset) else record_value = self:left(offset) From 6ce8387e1db7945b43a8f14407db9da11c940668 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Fri, 30 Dec 2016 12:52:46 +0300 Subject: [PATCH 5/8] Update code style --- mmdb/init.lua | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/mmdb/init.lua b/mmdb/init.lua index 201b6f6..05ebf6c 100644 --- a/mmdb/init.lua +++ b/mmdb/init.lua @@ -494,7 +494,15 @@ function geodb_methods:search_ipv6(str) return select(2, self:search(bits)) end +local function open(...) + return open_db(false, ...) +end + +local function open_safe(...) + return open_db(true, ...) +end + return { - open = function(...) return open_db(false, ...) end; - open_safe = function(...) return open_db(true, ...) end; + open = open; + open_safe = open_safe; } From 900d78d118a0a79c53325394e8f3efd46cd6a5dd Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Thu, 6 Jul 2017 16:28:02 +0300 Subject: [PATCH 6/8] Update init.lua --- mmdb/init.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mmdb/init.lua b/mmdb/init.lua index 05ebf6c..fe96481 100644 --- a/mmdb/init.lua +++ b/mmdb/init.lua @@ -15,7 +15,9 @@ local data_types = {} local getters = {} local function fail(safe, ...) - if safe then return nil, ... end + if safe then + return nil, ... + end error(..., 2) end From 3b3524a2245c6d4cf400145c7a438a8dd7ce1ddb Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Wed, 6 Jan 2021 13:08:17 +0300 Subject: [PATCH 7/8] Fix. Use undefined error message --- mmdb/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mmdb/init.lua b/mmdb/init.lua index 530cda1..614e4cb 100644 --- a/mmdb/init.lua +++ b/mmdb/init.lua @@ -23,7 +23,7 @@ end local function new(contents, safe) if not contents then - return fail(safe, err) + return fail(safe, "No MaxMind Database content") end local start_metadata do From d14ce3c8e574cd7bfc27771f49d6d43bc09205b5 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Wed, 6 Jan 2021 18:58:55 +0300 Subject: [PATCH 8/8] Add. basic script for lookup --- geo.lua | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 geo.lua diff --git a/geo.lua b/geo.lua new file mode 100644 index 0000000..26429df --- /dev/null +++ b/geo.lua @@ -0,0 +1,50 @@ +#!/usr/bin/env ujit + +local argparse = require "argparse" +local mmdb = require "mmdb" +local json = require "dkjson" + +local function get_args() + local parser = argparse("geo", "Get info from MMDB database") + + parser:argument("ip", "IP v4/v6 address"):args "?" + + parser:flag("-i --info", "Infromation about database") + + parser + :option("-d --mmdb", "MaxMind2 database", "/usr/share/GeoIP/GeoIP2-City.mmdb") + + return parser:parse() +end + +local args = get_args() + +local geodb, err = mmdb.read(args.mmdb, true) +if not geodb then + io.stderr:write(err) + os.exit(-1) +end + +if args.info then + io.stdout:write(string.format( + "%s (%s); Support IPv%d; Nodes: %d\n", + geodb.data.database_type, + os.date("%F %T%z", tonumber(geodb.data.build_epoch)), + geodb.data.ip_version, + geodb.data.node_count + )) +end + +if args.ip then + local info + if string.find(args.ip, ':') then + info = geodb:search_ipv6(args.ip) + else + info = geodb:search_ipv4(args.ip) + end + if not info then + io.stderr:write('Not found info: ' .. args.ip) + os.exit(-2) + end + io.stdout:write(json.encode(info, { indent = true })) +end