diff --git a/.gitignore b/.gitignore index f62c75f..b1e36e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,11 @@ -pkg/ -mkmf.log -Makefile +.bundle +/data +/doc +/pkg conftest.dSYM/ +Gemfile.lock geoip.bundle -geoip.so geoip.o +geoip.so +Makefile +mkmf.log diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..b4e2a20 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gemspec diff --git a/Rakefile b/Rakefile index c8dadc9..3f01b01 100644 --- a/Rakefile +++ b/Rakefile @@ -1,55 +1,49 @@ require 'rake' require 'rake/clean' require 'rake/testtask' -require 'rake/rdoctask' -require 'rake/gempackagetask' +require 'rdoc/task' +require 'rubygems/package_task' +require "rake/extensiontask" -task :default => [:compile, :test] +task :default => [:test] -CLEAN.add "geoip.{o,bundle,so,obj,pdb,lib,def,exp}" -CLOBBER.add ['Makefile', 'mkmf.log','doc'] +CLOBBER.add 'doc', 'data' -Rake::RDocTask.new do |rdoc| - rdoc.rdoc_files.add ['README', 'geoip.c'] - rdoc.main = "README" # page to start on +RDoc::Task.new do |rdoc| + rdoc.main = "README.md" # page to start on + rdoc.rdoc_files.include("README.md", "ext/geoip/geoip.c") rdoc.rdoc_dir = 'doc/' # rdoc output folder end Rake::TestTask.new do |t| - t.test_files = ['test.rb'] t.verbose = true end -spec = Gem::Specification.new do |s| - s.name = 'geoip-c' - s.version = "0.8.1" +spec = Gem::Specification.load "geoip-c.gemspec" - s.authors = ['Ryah Dahl', 'Matt Todd', 'Andy Lindeman'] - s.email = ['alindeman@gmail.com', 'mtodd@highgroove.com'] - - s.summary = "A Binding to the GeoIP C library" - s.description = 'Generic GeoIP lookup tool. Based on the geoip_city RubyGem by Ryah Dahl' - s.homepage = "http://github.com/mtodd/geoip" +Gem::PackageTask.new(spec) do |pkg| + pkg.need_tar = true +end - s.files = ["Rakefile", "extconf.rb", "test.rb", "geoip.c", "README.md"] - s.test_files = ['test.rb'] - s.extensions = ['extconf.rb'] - s.require_path = '.' +Rake::ExtensionTask.new("geoip", spec) do |ext| end -Rake::GemPackageTask.new(spec) do |p| - p.need_tar = true - p.gem_spec = spec +task(:webpage) do + sh 'scp -r doc/* rydahl@rubyforge.org:/var/www/gforge-projects/geoip-city/' end -desc 'compile the extension' -task(:compile => 'Makefile') { sh 'make' } -file('Makefile' => "geoip.c") { ruby 'extconf.rb' } +directory "data" -task :install => [:gem] do - `env ARCHFLAGS="-arch i386" gem install pkg/geoip-c-0.5.0.gem -- --with-geoip-dir=/usr/local/GeoIP` +file "data/GeoLiteCity.dat" => ["data"] do |f| + url = "http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz" + + sh "curl #{url} -o data/GeoLiteCity.dat.gz" + sh "gunzip data/GeoLiteCity.dat.gz" + touch f.name end -task(:webpage) do - sh 'scp -r doc/* rydahl@rubyforge.org:/var/www/gforge-projects/geoip-city/' +task :database => ["data/GeoLiteCity.dat"] do + ENV['CITY'] = File.expand_path("data/GeoLiteCity.dat") end + +task :test => [:compile, :database] diff --git a/extconf.rb b/ext/geoip/extconf.rb similarity index 100% rename from extconf.rb rename to ext/geoip/extconf.rb diff --git a/geoip.c b/ext/geoip/geoip.c similarity index 98% rename from geoip.c rename to ext/geoip/geoip.c index 085f6e1..e1eebfa 100644 --- a/geoip.c +++ b/ext/geoip/geoip.c @@ -117,6 +117,7 @@ static VALUE generic_single_value_lookup_response(char *key, char *value) VALUE rb_city_record_to_hash(GeoIPRecord *record) { VALUE hash = rb_hash_new(); + char *region_name; if(record->country_code) rb_hash_sset(hash, "country_code", encode_to_utf8_and_return_rb_str(record->country_code)); @@ -126,7 +127,9 @@ VALUE rb_city_record_to_hash(GeoIPRecord *record) rb_hash_sset(hash, "country_name", encode_to_utf8_and_return_rb_str(record->country_name)); if(record->region) { rb_hash_sset(hash, "region", encode_to_utf8_and_return_rb_str(record->region)); - rb_hash_sset(hash, "region_name", encode_to_utf8_and_return_rb_str(GeoIP_region_name_by_code(record->country_code, record->region))); + region_name = GeoIP_region_name_by_code(record->country_code, record->region); + if (region_name) + rb_hash_sset(hash, "region_name", encode_to_utf8_and_return_rb_str(region_name)); } if(record->city) rb_hash_sset(hash, "city", encode_to_utf8_and_return_rb_str(record->city)); diff --git a/geoip-c.gemspec b/geoip-c.gemspec index da88c50..5a81639 100644 --- a/geoip-c.gemspec +++ b/geoip-c.gemspec @@ -1,16 +1,21 @@ +# -*- encoding: utf-8 -*- + Gem::Specification.new do |s| s.name = 'geoip-c' s.version = "0.8.1" - + s.authors = ['Ryan Dahl', 'Matt Todd', 'Charles Brian Quinn', 'Michael Sheakoski', 'Silvio Quadri', 'Andy Lindeman'] s.email = ['alindeman@gmail.com', 'mtodd@highgroove.com'] - + s.summary = "A Binding to the GeoIP C library" s.description = 'Generic GeoIP lookup tool. Based on the geoip_city RubyGem by Ryan Dahl' s.homepage = "http://github.com/mtodd/geoip" - - s.files = ["Rakefile", "extconf.rb", "test.rb", "geoip.c", "README.md"] - s.test_files = ['test.rb'] - s.extensions = ['extconf.rb'] - s.require_path = '.' + + s.files = ["Rakefile", "ext/geoip/extconf.rb", "ext/geoip/geoip.c", "test/test_geoip.rb", "README.md"] + s.test_files = ['test/test_geoip.rb'] + s.extensions = ["ext/geoip/extconf.rb"] + + s.add_development_dependency "rake", "~> 0.9.6" + s.add_development_dependency "rdoc", "~> 3.12" + s.add_development_dependency "rake-compiler", "~> 0.8.2" end diff --git a/test.rb b/test.rb deleted file mode 100644 index 31938c8..0000000 --- a/test.rb +++ /dev/null @@ -1,170 +0,0 @@ -require 'test/unit' -require File.dirname(__FILE__) + '/geoip' -require 'rubygems' -# require 'ruby-debug' -# Debugger.start - -CITY_DB = ENV.fetch("CITY", '/usr/local/GeoIP/share/GeoIP/GeoLiteCity.dat') -ORG_DB = ENV.fetch("ORG", '/usr/local/GeoIP/share/GeoIP/GeoIPOrg.dat') - -class Test::Unit::TestCase - - def assert_look_up(db, addr, field, value) - h = db.look_up(addr) - assert_equal value, h[field] - h - end - -end - -class GeoIPTest < Test::Unit::TestCase - - def setup - @ip = "24.24.24.24" - @ipnum = 16777216*24 + 65536*24 + 256*24 + 24 - - @large_ip = "245.245.245.245" - @large_ipnum = 16777216*245 + 65536*245 + 256*245 + 245 - end - - # addr_to_num - - def test_addr_to_num_converts_an_ip_to_an_ipnum - assert_equal @ipnum, GeoIP.addr_to_num(@ip) - end - - def test_addr_to_num_converts_large_ips_to_an_ipnum_correctly - assert_equal @large_ipnum, GeoIP.addr_to_num(@large_ip) - end - - def test_addr_to_num_expects_an_ip_string - assert_raises TypeError do - GeoIP.addr_to_num(nil) - end - end - - def test_addr_to_num_returns_zero_for_an_illformed_ip_string - assert_equal 0, GeoIP.addr_to_num("foo.bar") - end - - # num_to_addr - - def test_num_to_addr_converts_an_ipnum_to_an_ip - assert_equal @ip, GeoIP.num_to_addr(@ipnum) - end - - def test_num_to_addr_converts_large_ipnums_to_an_ip_correctly - assert_equal @large_ip, GeoIP.num_to_addr(@large_ipnum) - end - - def test_num_to_addr_expects_a_numeric_ip - assert_raises TypeError do - GeoIP.num_to_addr(nil) - end - assert_raises TypeError do - GeoIP.num_to_addr("foo.bar") - end - end - -end - -class GeoIPCityTest < Test::Unit::TestCase - - def setup - ## Change me! - @dbfile = CITY_DB - end - - def test_construction_default - db = GeoIP::City.new(@dbfile) - - assert_raises TypeError do - db.look_up(nil) - end - - h = db.look_up('24.24.24.24') - #debugger - assert_kind_of Hash, h - assert_equal 'New York', h[:city] - assert_equal 'United States', h[:country_name] - end - - def test_construction_index - db = GeoIP::City.new(@dbfile, :index) - assert_look_up(db, '24.24.24.24', :city, 'New York') - end - - def test_construction_filesystem - db = GeoIP::City.new(@dbfile, :filesystem) - assert_look_up(db, '24.24.24.24', :city, 'New York') - end - - def test_construction_memory - db = GeoIP::City.new(@dbfile, :memory) - assert_look_up(db, '24.24.24.24', :city, 'New York') - end - - def test_construction_filesystem_check - db = GeoIP::City.new(@dbfile, :filesystem, true) - assert_look_up(db, '24.24.24.24', :city, 'New York') - end - - def test_bad_db_file - assert_raises Errno::ENOENT do - GeoIP::City.new('/supposed-to-fail') - end - end - - def test_character_encoding_converted_to_utf8_first - db = GeoIP::City.new(@dbfile, :filesystem, true) - assert_look_up(db, '201.85.50.148', :city, 'São Paulo') - end - -end - -class GeoIPOrgTest < Test::Unit::TestCase - - def setup - ## Change me! - @dbfile = ORG_DB - end - - def test_construction_default - db = GeoIP::Organization.new(@dbfile) - - assert_raises TypeError do - db.look_up(nil) - end - - h = db.look_up('24.24.24.24') - assert_kind_of Hash, h - assert_equal 'Road Runner', h[:name] - end - - def test_construction_index - db = GeoIP::Organization.new(@dbfile, :index) - assert_look_up(db, '24.24.24.24', :name, 'Road Runner') - end - - def test_construction_filesystem - db = GeoIP::Organization.new(@dbfile, :filesystem) - assert_look_up(db, '24.24.24.24', :name, 'Road Runner') - end - - def test_construction_memory - db = GeoIP::Organization.new(@dbfile, :memory) - assert_look_up(db, '24.24.24.24', :name, 'Road Runner') - end - - def test_construction_filesystem_check - db = GeoIP::Organization.new(@dbfile, :filesystem, true) - assert_look_up(db, '24.24.24.24', :name, 'Road Runner') - end - - def test_bad_db_file - assert_raises Errno::ENOENT do - GeoIP::Organization.new('/supposed-to-fail') - end - end - -end diff --git a/test/fixtures/asnum.dat b/test/fixtures/asnum.dat new file mode 100644 index 0000000..d401e1b Binary files /dev/null and b/test/fixtures/asnum.dat differ diff --git a/test/fixtures/asnum.txt b/test/fixtures/asnum.txt new file mode 100644 index 0000000..0bd9353 --- /dev/null +++ b/test/fixtures/asnum.txt @@ -0,0 +1,6 @@ +0, 167772159, AS12345 +184549376, 2130706431, AS12345 +2147483648, 2851995647, AS12345 +2852061184, 2886729727, AS12345 +2887778304, 3232235519, AS12345 +3232301056, 4294967294, AS12345 diff --git a/test/fixtures/city.dat b/test/fixtures/city.dat new file mode 100644 index 0000000..1a280ee Binary files /dev/null and b/test/fixtures/city.dat differ diff --git a/test/fixtures/city.txt b/test/fixtures/city.txt new file mode 100644 index 0000000..ec5d5d4 --- /dev/null +++ b/test/fixtures/city.txt @@ -0,0 +1,6 @@ +"0","167772159","US","NY","New York","5.4321","-25.2015","1005","212","123" +"184549376","2130706431","US","NY","New York","5.4321","-25.2015","1005","212","123" +"2147483648","2851995647","US","NY","New York","5.4321","-25.2015","1005","212","123" +"2852061184","2886729727","US","NY","New York","5.4321","-25.2015","1005","212","123" +"2887778304","3232235519","US","NY","New York","5.4321","-25.2015","1005","212","123" +"3232301056","4294967294","US","NY","New York","5.4321","-25.2015","1005","212","123" diff --git a/test/fixtures/country.dat b/test/fixtures/country.dat new file mode 100644 index 0000000..4a37f00 Binary files /dev/null and b/test/fixtures/country.dat differ diff --git a/test/fixtures/country.txt b/test/fixtures/country.txt new file mode 100644 index 0000000..87dc980 --- /dev/null +++ b/test/fixtures/country.txt @@ -0,0 +1,6 @@ +0.0.0.0, 9.255.255.255, 0, 167772159, US +11.0.0.0, 126.255.255.255, 184549376, 2130706431, US +128.0.0.0, 169.253.255.255, 2147483648, 2851995647, US +169.255.0.0, 172.15.255.255, 2852061184, 2886729727, US +172.32.0.0, 192.167.255.255, 2887778304, 3232235519, US +192.169.0.0, 255.255.255.254, 3232301056, 4294967294, US diff --git a/test/fixtures/isp.dat b/test/fixtures/isp.dat new file mode 100644 index 0000000..0704dd1 Binary files /dev/null and b/test/fixtures/isp.dat differ diff --git a/test/fixtures/isp.txt b/test/fixtures/isp.txt new file mode 100644 index 0000000..acccbe1 --- /dev/null +++ b/test/fixtures/isp.txt @@ -0,0 +1,6 @@ +0,167772159,0.0.0.0,9.255.255.255,"A Company" +184549376,2130706431,11.0.0.0,126.255.255.255,"A Company" +2147483648,2851995647,128.0.0.0,169.253.255.255,"A Company" +2852061184,2886729727,169.255.0.0,172.15.255.255,"A Company" +2887778304,3232235519,172.32.0.0,192.167.255.255,"A Company" +3232301056,4294967294,192.169.0.0,255.255.255.254,"A Company" diff --git a/test/fixtures/netspeed.dat b/test/fixtures/netspeed.dat new file mode 100644 index 0000000..1666fa4 Binary files /dev/null and b/test/fixtures/netspeed.dat differ diff --git a/test/fixtures/netspeed.txt b/test/fixtures/netspeed.txt new file mode 100644 index 0000000..e26c265 --- /dev/null +++ b/test/fixtures/netspeed.txt @@ -0,0 +1,6 @@ +0,167772159,0.0.0.0,9.255.255.255,2 +184549376,2130706431,11.0.0.0,126.255.255.255,2 +2147483648,2851995647,128.0.0.0,169.253.255.255,2 +2852061184,2886729727,169.255.0.0,172.15.255.255,2 +2887778304,3232235519,172.32.0.0,192.167.255.255,2 +3232301056,4294967294,192.169.0.0,255.255.255.254,2 diff --git a/test/fixtures/org.dat b/test/fixtures/org.dat new file mode 100644 index 0000000..bae95fc Binary files /dev/null and b/test/fixtures/org.dat differ diff --git a/test/fixtures/org.txt b/test/fixtures/org.txt new file mode 100644 index 0000000..32163bf --- /dev/null +++ b/test/fixtures/org.txt @@ -0,0 +1,6 @@ +0,167772159,"org test 123 org" +184549376,2130706431,"org test 123 org" +2147483648,2851995647,"org test 123 org" +2852061184,2886729727,"org test 123 org" +2887778304,3232235519,"org test 123 org" +3232301056,4294967294,"org test 123 org" diff --git a/test/fixtures/region.dat b/test/fixtures/region.dat new file mode 100644 index 0000000..41e7b24 Binary files /dev/null and b/test/fixtures/region.dat differ diff --git a/test/fixtures/region.txt b/test/fixtures/region.txt new file mode 100644 index 0000000..5509e68 --- /dev/null +++ b/test/fixtures/region.txt @@ -0,0 +1,6 @@ +0,167772159,0.0.0.0,9.255.255.255,US,NY +184549376,2130706431,11.0.0.0,126.255.255.255,US,NY +2147483648,2851995647,128.0.0.0,169.253.255.255,US,NY +2852061184,2886729727,169.255.0.0,172.15.255.255,US,NY +2887778304,3232235519,172.32.0.0,192.167.255.255,US,NY +3232301056,4294967294,192.169.0.0,255.255.255.254,US,NY diff --git a/test/test_geoip.rb b/test/test_geoip.rb new file mode 100644 index 0000000..7c63edb --- /dev/null +++ b/test/test_geoip.rb @@ -0,0 +1,201 @@ +# encoding: utf-8 + +require 'test/unit' +require 'geoip' +require 'rubygems' + +CITY_DB = ENV.fetch("CITY", File.expand_path('test/fixtures/city.dat')) +ORG_DB = ENV.fetch("ORG", File.expand_path('test/fixtures/org.dat')) + +class Test::Unit::TestCase + + def assert_look_up(db, addr, field, value) + h = db.look_up(addr) + assert_equal value, h[field] + h + end + +end + +class GeoIPTest < Test::Unit::TestCase + + def ip_address_to_int address + ( o1, o2, o3, o4 ) = address.split('.').map(&:to_i) + + 16777216 * o1 + 65536 * o2 + 256 * o3 + o4 + end + + def setup + # 29690,"US","NY","East Syracuse","13057",43.0892,-76.0250,555,315 + @ip = "24.24.24.24" + @ipnum = ip_address_to_int @ip + + # + @large_ip = "245.245.245.245" + @large_ipnum = ip_address_to_int @large_ip + end + + # addr_to_num + + def test_addr_to_num_converts_an_ip_to_an_ipnum + assert_equal @ipnum, GeoIP.addr_to_num(@ip) + end + + def test_addr_to_num_converts_large_ips_to_an_ipnum_correctly + assert_equal @large_ipnum, GeoIP.addr_to_num(@large_ip) + end + + def test_addr_to_num_expects_an_ip_string + assert_raises TypeError do + GeoIP.addr_to_num(nil) + end + end + + def test_addr_to_num_returns_zero_for_an_illformed_ip_string + assert_equal 0, GeoIP.addr_to_num("foo.bar") + end + + # num_to_addr + + def test_num_to_addr_converts_an_ipnum_to_an_ip + assert_equal @ip, GeoIP.num_to_addr(@ipnum) + end + + def test_num_to_addr_converts_large_ipnums_to_an_ip_correctly + assert_equal @large_ip, GeoIP.num_to_addr(@large_ipnum) + end + + def test_num_to_addr_expects_a_numeric_ip + assert_raises TypeError do + GeoIP.num_to_addr(nil) + end + assert_raises TypeError do + GeoIP.num_to_addr("foo.bar") + end + end + +end + +unless File.exist?(CITY_DB) + puts "No City DB found, skipping GeoIP::City tests" + puts "* Set CITY=/path/to/GeoIPCity.dat to run tests" +else + class GeoIPCityTest < Test::Unit::TestCase + + def setup + ## Change me! + @dbfile = CITY_DB + end + + def test_construction_default + db = GeoIP::City.new(@dbfile) + + assert_raises TypeError do + db.look_up(nil) + end + + h = db.look_up('24.24.24.24') + assert_kind_of Hash, h + assert_equal 'East Syracuse', h[:city] + assert_equal 'United States', h[:country_name] + end + + def test_construction_index + db = GeoIP::City.new(@dbfile, :index) + assert_look_up(db, '24.24.24.24', :city, 'East Syracuse') + end + + def test_construction_filesystem + db = GeoIP::City.new(@dbfile, :filesystem) + assert_look_up(db, '24.24.24.24', :city, 'East Syracuse') + end + + def test_construction_memory + db = GeoIP::City.new(@dbfile, :memory) + assert_look_up(db, '24.24.24.24', :city, 'East Syracuse') + end + + def test_hong_kong_segfault + db = GeoIP::City.new(@dbfile, :filesystem, true) + assert_look_up(db, "61.93.14.4", :country_name, "Hong Kong") + end + + def test_construction_filesystem_check + db = GeoIP::City.new(@dbfile, :filesystem, true) + assert_look_up(db, '24.24.24.24', :city, 'East Syracuse') + end + + def test_bad_db_file + assert_raises Errno::ENOENT do + GeoIP::City.new('/supposed-to-fail') + end + end + + def test_character_encoding_converted_to_utf8_first + db = GeoIP::City.new(@dbfile, :filesystem, true) + assert_look_up(db, '201.85.50.148', :city, 'São Paulo') + end + + def test_blank_region_skips_region_name_set + db = GeoIP::City.new(@dbfile, :filesystem, true) + ip = '116.48.136.76' + assert_look_up(db, ip, :region, nil) + assert_look_up(db, ip, :region_name, nil) + assert_look_up(db, ip, :city, 'Central District') + assert_look_up(db, ip, :country_name, 'Hong Kong') + end + + end +end + +unless File.exist?(ORG_DB) + puts "No Org DB found, skipping GeoIP::Organization tests" + puts "* Set ORG=/path/to/GeoIPORG.dat to run tests" +else + class GeoIPOrgTest < Test::Unit::TestCase + + def setup + ## Change me! + @dbfile = ORG_DB + end + + def test_construction_default + db = GeoIP::Organization.new(@dbfile) + + assert_raises TypeError do + db.look_up(nil) + end + + h = db.look_up('24.24.24.24') + assert_kind_of Hash, h + assert_equal 'org test 123 org', h[:name] + end + + def test_construction_index + db = GeoIP::Organization.new(@dbfile, :index) + assert_look_up(db, '24.24.24.24', :name, 'org test 123 org') + end + + def test_construction_filesystem + db = GeoIP::Organization.new(@dbfile, :filesystem) + assert_look_up(db, '24.24.24.24', :name, 'org test 123 org') + end + + def test_construction_memory + db = GeoIP::Organization.new(@dbfile, :memory) + assert_look_up(db, '24.24.24.24', :name, 'org test 123 org') + end + + def test_construction_filesystem_check + db = GeoIP::Organization.new(@dbfile, :filesystem, true) + assert_look_up(db, '24.24.24.24', :name, 'org test 123 org') + end + + def test_bad_db_file + assert_raises Errno::ENOENT do + GeoIP::Organization.new('/supposed-to-fail') + end + end + + end +end