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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +78 -0
- data/Rakefile +12 -0
- data/bin/dnsbl-client +42 -0
- data/eventmachine-dnsbl.gemspec +26 -0
- data/lib/eventmachine/dnsbl.rb +6 -0
- data/lib/eventmachine/dnsbl/client.rb +103 -0
- data/lib/eventmachine/dnsbl/defaults.rb +4296 -0
- data/lib/eventmachine/dnsbl/server.rb +63 -0
- data/lib/eventmachine/dnsbl/version.rb +5 -0
- data/lib/eventmachine/dnsbl/zone/abstract_zone.rb +34 -0
- data/lib/eventmachine/dnsbl/zone/memory_zone.rb +31 -0
- data/lib/eventmachine/dnsbl/zone/sqlite3_zone.rb +61 -0
- data/test/helper.rb +5 -0
- data/test/test_dnsbl_client.rb +38 -0
- data/test/test_dnsbl_server.rb +96 -0
- metadata +137 -0
@@ -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,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,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
|