ffi-geoip 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|