ryodo 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rbenv-gemsets +1 -0
- data/.rbenv-version +1 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +16 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +46 -0
- data/LICENSE.txt +20 -0
- data/README.md +72 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/checks/matching.rb +89 -0
- data/data/suffix.dat +4317 -0
- data/lib/ryodo/convenience/.u.rb +19 -0
- data/lib/ryodo/convenience.rb +13 -0
- data/lib/ryodo/domain.rb +83 -0
- data/lib/ryodo/ext/string.rb +9 -0
- data/lib/ryodo/methods.rb +12 -0
- data/lib/ryodo/parser.rb +37 -0
- data/lib/ryodo/rule.rb +15 -0
- data/lib/ryodo/rule_set.rb +77 -0
- data/lib/ryodo/suffix_list.rb +57 -0
- data/lib/ryodo/suffix_list_fetcher.rb +54 -0
- data/lib/ryodo.rb +29 -0
- data/spec/_files/mozilla_effective_tld_names.dat +5229 -0
- data/spec/ryodo/suffix_list_fetcher_spec.rb +81 -0
- data/spec/ryodo/suffix_list_spec.rb +50 -0
- data/spec/ryodo_spec.rb +6 -0
- data/spec/spec_helper.rb +37 -0
- metadata +192 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Ryodo
|
4
|
+
module Convenience
|
5
|
+
module U
|
6
|
+
|
7
|
+
alias_method :ryodo, :Ryodo
|
8
|
+
|
9
|
+
# Unicode junkie? ;o)
|
10
|
+
alias_method :"ryōdo", :Ryodo
|
11
|
+
alias_method :"領土", :Ryodo
|
12
|
+
alias_method :"りょうど", :Ryodo
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
include Ryodo::Convenience::U
|
data/lib/ryodo/domain.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Ryodo
|
4
|
+
class Domain
|
5
|
+
|
6
|
+
# DomainString is a String with extended methods
|
7
|
+
class DomainString < String
|
8
|
+
|
9
|
+
def reverse
|
10
|
+
to_a(:r).join(".")
|
11
|
+
end
|
12
|
+
alias_method :r, :reverse
|
13
|
+
|
14
|
+
def to_a option = nil
|
15
|
+
case option
|
16
|
+
when :reverse, :r
|
17
|
+
dsplit.reverse
|
18
|
+
else
|
19
|
+
dsplit
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def dsplit
|
26
|
+
self.split(".",-1)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
# remove own class comparison (we will use String#== via method_missing)
|
32
|
+
undef_method :==
|
33
|
+
|
34
|
+
def initialize domainStr
|
35
|
+
raise TypeError, "Not a valid domain string!" unless domainStr.is_a?(String)
|
36
|
+
@domain_string = DomainString.new domainStr.downcase
|
37
|
+
|
38
|
+
parts = Ryodo::Parser.run(@domain_string)
|
39
|
+
|
40
|
+
@suffix = parts ? parts[0].reverse.join(".") : nil
|
41
|
+
@domain = parts && !parts[1].empty? ? (parts[0] + parts[1]).reverse.join(".") : nil
|
42
|
+
@subdomain = parts && !parts[2].empty? ? (parts[2]).reverse.join(".") : nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def suffix
|
46
|
+
DomainString.new @suffix if @suffix
|
47
|
+
end
|
48
|
+
alias_method :tld, :suffix
|
49
|
+
|
50
|
+
def domain
|
51
|
+
DomainString.new @domain if @domain
|
52
|
+
end
|
53
|
+
alias_method :registered_domain, :domain
|
54
|
+
alias_method :regdomain, :domain
|
55
|
+
|
56
|
+
def subdomain
|
57
|
+
DomainString.new @subdomain if @subdomain
|
58
|
+
end
|
59
|
+
|
60
|
+
def fqdn
|
61
|
+
DomainString.new "#{to_s}."
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
@domain_string
|
66
|
+
end
|
67
|
+
|
68
|
+
def inspect
|
69
|
+
@domain_string.inspect
|
70
|
+
end
|
71
|
+
|
72
|
+
# pass all missings to @domain_string (String class)
|
73
|
+
def method_missing(name, *args, &block)
|
74
|
+
@domain_string.send(name, *args, &block)
|
75
|
+
end
|
76
|
+
|
77
|
+
# explicit definition of class' send
|
78
|
+
def send(symbol, *args)
|
79
|
+
__send__(symbol, *args)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
data/lib/ryodo/parser.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Ryodo
|
4
|
+
class Parser
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@rules = Ryodo::RuleSet.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def build_query domain
|
11
|
+
if domain.to_s[0] == "."
|
12
|
+
# FQDN in reversed order
|
13
|
+
domain.downcase[1..-1].split(".")
|
14
|
+
else
|
15
|
+
domain.downcase.split(".").reverse
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse domain
|
20
|
+
@rules.match build_query(domain)
|
21
|
+
end
|
22
|
+
|
23
|
+
class << self
|
24
|
+
|
25
|
+
def run domain
|
26
|
+
instance.parse domain
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def instance
|
31
|
+
@@instance ||= new
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
data/lib/ryodo/rule.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Ryodo
|
4
|
+
class RuleSet
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@tree = {}
|
8
|
+
build!
|
9
|
+
end
|
10
|
+
|
11
|
+
def build!
|
12
|
+
|
13
|
+
Ryodo::SuffixList.list.each do |line|
|
14
|
+
|
15
|
+
line.each.with_index do |node_name, idx|
|
16
|
+
|
17
|
+
stopOK = node_name == line.last
|
18
|
+
exception = node_name[0] == "!"
|
19
|
+
node_name = node_name[1..-1] if exception
|
20
|
+
children = {}
|
21
|
+
|
22
|
+
node = Ryodo::Rule.new(exception, stopOK, children)
|
23
|
+
|
24
|
+
if idx > 0
|
25
|
+
end_idx = idx - 1
|
26
|
+
parent = select_rule(line[0..end_idx])
|
27
|
+
parent.children[node_name] = node unless parent.children[node_name]
|
28
|
+
|
29
|
+
else
|
30
|
+
@tree[node_name] = node unless @tree[node_name]
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
def select_rule(rule_path)
|
40
|
+
|
41
|
+
path = rule_path.dup
|
42
|
+
|
43
|
+
if current = path.pop
|
44
|
+
|
45
|
+
if path.empty?
|
46
|
+
@tree[current]
|
47
|
+
|
48
|
+
else
|
49
|
+
rule = select_rule(path)
|
50
|
+
rule.children[current] if rule
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
def match(path)
|
58
|
+
suffix, domain, match = [], [], nil
|
59
|
+
|
60
|
+
until match || path.empty?
|
61
|
+
match = select_rule(path) || select_rule(path.dup.fill("*",-1))
|
62
|
+
match = nil if match && !match.is_suffix?
|
63
|
+
domain.unshift path.pop
|
64
|
+
suffix = path
|
65
|
+
end
|
66
|
+
|
67
|
+
suffix.push(domain.shift) if match && !match.exception
|
68
|
+
|
69
|
+
# only if match has no children with domain and domain is present
|
70
|
+
if match && !match.children.keys.include?(domain[0]) && domain[0]
|
71
|
+
[ suffix, [domain.shift], domain ]
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Ryodo
|
4
|
+
|
5
|
+
class SuffixList
|
6
|
+
def initialize suffix_file = Ryodo::PUBLIC_SUFFIX_STORE
|
7
|
+
load_file(suffix_file)
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse_data
|
11
|
+
# loads and converts to array
|
12
|
+
# "baz.bar.foo" => ["baz", "bar", "foo"]
|
13
|
+
File.readlines(@suffix_file).map{ |line| line.strip.split(".") }
|
14
|
+
end
|
15
|
+
|
16
|
+
def load_file suffix_file = Ryodo::PUBLIC_SUFFIX_STORE
|
17
|
+
@suffix_file = suffix_file
|
18
|
+
@suffix_data = parse_data
|
19
|
+
end
|
20
|
+
|
21
|
+
def list
|
22
|
+
@suffix_data
|
23
|
+
end
|
24
|
+
|
25
|
+
def inspect
|
26
|
+
"#<#{self.class} FILE:#{@suffix_file} ENTRIES:#{@suffix_data.size}>"
|
27
|
+
end
|
28
|
+
|
29
|
+
class << self
|
30
|
+
|
31
|
+
def SuffixList suffix_file = Ryodo::PUBLIC_SUFFIX_STORE
|
32
|
+
instance(suffix_file)
|
33
|
+
end
|
34
|
+
|
35
|
+
def reload suffix_file = Ryodo::PUBLIC_SUFFIX_STORE
|
36
|
+
instance.load_file(suffix_file) && true
|
37
|
+
end
|
38
|
+
|
39
|
+
def list
|
40
|
+
instance.list
|
41
|
+
end
|
42
|
+
|
43
|
+
def instance
|
44
|
+
@@instance ||= new
|
45
|
+
end
|
46
|
+
|
47
|
+
def inspect
|
48
|
+
instance.inspect
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
private_class_method :new
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
require "net/http"
|
5
|
+
|
6
|
+
module Ryodo
|
7
|
+
|
8
|
+
class FetchError < StandardError; end
|
9
|
+
|
10
|
+
class SuffixListFetcher
|
11
|
+
|
12
|
+
def initialize uri = Ryodo::PUBLIC_SUFFIX_DATA_URI, store = Ryodo::PUBLIC_SUFFIX_STORE
|
13
|
+
@uri = URI(uri)
|
14
|
+
@store = store
|
15
|
+
end
|
16
|
+
|
17
|
+
def fetch_data
|
18
|
+
res = Net::HTTP.get_response(@uri)
|
19
|
+
raise Ryodo::FetchError, "Could not fetch suffix data! (#{res})" unless res.is_a?(Net::HTTPSuccess)
|
20
|
+
@fetched_data = res.body.lines
|
21
|
+
end
|
22
|
+
|
23
|
+
def prepare_data
|
24
|
+
@prepared_data = @fetched_data.inject([]) do |acc, line|
|
25
|
+
next(acc) if line =~ /^\/\/|^\n/
|
26
|
+
dns_line = line.strip.
|
27
|
+
split(".").
|
28
|
+
reverse.
|
29
|
+
join(".") # "foo.bar.baz" => "baz.bar.foo"
|
30
|
+
acc << dns_line
|
31
|
+
end.sort
|
32
|
+
end
|
33
|
+
|
34
|
+
def save_data
|
35
|
+
File.open(Ryodo::PUBLIC_SUFFIX_STORE, "w") do |fh|
|
36
|
+
fh.write @prepared_data.join("\n")
|
37
|
+
end if @prepared_data
|
38
|
+
end
|
39
|
+
|
40
|
+
class << self
|
41
|
+
def fetch_and_save! uri = Ryodo::PUBLIC_SUFFIX_DATA_URI, store = Ryodo::PUBLIC_SUFFIX_STORE
|
42
|
+
fetcher = self.new uri, store
|
43
|
+
fetcher.fetch_data
|
44
|
+
fetcher.prepare_data
|
45
|
+
fetcher.save_data
|
46
|
+
true
|
47
|
+
rescue
|
48
|
+
false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/lib/ryodo.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Ryodo
|
4
|
+
|
5
|
+
VERSION_FILE = File.expand_path("../../VERSION", __FILE__)
|
6
|
+
VERSION = File.exist?(VERSION_FILE) ? File.read(VERSION_FILE) : "(could not find VERSION file)"
|
7
|
+
|
8
|
+
RYODO_ROOT = File.expand_path("../..", __FILE__)
|
9
|
+
|
10
|
+
PUBLIC_SUFFIX_DATA_URI = "http://mxr.mozilla.org/mozilla-central/source/netwerk/dns/effective_tld_names.dat?raw=1"
|
11
|
+
PUBLIC_SUFFIX_STORE = "#{RYODO_ROOT}/data/suffix.dat"
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
require "ryodo/domain"
|
16
|
+
require "ryodo/parser"
|
17
|
+
require "ryodo/rule"
|
18
|
+
require "ryodo/rule_set"
|
19
|
+
require "ryodo/suffix_list"
|
20
|
+
|
21
|
+
require "ryodo/methods"
|
22
|
+
require "ryodo/ext/string"
|
23
|
+
|
24
|
+
# Convenient shorthands
|
25
|
+
module Ryodo
|
26
|
+
extend Ryodo::Methods
|
27
|
+
require "ryodo/convenience"
|
28
|
+
require "ryodo/convenience/.u" unless Kernel.const_defined?("RYODO_NO_U") || ENV['RYODO_NO_U']
|
29
|
+
end
|