ffi-geoip 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.travis.yml +14 -0
- data/Gemfile +25 -0
- data/Guardfile +17 -0
- data/MIT-LICENSE +22 -0
- data/README.rdoc +22 -0
- data/Rakefile +29 -0
- data/ffi-geoip.gemspec +26 -0
- data/lib/ffi-geoip.rb +470 -0
- data/lib/ffi-geoip/base.rb +68 -0
- data/lib/ffi-geoip/city.rb +26 -0
- data/lib/ffi-geoip/country.rb +24 -0
- data/lib/ffi-geoip/isp.rb +20 -0
- data/lib/ffi-geoip/organization.rb +20 -0
- data/lib/ffi-geoip/record.rb +53 -0
- data/lib/ffi-geoip/tools.rb +26 -0
- data/lib/ffi-geoip/version.rb +6 -0
- data/test/city_tests.rb +77 -0
- data/test/country_tests.rb +51 -0
- data/test/geoip_tests.rb +50 -0
- data/test/isp_tests.rb +54 -0
- data/test/organization_tests.rb +61 -0
- data/test/test_helper.rb +53 -0
- metadata +107 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module GeoIP
|
4
|
+
module Base
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def release(ptr)
|
11
|
+
FFIGeoIP.GeoIP_delete(ptr)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def value_to_addr(value)
|
16
|
+
case value
|
17
|
+
when Numeric
|
18
|
+
GeoIP.num_to_addr(value)
|
19
|
+
|
20
|
+
when /^\d+\.\d+\.\d+\.\d+$/
|
21
|
+
value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def initialize_geoip(file, load_option = nil, check_cache = nil)
|
27
|
+
if load_option.nil?
|
28
|
+
load_option = :memory
|
29
|
+
end
|
30
|
+
|
31
|
+
flags = check_load_option(load_option) or
|
32
|
+
raise TypeError.new("the second option must be :memory, :filesystem, or :index")
|
33
|
+
|
34
|
+
if check_cache
|
35
|
+
flags |= GeoIPOptions[:check_cache]
|
36
|
+
end
|
37
|
+
|
38
|
+
if !File.exists?(file)
|
39
|
+
raise enoent_exception
|
40
|
+
else
|
41
|
+
@ptr = FFI::AutoPointer.new(
|
42
|
+
FFIGeoIP.GeoIP_open(file, flags),
|
43
|
+
self.class.method(:release)
|
44
|
+
)
|
45
|
+
|
46
|
+
if @ptr.null?
|
47
|
+
raise_enoent
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def enoent_exception
|
53
|
+
Errno::ENOENT.new("Problem opening database")
|
54
|
+
end
|
55
|
+
|
56
|
+
def check_load_option(load_option)
|
57
|
+
case load_option
|
58
|
+
when :memory
|
59
|
+
GeoIP::GeoIPOptions[:memory_cache]
|
60
|
+
when :filesystem
|
61
|
+
GeoIP::GeoIPOptions[:standard]
|
62
|
+
when :index
|
63
|
+
GeoIP::GeoIPOptions[:index_cache]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module GeoIP
|
4
|
+
class City
|
5
|
+
include GeoIP::Base
|
6
|
+
include GeoIP::Tools
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
initialize_geoip(*args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def look_up(addr)
|
13
|
+
addr = value_to_addr(addr)
|
14
|
+
check_type(addr, String)
|
15
|
+
|
16
|
+
record = Record.new(FFIGeoIP.GeoIP_record_by_addr(@ptr, addr))
|
17
|
+
|
18
|
+
if !record.null?
|
19
|
+
hash = record.to_h
|
20
|
+
FFIGeoIP.GeoIPRecord_delete(record)
|
21
|
+
hash
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module GeoIP
|
4
|
+
class Country
|
5
|
+
include GeoIP::Base
|
6
|
+
include GeoIP::Tools
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
initialize_geoip(*args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def look_up(addr)
|
13
|
+
addr = value_to_addr(addr)
|
14
|
+
check_type(addr, String)
|
15
|
+
|
16
|
+
fix_encoding(
|
17
|
+
:country_code => FFIGeoIP.GeoIP_country_code_by_addr(@ptr, addr),
|
18
|
+
:country_code3 => FFIGeoIP.GeoIP_country_code3_by_addr(@ptr, addr),
|
19
|
+
:country_name => FFIGeoIP.GeoIP_country_name_by_addr(@ptr, addr)
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module GeoIP
|
4
|
+
class ISP
|
5
|
+
include GeoIP::Base
|
6
|
+
include GeoIP::Tools
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
initialize_geoip(*args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def look_up(addr)
|
13
|
+
addr = value_to_addr(addr)
|
14
|
+
check_type(addr, String)
|
15
|
+
|
16
|
+
fix_encoding(:isp => FFIGeoIP.GeoIP_name_by_addr(@ptr, addr))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module GeoIP
|
4
|
+
class Organization
|
5
|
+
include GeoIP::Base
|
6
|
+
include GeoIP::Tools
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
initialize_geoip(*args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def look_up(addr)
|
13
|
+
addr = value_to_addr(addr)
|
14
|
+
check_type(addr, String)
|
15
|
+
|
16
|
+
fix_encoding(:name => FFIGeoIP.GeoIP_name_by_addr(@ptr, addr))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module GeoIP
|
4
|
+
class Record < FFI::Struct
|
5
|
+
include Tools
|
6
|
+
|
7
|
+
layout(
|
8
|
+
:country_code, :string,
|
9
|
+
:country_code3, :string,
|
10
|
+
:country_name, :string,
|
11
|
+
:region, :string,
|
12
|
+
:city, :string,
|
13
|
+
:postal_code, :string,
|
14
|
+
:latitude, :float,
|
15
|
+
:longitude, :float,
|
16
|
+
:dma_code, :int,
|
17
|
+
:area_code, :int,
|
18
|
+
:charset, :int,
|
19
|
+
:continent_code, :string,
|
20
|
+
:country_conf, :uchar,
|
21
|
+
:region_conf, :uchar,
|
22
|
+
:city_conf, :uchar,
|
23
|
+
:postal_conf, :uchar,
|
24
|
+
:accuracy_radius, :int
|
25
|
+
)
|
26
|
+
|
27
|
+
HASH_KEYS = [
|
28
|
+
:country_code,
|
29
|
+
:country_code3,
|
30
|
+
:country_name,
|
31
|
+
:region,
|
32
|
+
:city,
|
33
|
+
:postal_code,
|
34
|
+
:latitude,
|
35
|
+
:longitude,
|
36
|
+
:dma_code,
|
37
|
+
:area_code
|
38
|
+
].freeze
|
39
|
+
|
40
|
+
def to_h
|
41
|
+
hash = HASH_KEYS.each_with_object({}) do |key, memo|
|
42
|
+
memo[key] = self[key] unless self[key].nil?
|
43
|
+
end
|
44
|
+
|
45
|
+
if self[:region]
|
46
|
+
hash[:region_name] = FFIGeoIP.GeoIP_region_name_by_code(self[:country_code], self[:region])
|
47
|
+
end
|
48
|
+
|
49
|
+
fix_encoding(hash)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module GeoIP
|
4
|
+
module Tools
|
5
|
+
private
|
6
|
+
def check_enum_value(enum, value)
|
7
|
+
enum[value] or
|
8
|
+
raise TypeError.new("Couldn't find valid #{enum.tag} value: #{value}")
|
9
|
+
end
|
10
|
+
|
11
|
+
def check_type(value, *types)
|
12
|
+
if !types.include?(value.class)
|
13
|
+
type_names = types.collect(&:name).join(' or ')
|
14
|
+
|
15
|
+
raise TypeError.new("wrong argument type #{value.class.name} (expected #{type_names})")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def fix_encoding(hash)
|
20
|
+
hash.each do |_, value|
|
21
|
+
value.encode!('UTF-8', 'ISO-8859-1') if value.is_a?(String)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
data/test/city_tests.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
$: << File.dirname(__FILE__)
|
4
|
+
require 'test_helper'
|
5
|
+
|
6
|
+
class GeoIPCityTest < Minitest::Test
|
7
|
+
PITTSBURGH = {
|
8
|
+
:country_code => "US",
|
9
|
+
:country_code3 => "USA",
|
10
|
+
:country_name => "United States",
|
11
|
+
:region => "PA",
|
12
|
+
:city => "Pittsburgh",
|
13
|
+
:latitude => 40.44060134887695,
|
14
|
+
:longitude => -79.99590301513672,
|
15
|
+
:dma_code => 508,
|
16
|
+
:area_code => 412,
|
17
|
+
:region_name => "Pennsylvania"
|
18
|
+
}
|
19
|
+
|
20
|
+
PITTSBURGH_IP = '12.87.118.0'
|
21
|
+
|
22
|
+
def assert_look_up(db, expected = PITTSBURGH, lookup = PITTSBURGH_IP)
|
23
|
+
hash = db.look_up(lookup)
|
24
|
+
assert_kind_of(Hash, hash)
|
25
|
+
assert_equal(expected, hash)
|
26
|
+
end
|
27
|
+
|
28
|
+
def assert_raises_type_error(db)
|
29
|
+
assert_raises(TypeError) do
|
30
|
+
db.look_up(nil)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_construction_default
|
35
|
+
db = GeoIP::City.new(CITY_DB)
|
36
|
+
assert_raises_type_error(db)
|
37
|
+
assert_look_up(db)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_construction_index
|
41
|
+
db = GeoIP::City.new(CITY_DB, :index)
|
42
|
+
assert_raises_type_error(db)
|
43
|
+
assert_look_up(db)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_construction_filesystem
|
47
|
+
db = GeoIP::City.new(CITY_DB, :filesystem)
|
48
|
+
assert_raises_type_error(db)
|
49
|
+
assert_look_up(db)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_construction_memory
|
53
|
+
db = GeoIP::City.new(CITY_DB, :memory)
|
54
|
+
assert_raises_type_error(db)
|
55
|
+
assert_look_up(db)
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_construction_filesystem_check
|
59
|
+
db = GeoIP::City.new(CITY_DB, :filesystem, true)
|
60
|
+
assert_raises_type_error(db)
|
61
|
+
assert_look_up(db)
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_bad_db_file
|
65
|
+
assert_raises(Errno::ENOENT) do
|
66
|
+
GeoIP::City.new('/supposed-to-fail')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_character_encoding_converted_to_utf8_first
|
71
|
+
db = GeoIP::City.new(CITY_DB, :filesystem, true)
|
72
|
+
hash = db.look_up('89.160.20.112')
|
73
|
+
assert_equal('UTF-8', hash[:city].encoding.to_s)
|
74
|
+
assert_equal('Linköping', hash[:city])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
$: << File.dirname(__FILE__)
|
4
|
+
require 'test_helper'
|
5
|
+
|
6
|
+
class GeoIPCountryTest < Minitest::Test
|
7
|
+
UNITED_STATES = {
|
8
|
+
:country_code => "US",
|
9
|
+
:country_code3 => "USA",
|
10
|
+
:country_name => "United States"
|
11
|
+
}
|
12
|
+
|
13
|
+
UNITED_STATES_IP = '12.87.118.0'
|
14
|
+
|
15
|
+
def assert_look_up(db, expected = UNITED_STATES, lookup = UNITED_STATES_IP)
|
16
|
+
hash = db.look_up(lookup)
|
17
|
+
assert_kind_of(Hash, hash)
|
18
|
+
assert_equal(expected, hash)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_construction_default
|
22
|
+
db = GeoIP::Country.new(COUNTRY_DB)
|
23
|
+
assert_raises_type_error(db)
|
24
|
+
assert_look_up(db)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_construction_filesystem
|
28
|
+
db = GeoIP::Country.new(COUNTRY_DB, :filesystem)
|
29
|
+
assert_raises_type_error(db)
|
30
|
+
assert_look_up(db)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_construction_memory
|
34
|
+
db = GeoIP::Country.new(COUNTRY_DB, :memory)
|
35
|
+
assert_raises_type_error(db)
|
36
|
+
assert_look_up(db)
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_construction_filesystem_check
|
40
|
+
db = GeoIP::Country.new(COUNTRY_DB, :filesystem, true)
|
41
|
+
assert_raises_type_error(db)
|
42
|
+
assert_look_up(db)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_bad_db_file
|
46
|
+
assert_raises(Errno::ENOENT) do
|
47
|
+
GeoIP::Country.new('/supposed-to-fail')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
data/test/geoip_tests.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
$: << File.dirname(__FILE__)
|
4
|
+
require 'test_helper'
|
5
|
+
|
6
|
+
class GeoIPTest < Minitest::Test
|
7
|
+
def setup
|
8
|
+
@ip = "24.24.24.24"
|
9
|
+
@ipnum = 16777216 * 24 + 65536 * 24 + 256 * 24 + 24
|
10
|
+
|
11
|
+
@large_ip = "245.245.245.245"
|
12
|
+
@large_ipnum = 16777216 * 245 + 65536 * 245 + 256 * 245 + 245
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_addr_to_num_converts_an_ip_to_an_ipnum
|
16
|
+
assert_equal @ipnum, GeoIP.addr_to_num(@ip)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_addr_to_num_converts_large_ips_to_an_ipnum_correctly
|
20
|
+
assert_equal @large_ipnum, GeoIP.addr_to_num(@large_ip)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_addr_to_num_expects_an_ip_string
|
24
|
+
assert_raises TypeError do
|
25
|
+
GeoIP.addr_to_num(nil)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_addr_to_num_returns_zero_for_an_illformed_ip_string
|
30
|
+
assert_equal 0, GeoIP.addr_to_num("foo.bar")
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_num_to_addr_converts_an_ipnum_to_an_ip
|
34
|
+
assert_equal @ip, GeoIP.num_to_addr(@ipnum)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_num_to_addr_converts_large_ipnums_to_an_ip_correctly
|
38
|
+
assert_equal @large_ip, GeoIP.num_to_addr(@large_ipnum)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_num_to_addr_expects_a_numeric_ip
|
42
|
+
assert_raises TypeError do
|
43
|
+
GeoIP.num_to_addr(nil)
|
44
|
+
end
|
45
|
+
assert_raises TypeError do
|
46
|
+
GeoIP.num_to_addr("foo.bar")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
data/test/isp_tests.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
$: << File.dirname(__FILE__)
|
4
|
+
require 'test_helper'
|
5
|
+
|
6
|
+
class GeoIPISPTest < Minitest::Test
|
7
|
+
ISP = {
|
8
|
+
:isp => "AT&T Services"
|
9
|
+
}
|
10
|
+
|
11
|
+
ISP_IP = '12.87.118.0'
|
12
|
+
|
13
|
+
def assert_look_up(db, expected = ISP, lookup = ISP_IP)
|
14
|
+
hash = db.look_up(lookup)
|
15
|
+
assert_kind_of(Hash, hash)
|
16
|
+
assert_equal(expected, hash)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_construction_default
|
20
|
+
db = GeoIP::ISP.new(ISP_DB)
|
21
|
+
assert_raises_type_error(db)
|
22
|
+
assert_look_up(db)
|
23
|
+
end
|
24
|
+
|
25
|
+
# def test_construction_index
|
26
|
+
# db = GeoIP::ISP.new(ISP_DB, :index)
|
27
|
+
# assert_look_up(db, ISP_IP, :isp, 'Bell Aliant')
|
28
|
+
# end
|
29
|
+
|
30
|
+
def test_construction_filesystem
|
31
|
+
db = GeoIP::ISP.new(ISP_DB, :filesystem)
|
32
|
+
assert_raises_type_error(db)
|
33
|
+
assert_look_up(db)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_construction_memory
|
37
|
+
db = GeoIP::ISP.new(ISP_DB, :memory)
|
38
|
+
assert_raises_type_error(db)
|
39
|
+
assert_look_up(db)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_construction_filesystem_check
|
43
|
+
db = GeoIP::ISP.new(ISP_DB, :filesystem, true)
|
44
|
+
assert_raises_type_error(db)
|
45
|
+
assert_look_up(db)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_bad_db_file
|
49
|
+
assert_raises Errno::ENOENT do
|
50
|
+
GeoIP::ISP.new('/supposed-to-fail')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|