enricher 0.0.2 → 0.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ba7623f738d308075b0b07cf6365f83f3f11053b
4
- data.tar.gz: 040c1a1349e741d87da53fa7c435841ef8995c34
3
+ metadata.gz: 7f87c647a272e4dd0b39b45b5e3691fdf3f8fa0e
4
+ data.tar.gz: 6fc0c075c318729355ee93f56c9982714d39bfad
5
5
  SHA512:
6
- metadata.gz: 36748a53b45b8ba519090621481ebbeb078d098f42a1fc39dd1bc45483cbab4bfe7a8be3e7360ed531717e51f750f6cf49d347e8b61d7f49b94e52e4b876d33b
7
- data.tar.gz: 81e992ba9f680437405ee6369a3c6ae171660684454351c4464133487306c4f678e855efdbaeaffe937bc39ff13a114b98e8a0a98279b40ed571ec51351ceed4
6
+ metadata.gz: d883c35af6c5d1542aaade6b622f82b5c3044a78633fba1abc250a00c4851c53b2e6aef4c895b84fca7044657c1e7c16dbac34843d17b11bea9ebdb9c685510a
7
+ data.tar.gz: d88bca4c18a7c68e74490492a8763d5e5b83f300c257d6cf421e289152ed98d161a28e243bff86d2e2084cbca3c63f1df4afbdb4443f150b14e3624a886d6f9b
data/enricher.gemspec CHANGED
@@ -18,5 +18,7 @@ Gem::Specification.new do |gem|
18
18
  gem.add_development_dependency 'bundler', '~> 1.0'
19
19
  gem.add_dependency "geoip", '~> 1.2'
20
20
  gem.add_dependency "netaddr", '~> 1.5'
21
+ gem.add_dependency "rest-client"
22
+ gem.add_dependency "json"
21
23
 
22
24
  end
data/lib/enricher.rb CHANGED
@@ -4,20 +4,28 @@ require 'ipaddr'
4
4
  require 'logger'
5
5
  require 'rubygems'
6
6
  require 'tempfile'
7
+ require 'net/http'
8
+ require 'date'
7
9
 
8
10
  # RubyGems
9
11
 
10
12
  require 'json'
11
13
  require 'geoip'
12
14
  require 'netaddr'
15
+ require 'rest-client'
16
+
13
17
 
14
18
  # Internal
15
19
  module Enricher
16
20
  $:.unshift(File.dirname(__FILE__))
21
+
22
+ require 'vash'
17
23
  require 'enricher/version'
24
+ require 'enricher/exceptions'
18
25
  require 'enricher/bogon'
26
+ require 'enricher/bgpranking'
19
27
  require 'enricher/encoder'
20
-
28
+
21
29
  DEBUG=false
22
30
  LOGGING=false
23
31
 
@@ -0,0 +1,41 @@
1
+ module Enricher
2
+ #
3
+ # BGP dynamic ASN ranking checks provided by circl.lu.
4
+ #
5
+ # Results cached locally for 12hr.
6
+ #
7
+ # > r = RestClient.post "http://bgpranking.circl.lu/json", { 'method' => 'cached_daily_rank', 'asn' => 198540, 'date' => '2014-02-23' }.to_json, :content_type => :json, :accept => :json
8
+ # => "[198540, "ELAN-AS Przedsiebiorstwo Uslug Specjalistycznych ELAN mgr inz. Andrzej Niechcial", "2014-02-23", "global", 1.0496093750000002]"
9
+ # >> r = RestClient.post "http://bgpranking.circl.lu/json", { 'method' => 'cached_daily_rank', 'asn' => 198540, 'date' => '2014-02-24' }.to_json, :content_type => :json, :accept => :json
10
+ # => "[198540, "ELAN-AS Przedsiebiorstwo Uslug Specjalistycznych ELAN mgr inz. Andrzej Niechcial", "2014-02-24", "global", 1.09609375]"
11
+ # >> a = JSON.parse(r)
12
+
13
+ class BGPRanking
14
+
15
+ BGP_RANK_URL = "http://bgpranking.circl.lu/json"
16
+
17
+ def self.rank?(addr)
18
+ asn = addr.strip[/[0-9]+/]
19
+ if asn =~ /[0-9]+/
20
+ @@cache ||= Vash.new
21
+ # Voliate Cache store for 43200 (12hr)
22
+ @@cache["asn#{asn}".to_sym] ||= self.onlinerank?(asn)
23
+ else
24
+ return "0.0"
25
+ end
26
+ end
27
+
28
+ def self.cache
29
+ @@cache
30
+ end
31
+
32
+ private
33
+
34
+ def self.onlinerank?(addr)
35
+ resp = RestClient.post BGP_RANK_URL, { 'method' => 'cached_daily_rank', 'asn' => addr, 'date' => Date.strptime((Date.today - 1).to_s, '%Y-%m-%d').to_s }.to_json, :content_type => :json, :accept => :json
36
+ return "%.6f" % JSON.parse(resp)[4]
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -1,4 +1,15 @@
1
1
  module Enricher
