Class: HttpBL

Inherits:
Object
  • Object
show all
Defined in:
lib/httpbl.rb

Overview

The Httpbl middleware

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app, options = {}) ⇒ HttpBL

Returns a new instance of HttpBL.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/httpbl.rb', line 23

def initialize(app, options = {})
  @app = app
  @options = {:blocked_search_engines => [],
              :age_threshold => 10,
              :threat_level_threshold => 2,
              :deny_types => [1, 2, 4, 8, 16, 32, 64, 128], # 8..128 aren't used as of 10/2009, but might be used in the future
              :dns_timeout => 0.5,
              :memcached_server => nil,
              :memcached_options => {}
              }.merge(options)
  raise "Missing :api_key for Http:BL middleware" unless @options[:api_key]
  if @options[:memcached_server]
    require 'memcache'
    @cache = MemCache.new(@options[:memcached_server], @options[:memcached_options])
  end
end

Class Method Details

.encourage_safe_timeoutsObject



6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/httpbl.rb', line 6

def self.encourage_safe_timeouts
  if /^1\.8/ =~ RUBY_VERSION 
    begin
      require 'system_timer'
      @@DnsTimeout = SystemTimer
    rescue LoadError
      require 'timeout'
      @@DnsTimeout = Timeout
    end
  else
    require 'timeout'
    @@DnsTimeout = Timeout
  end
end

Instance Method Details

#_call(env) ⇒ Object



44
45
46
47
48
49
50
51
52
53
# File 'lib/httpbl.rb', line 44

def _call(env)
  request = Rack::Request.new(env)
  bl_status = check(request.ip)
  if bl_status and blocked?(bl_status)
    [403, {"Content-Type" => "text/html"}, "<h1>403 Forbidden</h1> Request IP is listed as suspicious by <a href='http://projecthoneypot.org/ip_#{request.ip}'>Project Honeypot</a>"]
  else
    @app.call(env)
  end
  
end

#blocked?(response) ⇒ Boolean

Returns:

  • (Boolean)


76
77
78
79
80
81
82
83
84
85
86
# File 'lib/httpbl.rb', line 76

def blocked?(response)
  response = response.split('.').collect!(&:to_i)
  if response[0] == 127 
    if response[3] == 0
      blocked = @options[:blocked_search_engines].include?(response[2])
    else 
      blocked = @options[:deny_types].collect{|key| response[3] & key == key }.any? and response[2] > @options[:threat_level_threshold] and response[1] < @options[:age_threshold]
    end
  end
  return blocked
end

#cache_check(ip) ⇒ Object



59
60
61
62
63
64
65
66
# File 'lib/httpbl.rb', line 59

def cache_check(ip)
  cache = @cache.clone if @cache
  unless response = cache.get("httpbl_#{ip}")
    response = resolve(ip)
    cache.set("httpbl_#{ip}", (response || "0.0.0.0"), 1.hour)
  end
  return response
end

#call(env) ⇒ Object



40
41
42
# File 'lib/httpbl.rb', line 40

def call(env)
  dup._call(env)
end

#check(ip) ⇒ Object



55
56
57
# File 'lib/httpbl.rb', line 55

def check(ip)
  @cache ? cache_check(ip) : resolve(ip)
end

#resolve(ip) ⇒ Object



68
69
70
71
72
73
74
# File 'lib/httpbl.rb', line 68

def resolve(ip)
  query = @options[:api_key] + '.' + ip.split('.').reverse.join('.') + '.dnsbl.httpbl.org'
  @@DnsTimeout::timeout(@options[:dns_timeout]) do
     Resolv::DNS.new.getaddress(query).to_s rescue false
  end
  rescue Timeout::Error, Errno::ECONNREFUSED
end