ipcat 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # ipcat-ruby
2
+
3
+ A ruby port of the [ipcat](https://github.com/client9/ipcat) library to classify IP addresses from known datacenters
4
+
5
+ ## Installation
6
+
7
+ With bundler:
8
+
9
+ # In Gemfile
10
+ gem 'ipcat'
11
+
12
+ Or with rubygems:
13
+
14
+ gem install ipcat
15
+
16
+ ## Usage
17
+
18
+ IPCat.matches?(ip_address)
19
+
20
+ It will return an IPCat::IPRange if ip_address is from a known datacenter; nil otherwise.
21
+
22
+ For example,
23
+
24
+ range = IPCat.matches?('8.18.145.0') # => instance of IPCat::IPRange
25
+ range.name # => 'Amazon AWS'
26
+
27
+ IPCat.matches?('127.0.0.1') # => nil
28
+
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.pattern = "spec/*_spec.rb"
7
+ end
8
+
9
+ task :default => :test
10
+
11
+ task :generate_dataset do
12
+
13
+ $:.unshift './lib'
14
+ require 'ipcat'
15
+ IPCat.load_csv!
16
+ File.open("data/datacenters", 'w') {|f| f << Marshal.dump(IPCat.ranges) }
17
+ end
data/data/datacenters ADDED
Binary file
data/lib/ipcat.rb ADDED
@@ -0,0 +1,70 @@
1
+ ##
2
+ # IPCat
3
+ # Ruby lib for https://github.com/client9/ipcat/
4
+
5
+ require 'ipaddr'
6
+ require 'ipcat/iprange'
7
+ require 'ipcat/version'
8
+
9
+
10
+ class IPCat
11
+ class << self
12
+ def matches?(ip)
13
+ bsearch(ip_to_fixnum(ip))
14
+ end
15
+
16
+ def ip_to_fixnum(ip)
17
+ Fixnum === ip ? ip : IPAddr.new(ip).to_i
18
+ end
19
+
20
+ def ranges
21
+ @ranges ||= []
22
+ end
23
+
24
+ def reset_ranges!(new_ranges = [])
25
+ @ranges = new_ranges
26
+ end
27
+
28
+ def load_csv!(path='https://raw.github.com/client9/ipcat/master/datacenters.csv')
29
+ reset_ranges!
30
+
31
+ require 'open-uri'
32
+ open(path).readlines.each do |line|
33
+ next if line =~/\s*#/ # Skip comments
34
+ first, last, name, url = line.split(',')
35
+ self.ranges << IPRange.new(first, last, name, url).freeze
36
+ end
37
+ self.ranges.freeze
38
+ end
39
+
40
+ def load!
41
+ reset_ranges!
42
+ # NB: loading an array of marshaled ruby objects takes ~15ms;
43
+ # versus ~100ms to load a CSV file
44
+ path = File.join(File.dirname(__FILE__), '..', 'data', 'datacenters')
45
+ @ranges = Marshal.load(File.read(path))
46
+ @ranges.each(&:freeze)
47
+ @ranges.freeze
48
+ rescue
49
+ load_csv!
50
+ end
51
+
52
+ # Assume ranges is an array of comparable objects
53
+ def bsearch(needle, haystack=ranges, first=0, last=ranges.size-1)
54
+ return nil if last < first # not found, or empty range
55
+
56
+ cur = first + (last - first)/2
57
+ case ranges[cur] <=> needle
58
+ when -1 # needle is larger than cur value
59
+ bsearch(needle, haystack, cur+1, last)
60
+ when 1 # needle is smaller than cur value
61
+ bsearch(needle, haystack, first, cur-1)
62
+ when 0
63
+ ranges[cur]
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ # Load dataset
70
+ IPCat.load!
@@ -0,0 +1,34 @@
1
+ class IPCat
2
+ class IPRange
3
+
4
+ attr_accessor :first, :last, :name, :url
5
+
6
+ def initialize(first, last, name=nil, url=nil)
7
+ @first = IPCat.ip_to_fixnum(first)
8
+ @last = IPCat.ip_to_fixnum(last)
9
+ @name, @url = name, url
10
+ raise ArgumentError.new("first must be <= last") if @first > @last
11
+ end
12
+
13
+ def <=>(obj)
14
+ case obj
15
+ when Fixnum
16
+ compare_with_fixnum(obj)
17
+ when IPRange
18
+ # Assume all IPRanges are non-overlapping
19
+ first <=> obj.first
20
+ end
21
+ end
22
+
23
+ protected
24
+ def compare_with_fixnum(i)
25
+ if first > i
26
+ 1
27
+ elsif last < i
28
+ -1
29
+ else
30
+ 0
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ class IPCat
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,32 @@
1
+ require_relative 'spec_helper'
2
+ require 'minitest/benchmark'
3
+
4
+ describe 'IPCat.bsearch' do
5
+
6
+ # Makes +n+ IPRanges
7
+ def make_ranges(n)
8
+ ips = (n*2).times.map{ rand(2**32) }.sort
9
+ ranges = []
10
+ ips.each_slice(2) do |first, last|
11
+ ranges << IPCat::IPRange.new(first, last)
12
+ end
13
+
14
+ ranges
15
+ end
16
+
17
+ before do
18
+ @ips = 1000.times.map{ rand(2**32) }
19
+ @ranges = MiniTest::Spec.bench_range.inject({}) {|h, n|
20
+ h[n] = make_ranges(n)
21
+ h
22
+ }
23
+ end
24
+
25
+ it("should be logarithmic") do
26
+ assert_performance_logarithmic 0.95 do |n|
27
+ IPCat.reset_ranges!(@ranges[n])
28
+ @ips.each { |ip| IPCat.bsearch(ip) }
29
+ end
30
+ end
31
+
32
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe 'IPCat::IPRange' do
4
+ attr_accessor :range
5
+
6
+ before do
7
+ IPCat.reset_ranges!
8
+ first = IPAddr.new('1.2.3.0').to_i
9
+ last = IPAddr.new('1.2.3.255').to_i
10
+ @range = IPCat::IPRange.new(first, last, 'example', 'www.example.com')
11
+ end
12
+
13
+ describe '#initialize' do
14
+ it "should fail if last < first" do
15
+ ->{ IPCat::IPRange.new(2, 1) }.must_raise ArgumentError
16
+ end
17
+ end
18
+
19
+ describe '#<=> for fixnums' do
20
+ it("should match first") { (range <=> range.first).must_equal 0 }
21
+ it("should match last") { (range <=> range.last).must_equal 0 }
22
+ it("should match first-1") { (range <=> range.first - 1).must_equal 1 }
23
+ it("should match last+1") { (range <=> range.last + 1).must_equal -1 }
24
+ end
25
+ end
26
+
@@ -0,0 +1,24 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe 'IPCat' do
4
+
5
+ before do
6
+ IPCat.reset_ranges!
7
+ start = IPAddr.new('1.2.3.0').to_i
8
+ stop = IPAddr.new('1.2.3.255').to_i
9
+ IPCat.ranges << IPCat::IPRange.new(start, stop, 'example', 'www.example.com')
10
+ end
11
+
12
+ describe '#ranges' do
13
+ it("has a range") { IPCat.ranges.size.must_equal 1 }
14
+ end
15
+
16
+ describe '#matches?' do
17
+ it("should match 1.2.3.0") { IPCat.matches?('1.2.3.0').must_be_instance_of IPCat::IPRange }
18
+ it("should match 1.2.3.1") { IPCat.matches?('1.2.3.1').must_be_instance_of IPCat::IPRange }
19
+ it("should match 1.2.3.1") { IPCat.matches?('1.2.3.1').must_be_instance_of IPCat::IPRange }
20
+ it("should not match 1.1.1.1") { IPCat.matches?('1.1.1.1').must_be_nil }
21
+ it("should not match 2.2.2.2") { IPCat.matches?('2.2.2.2').must_be_nil }
22
+ end
23
+
24
+ end
@@ -0,0 +1,7 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+
4
+ require "minitest/autorun"
5
+ require 'debugger'
6
+
7
+ require "ipcat"
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ipcat
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Aaron Suggs
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-01 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: minitest
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: ruby-prof
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: debugger
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 1.1.3
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 1.1.3
78
+ description: ! 'A ruby port of the ipcat library: https://github.com/client9/ipcat/'
79
+ email: aaron@ktheory.com
80
+ executables: []
81
+ extensions: []
82
+ extra_rdoc_files: []
83
+ files:
84
+ - lib/ipcat/iprange.rb
85
+ - lib/ipcat/version.rb
86
+ - lib/ipcat.rb
87
+ - data/datacenters
88
+ - Rakefile
89
+ - README.md
90
+ - spec/benchmark_spec.rb
91
+ - spec/ipcat_iprange_spec.rb
92
+ - spec/ipcat_spec.rb
93
+ - spec/spec_helper.rb
94
+ homepage: https://github.com/kickstarter/ipcat-ruby
95
+ licenses: []
96
+ post_install_message:
97
+ rdoc_options:
98
+ - --charset=UTF-8
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ! '>='
105
+ - !ruby/object:Gem::Version
106
+ version: 1.9.3
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ! '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 1.8.24
116
+ signing_key:
117
+ specification_version: 3
118
+ summary: dataset for categorizing IP addresses in ruby
119
+ test_files:
120
+ - spec/benchmark_spec.rb
121
+ - spec/ipcat_iprange_spec.rb
122
+ - spec/ipcat_spec.rb
123
+ - spec/spec_helper.rb