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 +4 -4
- data/enricher.gemspec +2 -0
- data/lib/enricher.rb +9 -1
- data/lib/enricher/bgpranking.rb +41 -0
- data/lib/enricher/bogon.rb +31 -6
- data/lib/enricher/encoder.rb +24 -10
- data/lib/enricher/exceptions.rb +5 -0
- data/lib/enricher/version.rb +1 -1
- data/lib/vash.rb +110 -0
- metadata +33 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f87c647a272e4dd0b39b45b5e3691fdf3f8fa0e
|
4
|
+
data.tar.gz: 6fc0c075c318729355ee93f56c9982714d39bfad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d883c35af6c5d1542aaade6b622f82b5c3044a78633fba1abc250a00c4851c53b2e6aef4c895b84fca7044657c1e7c16dbac34843d17b11bea9ebdb9c685510a
|
7
|
+
data.tar.gz: d88bca4c18a7c68e74490492a8763d5e5b83f300c257d6cf421e289152ed98d161a28e243bff86d2e2084cbca3c63f1df4afbdb4443f150b14e3624a886d6f9b
|
data/enricher.gemspec
CHANGED
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
|
data/lib/enricher/bogon.rb
CHANGED
@@ -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 == :
|
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 :
|
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
|
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
|
data/lib/enricher/encoder.rb
CHANGED
@@ -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.
|
14
|
-
|
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
|
-
@@
|
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 "--"
|
data/lib/enricher/version.rb
CHANGED
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.
|
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-
|
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:
|