eventmachine-dnsbl 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,63 @@
1
+ require 'eventmachine'
2
+ require 'resolv'
3
+
4
+ module EventMachine
5
+ module DNSBL
6
+ class Server < EventMachine::Connection
7
+ def initialize(zones = MemoryZone.new)
8
+ @zones = zones
9
+ end
10
+
11
+ def receive_data(data)
12
+ dns = Resolv::DNS::Message.decode(data)
13
+ dns.qr = 1
14
+ dns.aa = 1
15
+ return if dns.opcode != Resolv::DNS::OpCode::Query
16
+ unless dns.question.length == 1 and dns.question[0][1] == Resolv::DNS::Resource::IN::A
17
+ dns.rcode = Resolv::DNS::RCode::FormErr
18
+ res = dns.encode
19
+ send_data res
20
+ return
21
+ end
22
+ query, qtype = dns.question[0]
23
+ rs = @zones.get_records(query.to_s, qtype)
24
+ unless rs
25
+ dns.rcode = Resolv::DNS::RCode::NXDomain
26
+ res = dns.encode
27
+ send_data res
28
+ return
29
+ end
30
+ # add the records them to the set of answers
31
+ rs.each do |rr|
32
+ dns.add_answer(query,rr.ttl,rr.answer)
33
+ end
34
+ # Make sure that we are authoritative for the response! Otherwise, return REFUSED
35
+ dns.rcode = Resolv::DNS::RCode::NoError # Or Refused
36
+ res = dns.encode
37
+ #port, ip = Socket.unpack_sockaddr_in(get_peername)
38
+ #puts "DEBUG: #{ip}:#{port} #{res.length}"
39
+ send_data res
40
+ end
41
+
42
+ def add_record(qname, answer, ttl=300)
43
+ rr = ResourceRecord.new(qname, ttl, answer)
44
+ @rr[qname] = [] unless @rr[qname]
45
+ @rr[qname] << rr
46
+ end
47
+
48
+ def del_record(qname, answer)
49
+ return nil unless @rr[qname]
50
+ @rr[qname].delete_if {|rr| rr.answer == answer}
51
+ end
52
+
53
+ def del_all_records(qname)
54
+ @rr.delete(qname)
55
+ end
56
+
57
+ def get_records(qname, qtype)
58
+ return nil unless @rr[qname]
59
+ @rr[qname].find_all {|rr| rr.answer.class == qtype}
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,5 @@
1
+ module EventMachine
2
+ module DNSBL
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,34 @@
1
+ module EventMachine
2
+ module DNSBL
3
+ module Zone
4
+ class DNSBLResourceRecord < Struct.new(:zone, :label_regex, :ttl, :answer, :valid_until); end
5
+
6
+ class AbstractZone
7
+ def get_records(query, qtype)
8
+ records = Array.new
9
+ # A queries are all that I support right now
10
+ if qtype != Resolv::DNS::Resource::IN::A
11
+ return records
12
+ end
13
+
14
+ zone = label = nil
15
+ @zones.each do |z|
16
+ if query.end_with?(z)
17
+ label = query[0, query.length - z.length]
18
+ zone = z
19
+ end
20
+ end
21
+
22
+ if zone
23
+ get_all_records_for_zone(zone).each do |rec|
24
+ if rec[:label_regex].match(label)
25
+ records << rec
26
+ end
27
+ end
28
+ end
29
+ records
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,31 @@
1
+ require "eventmachine/dnsbl/zone/abstract_zone"
2
+
3
+ module EventMachine
4
+ module DNSBL
5
+ module Zone
6
+ class MemoryZone < AbstractZone
7
+ def initialize
8
+ @zones = Array.new
9
+ @backend = Hash.new
10
+ end
11
+
12
+ def add_dnsblresource(dnsblrr)
13
+ zone = dnsblrr[:zone]
14
+ if not @backend[zone]
15
+ if not @zones.include?(zone)
16
+ @zones << zone
17
+ @zones = @zones.uniq.sort {|a,b| b.length <=> a.length}
18
+ end
19
+ @backend[zone] = Array.new
20
+ end
21
+ @backend[zone] << dnsblrr
22
+ end
23
+
24
+ def get_all_records_for_zone(zone)
25
+ @backend[zone]
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,61 @@
1
+ require "eventmachine/dnsbl/zone/abstract_zone"
2
+ require 'sqlite3'
3
+ require 'resolv'
4
+ require 'pp'
5
+
6
+ module EventMachine
7
+ module DNSBL
8
+ module Zone
9
+ class Sqlite3Zone < AbstractZone
10
+ def initialize(sqlite3, tablename = "zone")
11
+ @class = DNSBLResourceRecord
12
+ @fields = @class.members.map {|x| x.to_s}.join(", ")
13
+ @tablename = tablename
14
+ if sqlite3.class == SQLite3::Database
15
+ @db = sqlite3
16
+ else
17
+ @db = SQLite3::Database.new(sqlite3)
18
+ end
19
+ if @db.table_info(tablename).length == 0
20
+ @db.execute("CREATE TABLE #{@tablename} (#{@fields})")
21
+ end
22
+ @zones = Array.new
23
+ @backend = Hash.new
24
+ end
25
+
26
+ def add_dnsblresource(dnsblrr)
27
+ dnsblrr.answer = dnsblrr.answer.address.to_s
28
+ args = (@class.members).map{|f| dnsblrr.send(f)}
29
+ qs = args.map{|x| "'#{quote(x.to_s)}'"}.join(",").gsub(/'NULL'/, "NULL")
30
+ zone = dnsblrr[:zone]
31
+ if not @zones.include?(zone)
32
+ @zones << zone
33
+ @zones = @zones.uniq.sort {|a,b| b.length <=> a.length}
34
+ end
35
+ sql = "INSERT INTO #{@tablename} (#{@fields}) VALUES (#{qs})"
36
+ @db.execute(sql)
37
+ end
38
+
39
+ def get_records_by_field_and_value(field, value)
40
+ records = Array.new
41
+ rs = @db.execute("SELECT #{@fields} FROM #{@tablename} WHERE #{field}='#{value}'")
42
+ rs.each do |row|
43
+ row[1] = Regexp.new(row[1])
44
+ row[3] = Resolv::DNS::Resource::IN::A.new(row[3])
45
+ records << DNSBLResourceRecord.new(*row)
46
+ end
47
+ records
48
+ end
49
+
50
+ def get_all_records_for_zone(zone)
51
+ get_records_by_field_and_value("zone", zone)
52
+ end
53
+
54
+ def quote( string )
55
+ string.gsub( /'/, "''" )
56
+ end
57
+ private :quote
58
+ end
59
+ end
60
+ end
61
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/test'
3
+ require 'minitest/unit'
4
+ include MiniTest::Assertions
5
+ require File.expand_path('../../lib/eventmachine/dnsbl.rb', __FILE__)
@@ -0,0 +1,38 @@
1
+ unless Kernel.respond_to?(:require_relative)
2
+ module Kernel
3
+ def require_relative(path)
4
+ require File.join(File.dirname(caller[0]), path.to_str)
5
+ end
6
+ end
7
+ end
8
+
9
+ require_relative 'helper'
10
+ require 'eventmachine'
11
+ include EventMachine::DNSBL
12
+ require 'pp'
13
+
14
+
15
+ class TestDNSBLClient < Minitest::Test
16
+ def run_test(item, expected_result)
17
+ EM.run do
18
+ EventMachine::DNSBL::Client.check(item) do |results|
19
+ pp results
20
+ assert_equal(expected_result, EventMachine::DNSBL::Client.blacklisted?(results))
21
+ EM.stop
22
+ end
23
+ end
24
+ end
25
+
26
+ def test_127_0_0_2
27
+ run_test("127.0.0.2", true)
28
+ end
29
+ def test_127_0_0_254
30
+ run_test("127.0.0.254", false)
31
+ end
32
+ def test_surbl
33
+ run_test("surbl-org-permanent-test-point.com", true)
34
+ end
35
+ def test_example_com
36
+ run_test("example.com", false)
37
+ end
38
+ end
@@ -0,0 +1,96 @@
1
+ unless Kernel.respond_to?(:require_relative)
2
+ module Kernel
3
+ def require_relative(path)
4
+ require File.join(File.dirname(caller[0]), path.to_str)
5
+ end
6
+ end
7
+ end
8
+
9
+ require_relative 'helper'
10
+ #require 'eventmachine'
11
+ require 'pp'
12
+
13
+ #Monkeypatching for testing
14
+ module EventMachine
15
+ module DNS
16
+ class Socket < EventMachine::Connection
17
+ def send_packet(pkt)
18
+ send_datagram(pkt, nameserver, 2053)
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ class TestDNSBLServer < Minitest::Test
25
+
26
+ def zone_add_test_resources(zone)
27
+ zone.add_dnsblresource(
28
+ EventMachine::DNSBL::Zone::DNSBLResourceRecord.new(
29
+ "example.com",
30
+ /viagra/i,
31
+ 300,
32
+ Resolv::DNS::Resource::IN::A.new("127.0.0.2"),
33
+ Time.now.to_i + 3600
34
+ )
35
+ )
36
+ zone.add_dnsblresource(
37
+ EventMachine::DNSBL::Zone::DNSBLResourceRecord.new(
38
+ "example.com",
39
+ /pillz/i,
40
+ 300,
41
+ Resolv::DNS::Resource::IN::A.new("127.0.0.3"),
42
+ Time.now.to_i + 3600
43
+ )
44
+ )
45
+ end
46
+
47
+ def zone_test(zone)
48
+ zone_add_test_resources(zone)
49
+ recs = zone.get_records("viagrapillz.example.com", Resolv::DNS::Resource::IN::A)
50
+ assert(2, recs.length)
51
+ recs = zone.get_records("cialispillz.example.com", Resolv::DNS::Resource::IN::A)
52
+ assert(1, recs.length)
53
+ end
54
+
55
+ def test_memory_zone
56
+ memzone = EventMachine::DNSBL::Zone::MemoryZone.new
57
+ zone_test(memzone)
58
+ end
59
+
60
+ def test_sqlite3_zone
61
+ if File.exist?("test/test.sqlite3")
62
+ File.unlink("test/test.sqlite3")
63
+ end
64
+ sqlite3zone = EventMachine::DNSBL::Zone::Sqlite3Zone.new("test/test.sqlite3")
65
+ zone_test(sqlite3zone)
66
+ if File.exist?("test/test.sqlite3")
67
+ File.unlink("test/test.sqlite3")
68
+ end
69
+ end
70
+
71
+ def test_dnsbl_server
72
+ memzone = EventMachine::DNSBL::Zone::MemoryZone.new
73
+ zone_add_test_resources(memzone)
74
+ completed = 0
75
+ EM.run {
76
+ EM::open_datagram_socket "0.0.0.0", 2053, EventMachine::DNSBL::Server, memzone
77
+ EM::DNS::Resolver.nameservers = ["127.0.0.1"]
78
+ r1 = EM::DNS::Resolver.resolve("viagrapillz.example.com")
79
+ r1.callback do |r|
80
+ assert_equal(2, r.length)
81
+ completed += 1
82
+ if completed == 2
83
+ EM.stop
84
+ end
85
+ end
86
+ r2 = EM::DNS::Resolver.resolve("cialispillz.example.com")
87
+ r2.callback do |r|
88
+ assert_equal(1, r.length)
89
+ completed += 1
90
+ if completed == 2
91
+ EM.stop
92
+ end
93
+ end
94
+ }
95
+ end
96
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eventmachine-dnsbl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - chrislee35
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: eventmachine
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.12.10
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.12.10
27
+ - !ruby/object:Gem::Dependency
28
+ name: sqlite3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.3.6
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.3.6
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.5'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.5'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.6'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.6'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: For use in the Rubot Emulation Framework, I needed an EventMachine-based
84
+ implementation of a DNSBL checker and server.
85
+ email:
86
+ - rubygems@chrislee.dhs.org
87
+ executables:
88
+ - dnsbl-client
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - ".gitignore"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - bin/dnsbl-client
98
+ - eventmachine-dnsbl.gemspec
99
+ - lib/eventmachine/dnsbl.rb
100
+ - lib/eventmachine/dnsbl/client.rb
101
+ - lib/eventmachine/dnsbl/defaults.rb
102
+ - lib/eventmachine/dnsbl/server.rb
103
+ - lib/eventmachine/dnsbl/version.rb
104
+ - lib/eventmachine/dnsbl/zone/abstract_zone.rb
105
+ - lib/eventmachine/dnsbl/zone/memory_zone.rb
106
+ - lib/eventmachine/dnsbl/zone/sqlite3_zone.rb
107
+ - test/helper.rb
108
+ - test/test_dnsbl_client.rb
109
+ - test/test_dnsbl_server.rb
110
+ homepage: https://github.com/chrislee35/eventmachine-dnsbl
111
+ licenses:
112
+ - MIT
113
+ metadata: {}
114
+ post_install_message:
115
+ rdoc_options: []
116
+ require_paths:
117
+ - lib
118
+ required_ruby_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ requirements: []
129
+ rubyforge_project:
130
+ rubygems_version: 2.2.2
131
+ signing_key:
132
+ specification_version: 4
133
+ summary: EventMachine-based implementation of DNSBL checker and server
134
+ test_files:
135
+ - test/helper.rb
136
+ - test/test_dnsbl_client.rb
137
+ - test/test_dnsbl_server.rb