summaryrefslogtreecommitdiff
path: root/lib/dnsxl_check.rb
blob: 1e506b2d9cbcc334bcec9ae4a12d28e5063a377f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
require 'resolv'

class DNSXLCheck

  class Resolver
    def self.search(query)
      begin
        Resolv.getaddress(query)
        true
      rescue Resolv::ResolvError
        false
      end
    end
  end

  IP_REGEXP = /\A(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\z/
  DEFAULT_THRESHOLD = 0.33

  def self.create_from_list(list)
    dnsxl_check = DNSXLCheck.new

    list.each do |entry|
      dnsxl_check.add_list(entry.domain, entry.weight)
    end

    dnsxl_check
  end

  def test(ip)
    if use_threshold?
      test_with_threshold(ip)
    else
      test_strict(ip)
    end
  end

  def test_with_threshold(ip)
    return false if lists.empty?

    search(ip)
    final_score >= threshold
  end

  def test_strict(ip)
    return false if lists.empty?

    search(ip)
    @score > 0
  end

  def use_threshold=(value)
    @use_threshold = value == true
  end

  def use_threshold?
    @use_threshold &&= true
  end

  def threshold=(threshold)
    raise ArgumentError, "'threshold' value must be grather than 0 and less than or equal to 1" unless threshold > 0 && threshold <= 1
    @threshold = threshold
  end

  def threshold
    @threshold ||= DEFAULT_THRESHOLD
  end

  def add_list(domain, weight)
    @lists ||= []
    @lists << { domain: domain, weight: weight }
  end

  def lists
    @lists ||= []
  end

  private

  def search(ip)
    raise ArgumentError, "'ip' value must be in #{IP_REGEXP} format" unless ip.match(IP_REGEXP)

    @score = 0

    reversed = reverse_ip(ip)
    search_in_rbls(reversed)
  end

  def reverse_ip(ip)
    ip.split('.').reverse.join('.')
  end

  def search_in_rbls(reversed_ip)
    lists.each do |rbl|
      query = "#{reversed_ip}.#{rbl[:domain]}"
      @score += rbl[:weight] if Resolver.search(query)
    end
  end

  def final_score
    weights = lists.map{ |rbl| rbl[:weight] }.reduce(:+).to_i
    return 0 if weights == 0

    (@score.to_f / weights.to_f).round(2)
  end
end