2
+ #
3
+ # Bogons ipv4 allow for both static simple checks and for dynamic full Bogon list checks provided by Team Cymru.
4
+ #
5
+ # >> @@mybogon = Enricher::Bogon.new(:live)^C
6
+ # >> @@mybogon.contains?('205.166.22.1')
7
+ # => true
8
+ # >> @@mybogon = Enricher::Bogon.new(:ipv4)
9
+ # => #<Enricher::Bogon:0x00000002fb0368 @bogon=[0.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.0.0.0/24, 192.0.2.0/24, 192.168.0.0/16, 198.18.0.0/15, 198.51.100.0/24, 203.0.113.0/24, 224.0.0.0/4, 240.0.0.0/4]>
10
+ # >> @@mybogon.contains?('205.166.22.1')
11
+ # => false
12
+ #
2
13
  class Bogon
3
14
 
4
15
  BOGONIPV4 = ['0.0.0.0/8',
@@ -16,22 +27,36 @@ module Enricher
16
27
  '224.0.0.0/4',
17
28
  '240.0.0.0/4']
18
29
 
30
+ LIST_URL = "http://www.team-cymru.org/Services/Bogons/fullbogons-ipv4.txt"
31
+
19
32
  def initialize(bogon)
20
- if bogon == :bogonipv4
33
+ if bogon == :ipv4
21
34
  @bogon = BOGONIPV4.collect do |cidr|
22
35
  NetAddr::CIDR.create(cidr)
23
36
  end
37
+ elsif bogon == :live
38
+ @bogon = []
39
+ Net::HTTP.get(URI.parse(LIST_URL)).each_line do |line|
40
+ if line !~ /^#/
41
+ @bogon << NetAddr::CIDR.create(line.strip)
42
+ end
43
+ end
24
44
  else
25
- raise BogonSetUndefined, "Only the :bogonipv4 aggregated set is defined at this time"
45
+ raise BogonSetUndefined, "Only the :ipv4 aggregated set, and :live via http is defined at this time. illegal use of #{bogon}"
26
46
  end
47
+
48
+ @bogon
27
49
  end
28
50
 
29
51
  def contains?(ip)
30
- @bogon.each do |net|
31
- return true if net.contains?(ip)
32
- end
52
+ @bogon.each { |net| return true if net.contains?(ip) }
33
53
  return false
34
54
  end
35
-
55
+
56
+ def addresses
57
+ @bogon ||= self.initialize
58
+ end
59
+
36
60
  end
61
+
37
62
  end
@@ -1,6 +1,20 @@
1
1
  module Enricher
2
2
 
3
3
  class Encoder
