eventmachine-dnsbl 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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