enricher 0.0.2 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|