4
+
5
+ def self.encode(ip)
6
+
7
+ @@geoASN ||= GeoIP.new("#{Enricher::DATA_PATH}/GeoIPASNum.dat")
8
+ @@geoCoder ||= GeoIP.new("#{Enricher::DATA_PATH}/GeoIP.dat")
9
+ @@geoCoderCity ||= GeoIP.new("#{Enricher::DATA_PATH}/GeoLiteCity.dat")
10
+
11
+ @@bogon_type ||= self.bogon_type
12
+ @@bogon ||= Bogon.new(@@bogon_type)
13
+
14
+ asn = @@geoASN.asn(ip).number rescue "--"
15
+
16
+ {:ip => IPAddr.new(ip).to_i, :asn => asn, :asn_rank => Enricher::BGPRanking.rank?(asn), :geoip => @@geoCoder.country(ip).country_code3, :bogon => @@bogon.contains?(ip)}
17
+ end
4
18
 
5
19
  def self.aton(a)
6
20
  IPAddr.new(a).to_i
@@ -9,21 +23,21 @@ module Enricher
9
23
  def self.ntoa(a)
10
24
  IPAddr.new(a, Socket::AF_INET).to_s
11
25
  end
12
-
13
- def self.encode(ip)
14
- @@geoASN ||= GeoIP.new("#{Enricher::DATA_PATH}/GeoIPASNum.dat")
15
- @@geoCoder ||= GeoIP.new("#{Enricher::DATA_PATH}/GeoIP.dat")
16
- @@geoCoderCity ||= GeoIP.new("#{Enricher::DATA_PATH}/GeoLiteCity.dat")
17
- @@bogon ||= Bogon.new(:bogonipv4)
18
- asn = @@geoASN.asn(ip).number rescue "--"
19
- {:ip => IPAddr.new(ip).to_i, :asn => asn, :geoip => @@geoCoder.country(ip).country_code3, :bogon => @@bogon.contains?(ip)}
26
+
27
+ def self.rank?(asn)
28
+ Enricher::BGPRanking.rank?(asn)
20
29
  end
21
-
30
+
22
31
  def self.bogon?(ip)
23
- @@bogon ||= Bogon.new(:bogonipv4)
32
+ @@bogon_type ||= self.bogon_type
33
+ @@bogon ||= Bogon.new(@@bogon_type)
24
34
  return @@bogon.contains?(ip)
25
35
  end
26
36
 
37
+ def self.bogon_type(bogon_sym=:ipv4)
38
+ bogon_sym
39
+ end
40
+
27
41
  def self.asn(ip)
28
42
  @@geoASN ||= GeoIP.new("#{Enricher::DATA_PATH}/GeoIPASNum.dat")
29
43
  return @@geoASN.asn(ip).number rescue "--"
@@ -0,0 +1,5 @@
1
+ module Enricher
2
+
3
+ class BogonSetUndefined < StandardError; end
4
+
5
+ end
@@ -1,3 +1,3 @@
1
1
  module Enricher
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.4'
3
3
  end
data/lib/vash.rb ADDED
@@ -0,0 +1,110 @@
1
+ #############################################################################
2
+ # Class: Vash (Ruby Volatile Hash)
3
+ # Hash that returns values only for a short time. This is useful as a cache
4
+ # where I/O is involved. The primary goal of this object is to reduce I/O
5
+ # access and due to the nature of I/O being slower then memory, you should also
6
+ # see a gain in quicker response times.
7
+ #
8
+ # For example, if Person.first found the first person from the database & cache
9
+ # was an instance of Vash then the following would only contact the database for
10
+ # the first iteration:
11
+ #
12
+ # > cache = Vash.new
13
+ # > 1000.times {cache[:person] ||= Person.first}
14
+ #
15
+ # However if you did the following immediately following that command it would
16
+ # hit the database again:
17
+ #
18
+ # > sleep 43201
19
+ # > cache[:person] ||= Person.first
20
+ #
21
+ # The reason is that there is a default Time-To-Live of 43200 seconds. You can
22
+ # also set a custom TTL of 10 seconds like so:
23
+ #
24
+ # > cache[:person, 10] = Person.first
25
+ #
26
+ # The Vash object will forget any answer that is requested after the specified
27
+ # TTL. It is a good idea to manually clean things up from time to time because
28
+ # it is possible that you'll cache data but never again access it and therefor
29
+ # it will stay in memory after the TTL has expired. To clean up the Vash object,
30
+ # call the method: cleanup!
31
+ #
32
+ # > sleep 11 # At this point the prior person ttl will be expired
33
+ # # but the person key and value will still exist.
34
+ # > cache # This will still show the the entire set of keys
35
+ # # regardless of the TTL, the :person will still exist
36
+ # > cache.cleanup! # All of the TTL's will be inspected and the expired
37
+ # # :person key will be deleted.
38
+ #
39
+ # The cleanup must be manually called because the purpose of the Vash is to
40
+ # lessen needless I/O calls and gain speed not to slow it down with regular
41
+ # maintenance.
42
+ class Vash < Hash
43
+ def initialize(constructor = {})
44
+ @register ||= {} # remembers expiration time of every key
45
+ if constructor.is_a?(Hash)
46
+ super()
47
+ merge(constructor)
48
+ else
49
+ super(constructor)
50
+ end
51
+ end
52
+
53
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
54
+ alias_method :regular_reader, :[] unless method_defined?(:regular_reader)
55
+
56
+ def [](key)
57
+ sterilize(key)
58
+ clear(key) if expired?(key)
59
+ regular_reader(key)
60
+ end
61
+
62
+ def []=(key, *args)
63
+ # a little bit o variable hacking to support (h[key, ttl] = value), which will come
64
+ # accross as (key, [ttl, value]) whereas (h[key]=value) comes accross as (key, [value])
65
+ if args.length == 2
66
+ value, ttl = args[1], args[0]
67
+ elsif args.length == 1
68
+ value, ttl = args[0], 43200
69
+ else
70
+ raise ArgumentError, "Wrong number of arguments, expected 2 or 3, received: #{args.length+1}\n"+
71
+ "Example Usage: volatile_hash[:key]=value OR volatile_hash[:key, ttl]=value"
72
+ end
73
+ sterilize(key)
74
+ ttl(key, ttl)
75
+ regular_writer(key, value)
76
+ end
77
+
78
+ def merge(hsh)
79
+ hsh.map {|key,value| self[sterile(key)] = hsh[key]}
80
+ self
81
+ end
82
+
83
+ def cleanup!
84
+ now = Time.now.to_i
85
+ @register.map {|k,v| clear(k) if v < now}
86
+ end
87
+
88
+ def clear(key)
89
+ sterilize(key)
90
+ @register.delete key
91
+ self.delete key
92
+ end
93
+
94
+ private
95
+ def expired?(key)
96
+ Time.now.to_i > @register[key].to_i
97
+ end
98
+
99
+ def ttl(key, secs=43200)
100
+ @register[key] = Time.now.to_i + secs.to_i
101
+ end
102
+
103
+ def sterile(key)
104
+ String === key ? key.chomp('!').chomp('=') : key.to_s.chomp('!').chomp('=').to_sym
105
+ end
106
+
107
+ def sterilize(key)
108
+ key = sterile(key)
109
+ end
110
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: enricher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - shadowbq
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-24 00:00:00.000000000 Z
11
+ date: 2014-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,34 @@ dependencies:
52
52
  - - ~>
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.5'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rest-client
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: json
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
55
83
  description: Enricher, the IP and URL data enhancer
56
84
  email:
57
85
  - shadowbq@gmail.com
@@ -66,9 +94,12 @@ files:
66
94
  - Rakefile
67
95
  - enricher.gemspec
68
96
  - lib/enricher.rb
97
+ - lib/enricher/bgpranking.rb
69
98
  - lib/enricher/bogon.rb
70
99
  - lib/enricher/encoder.rb
100
+ - lib/enricher/exceptions.rb
71
101
  - lib/enricher/version.rb
102
+ - lib/vash.rb
72
103
  - log/enricher.log
73
104
  homepage: https://github.com/shadowbq/enricher
74
105
  licenses: