mock_dns_server 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +24 -0
  5. data/README.md +127 -0
  6. data/RELEASE_NOTES.md +3 -0
  7. data/Rakefile +19 -0
  8. data/bin/show_dig_request +41 -0
  9. data/lib/mock_dns_server.rb +12 -0
  10. data/lib/mock_dns_server/action_factory.rb +84 -0
  11. data/lib/mock_dns_server/conditional_action.rb +42 -0
  12. data/lib/mock_dns_server/conditional_action_factory.rb +53 -0
  13. data/lib/mock_dns_server/conditional_actions.rb +73 -0
  14. data/lib/mock_dns_server/dnsruby_monkey_patch.rb +19 -0
  15. data/lib/mock_dns_server/history.rb +84 -0
  16. data/lib/mock_dns_server/history_inspections.rb +58 -0
  17. data/lib/mock_dns_server/ip_address_dispenser.rb +34 -0
  18. data/lib/mock_dns_server/message_builder.rb +199 -0
  19. data/lib/mock_dns_server/message_helper.rb +86 -0
  20. data/lib/mock_dns_server/message_transformer.rb +74 -0
  21. data/lib/mock_dns_server/predicate_factory.rb +108 -0
  22. data/lib/mock_dns_server/serial_history.rb +385 -0
  23. data/lib/mock_dns_server/serial_number.rb +129 -0
  24. data/lib/mock_dns_server/serial_transaction.rb +46 -0
  25. data/lib/mock_dns_server/server.rb +422 -0
  26. data/lib/mock_dns_server/server_context.rb +57 -0
  27. data/lib/mock_dns_server/server_thread.rb +13 -0
  28. data/lib/mock_dns_server/version.rb +3 -0
  29. data/mock_dns_server.gemspec +32 -0
  30. data/spec/mock_dns_server/conditions_factory_spec.rb +58 -0
  31. data/spec/mock_dns_server/history_inspections_spec.rb +84 -0
  32. data/spec/mock_dns_server/history_spec.rb +65 -0
  33. data/spec/mock_dns_server/ip_address_dispenser_spec.rb +30 -0
  34. data/spec/mock_dns_server/message_builder_spec.rb +18 -0
  35. data/spec/mock_dns_server/predicate_factory_spec.rb +147 -0
  36. data/spec/mock_dns_server/serial_history_spec.rb +385 -0
  37. data/spec/mock_dns_server/serial_number_spec.rb +119 -0
  38. data/spec/mock_dns_server/serial_transaction_spec.rb +37 -0
  39. data/spec/mock_dns_server/server_context_spec.rb +20 -0
  40. data/spec/mock_dns_server/server_spec.rb +411 -0
  41. data/spec/mock_dns_server/socket_research_spec.rb +59 -0
  42. data/spec/spec_helper.rb +44 -0
  43. data/todo.txt +0 -0
  44. metadata +212 -0
@@ -0,0 +1,57 @@
1
+ require 'dnsruby'
2
+ require 'forwardable'
3
+
4
+ require 'mock_dns_server/conditional_actions'
5
+ require 'mock_dns_server/history'
6
+ require 'mock_dns_server/message_builder'
7
+
8
+ module MockDnsServer
9
+
10
+ class ServerContext
11
+
12
+ include MessageBuilder
13
+ extend Forwardable
14
+
15
+ def_delegators :@conditional_actions, :respond_to
16
+
17
+ attr_reader :server, :port, :host, :timeout_secs, :mutex,
18
+ :conditional_actions, :history, :verbose
19
+
20
+ #def shutdown_requested?; @shutdown_requested end
21
+ #def request_shutdown; @shutdown_requested = true end
22
+
23
+
24
+ def initialize(server, options = {})
25
+ @server = server
26
+ @port = options[:port]
27
+ @host = options[:host]
28
+ @timeout_secs = options[:timeout_secs]
29
+ @verbose = options[:verbose]
30
+ @mutex = Mutex.new
31
+ @conditional_actions = ConditionalActions.new(self)
32
+ @history = History.new(self)
33
+ end
34
+
35
+
36
+ def with_mutex(&block)
37
+
38
+ start_time = Time.now
39
+ duration = ->() do
40
+ now = Time.now
41
+ elapsed_in_usec = (now - start_time) * 1_000_000
42
+ start_time = now
43
+ "#{elapsed_in_usec} usec"
44
+ end
45
+
46
+ #puts "#{Thread.current}: Waiting for mutex..."
47
+ mutex.synchronize do
48
+ #puts "time to get mutex: #{duration.()}"
49
+ block.call
50
+ #puts "time using mutex: #{duration.()}"
51
+ end
52
+ end
53
+
54
+
55
+ end
56
+
57
+ end
@@ -0,0 +1,13 @@
1
+ module MockDnsServer
2
+
3
+ class ServerThread < Thread
4
+
5
+ attr_accessor :server
6
+
7
+ # Thread::abort_on_exception = true
8
+
9
+ def self.all
10
+ Thread.list.select { |thread| thread.is_a?(ServerThread) }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module MockDnsServer
2
+ VERSION = "0.3.0"
3
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mock_dns_server/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "mock_dns_server"
8
+ spec.version = MockDnsServer::VERSION
9
+ spec.authors = ["Keith Bennett"]
10
+ spec.email = ["keithrbennett@gmail.com"]
11
+ spec.description = %q{Mock DNS Server}
12
+ spec.summary = %q{Mock DNS Server}
13
+ spec.homepage = "http://www.github.com" # TODO: change when able
14
+ spec.license = "BSD-3-Clause"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "dnsruby", '~> 1.54'
22
+ spec.add_dependency "hexdump", '~> 0.2'
23
+ spec.add_dependency "thread_safe", '~> 0.3'
24
+
25
+ spec.add_dependency "awesome_print", '~> 1.2'
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.3"
28
+ spec.add_development_dependency "pry"
29
+ spec.add_development_dependency "rake", '~> 10.1'
30
+ spec.add_development_dependency "rspec", '~> 3.0'
31
+
32
+ end
@@ -0,0 +1,58 @@
1
+ require 'dnsruby'
2
+ require 'spec_helper'
3
+ require 'mock_dns_server/predicate_factory'
4
+
5
+ module MockDnsServer
6
+
7
+ describe PredicateFactory do
8
+
9
+ describe "Conditions.message_qtype" do
10
+
11
+ subject { PredicateFactory.new.qtype('SOA') }
12
+
13
+ it "correctly returns true" do
14
+ message = Dnsruby::Message.new('com', Dnsruby::Types.SOA)
15
+ expect(subject.call(message)).to be true
16
+ end
17
+
18
+ it "correctly returns false" do
19
+ message = Dnsruby::Message.new('ruby-lang.org', Dnsruby::Types.A)
20
+ expect(subject.call(message)).to be false
21
+ end
22
+
23
+ end
24
+
25
+
26
+ describe "Conditions.message_qname" do
27
+
28
+ subject { PredicateFactory.new.qname('ruby-lang.org') }
29
+
30
+ it "correctly returns true" do
31
+ message = Dnsruby::Message.new('ruby-lang.org', 'A')
32
+ expect(subject.call(message)).to be true
33
+ end
34
+
35
+ it "correctly returns false" do
36
+ message = Dnsruby::Message.new('python.org', 'A')
37
+ expect(subject.call(message)).to be false
38
+ end
39
+ end
40
+
41
+ describe "Conditions.message_qclass" do
42
+
43
+ subject { PredicateFactory.new.qclass('IN')}
44
+
45
+ it "correctly returns true" do
46
+ message = Dnsruby::Message.new('ruby-lang.org', 'A', 'IN')
47
+ expect(subject.call(message)).to be true
48
+ end
49
+
50
+ it "correctly returns false" do
51
+ message = Dnsruby::Message.new('ruby-lang.org', 'A', 'CH')
52
+ expect(subject.call(message)).to be false
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,84 @@
1
+ require_relative '../spec_helper'
2
+
3
+ require 'dnsruby'
4
+ require 'mock_dns_server/history_inspections'
5
+ require 'mock_dns_server/message_builder'
6
+
7
+ module MockDnsServer
8
+
9
+ describe HistoryInspections do
10
+
11
+ include MessageBuilder
12
+
13
+ let (:hi) { HistoryInspections.new }
14
+
15
+ let (:test_data) do
16
+ soa_opts = { name: 'com', mname: 'a.gtld-servers.net.', serial: 1001 }
17
+
18
+ [
19
+ { type: :outgoing, message: Dnsruby::Message.new('ruby-lang.org'), protocol: :udp, id: 1 },
20
+ { type: :outgoing, message: Dnsruby::Message.new('ruby-lang.org'), protocol: :tcp, id: 2 },
21
+
22
+ { type: :incoming, message: soa_response(soa_opts), protocol: :udp, id: 3 },
23
+ { type: :incoming, message: soa_response(soa_opts), protocol: :tcp, id: 4 },
24
+
25
+ { type: :incoming, message: specified_a_response("foo.example.com. 86400 A 10.1.2.3"), protocol: :udp, id: 5 },
26
+ { type: :incoming, message: specified_a_response("foo.example.com. 86400 A 10.1.2.3"), protocol: :tcp, id: 6 },
27
+ ]
28
+ end
29
+
30
+ def ids(records)
31
+ records.map { |rec| rec[:id] }
32
+ end
33
+
34
+ it 'correctly tests for incoming' do
35
+ inspection = hi.type(:incoming)
36
+ result = hi.apply(test_data, inspection)
37
+ expect(ids(result)).to eq([3, 4, 5, 6])
38
+ end
39
+
40
+ it 'correctly tests for outgoing' do
41
+ inspection = hi.type(:outgoing)
42
+ result = hi.apply(test_data, inspection)
43
+ expect(ids(result)).to eq([1, 2])
44
+ end
45
+
46
+ it 'correctly tests for udp' do
47
+ inspection = hi.protocol(:udp)
48
+ result = hi.apply(test_data, inspection)
49
+ expect(ids(result)).to eq([1, 3, 5])
50
+ end
51
+
52
+ it 'correctly tests for tcp' do
53
+ inspection = hi.protocol(:tcp)
54
+ result = hi.apply(test_data, inspection)
55
+ expect(ids(result)).to eq([2, 4, 6])
56
+ end
57
+
58
+ it 'correctly tests for qtype' do
59
+ inspection = hi.qtype('SOA')
60
+ result = hi.apply(test_data, inspection)
61
+ expect(ids(result)).to eq([3, 4])
62
+ end
63
+
64
+ it 'correctly operates with the all predicate' do
65
+ inspection = hi.all(hi.qtype('SOA'), hi.protocol(:udp))
66
+ result = hi.apply(test_data, inspection)
67
+ expect(ids(result)).to eq([3])
68
+ end
69
+
70
+ it 'correctly operates with the any predicate' do
71
+ inspection = hi.any(hi.qtype('SOA'), hi.protocol(:udp))
72
+ result = hi.apply(test_data, inspection)
73
+ expect(ids(result)).to eq([1, 3, 4, 5])
74
+ end
75
+
76
+ it 'correctly operates with the none predicate' do
77
+ inspection = hi.none(hi.qtype('SOA'), hi.protocol(:udp))
78
+ result = hi.apply(test_data, inspection)
79
+ expect(ids(result)).to eq([2, 6])
80
+ end
81
+
82
+
83
+ end
84
+ end
@@ -0,0 +1,65 @@
1
+ require_relative '../spec_helper'
2
+
3
+ require 'mock_dns_server/history'
4
+ require 'mock_dns_server/server'
5
+ require 'mock_dns_server/conditional_action_factory'
6
+
7
+
8
+ module MockDnsServer
9
+
10
+
11
+ describe History do
12
+
13
+ subject { History.new(nil) }
14
+ let(:options) { { port: 9999 } }
15
+ let(:sample_message) { 'Hello from RSpec' }
16
+ let(:caf) { ConditionalActionFactory.new }
17
+
18
+ it 'should report the correct size' do
19
+ n = 3
20
+ n.times { |n| subject.add_incoming(nil, nil, nil) }
21
+ expect(subject.size).to eq(n)
22
+ end
23
+
24
+
25
+ it 'should retain its content even after its copy is cleared' do
26
+ n = 3
27
+ n.times { |n| subject.add_incoming(nil, nil, nil) }
28
+ copy = subject.copy
29
+ copy.clear
30
+ expect(subject.size).to eq(n)
31
+ end
32
+
33
+
34
+ it 'should create a new copy for each clone' do
35
+ n = 3
36
+ n.times { |n| subject.add_incoming(nil, nil, nil) }
37
+ clone1 = subject.copy
38
+ clone2 = subject.copy
39
+ expect(clone1).not_to equal(clone2)
40
+ end
41
+
42
+
43
+ it 'should clone each record in the array' do
44
+ subject.add_incoming(nil, nil, nil)
45
+ clone1 = subject.copy
46
+ clone2 = subject.copy
47
+ expect(clone1.first).not_to equal(clone2.first)
48
+ end
49
+
50
+
51
+ it 'should contain entries from traffic' do
52
+
53
+ Server.with_new_server(options) do |server|
54
+ server.add_conditional_action(caf.echo)
55
+ server.start
56
+
57
+ socket = UDPSocket.new
58
+ socket.send(sample_message, 0, '127.0.0.1', options[:port])
59
+ _, _ = socket.recvfrom(10_000)
60
+ history = server.history_copy
61
+ expect(history.size > 0 && history.first[:type] == :incoming).to be true
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,30 @@
1
+ require_relative '../spec_helper'
2
+
3
+ require 'mock_dns_server/ip_address_dispenser'
4
+
5
+ module MockDnsServer
6
+
7
+ describe IpAddressDispenser do
8
+
9
+ it "should return the initial value when next is called for the first time" do
10
+ expect(IpAddressDispenser.new('10.10.10.10').next.to_s).to eq('10.10.10.10')
11
+ end
12
+
13
+ it "should increment correctly in the least significant octet" do
14
+ expect(IpAddressDispenser.new('10.10.10.10').next(2).to_s).to eq('10.10.10.11')
15
+ end
16
+
17
+ it "should roll over correctly in the 2 least significant octets" do
18
+ expect(IpAddressDispenser.new('10.10.10.255').next(2).to_s).to eq('10.10.11.0')
19
+ end
20
+
21
+ it "should does not raise an error and produces a valid IP address when called on 255.255.255.255" do
22
+ f = ->() do
23
+ new_address = IpAddressDispenser.new('255.255.255.255').next
24
+ IPAddr.new(new_address)
25
+ end
26
+ expect(f).not_to raise_error
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ require_relative '../spec_helper'
2
+
3
+ require 'mock_dns_server/message_builder'
4
+
5
+ module MockDnsServer
6
+
7
+ describe MessageBuilder do
8
+
9
+ include MessageBuilder
10
+
11
+ it 'creates dummy A records correctly' do
12
+ num_records = 3
13
+ response = dummy_a_response(num_records, 'ruby-lang.org')
14
+ expect(response.answer.size).to eq(num_records)
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,147 @@
1
+ require_relative '../spec_helper'
2
+
3
+ require 'dnsruby'
4
+ require 'mock_dns_server/predicate_factory'
5
+
6
+ module MockDnsServer
7
+
8
+ describe PredicateFactory do
9
+
10
+ subject { PredicateFactory.new }
11
+
12
+ it '.always_true works' do
13
+ func = subject.always_true
14
+ expect(func.call(nil, nil)).to be true
15
+ end
16
+
17
+
18
+ it '.always_false works' do
19
+ func = subject.always_false
20
+ expect(func.call(nil, nil)).to be false
21
+ end
22
+
23
+
24
+ it '.and works' do
25
+ t = subject.always_true
26
+ f = subject.always_false
27
+
28
+ composite_func = subject.all(t, t)
29
+ expect(composite_func.call(nil, nil)).to be true
30
+
31
+ composite_func = subject.all(t, f)
32
+ expect(composite_func.call(nil, nil)).to be false
33
+ end
34
+
35
+ it '.not works' do
36
+ t = subject.always_true
37
+ f = subject.always_false
38
+
39
+ composite_func = subject.none(f, f)
40
+ expect(composite_func.call(nil, nil)).to be true
41
+
42
+ composite_func = subject.none(t, f)
43
+ expect(composite_func.call(nil, nil)).to be false
44
+ end
45
+
46
+ it '.or works' do
47
+ t = subject.always_true
48
+ f = subject.always_false
49
+
50
+ composite_func = subject.any(t, f)
51
+ expect(composite_func.call(nil, nil)).to be true
52
+
53
+ composite_func = subject.any(f, f)
54
+ expect(composite_func.call(nil, nil)).to be false
55
+ end
56
+
57
+ it 'qname works' do
58
+ message = Dnsruby::Message.new('amazon.com', 'A')
59
+
60
+ predicate = subject.qname('amazon.com')
61
+ expect(predicate.(message)).to be true
62
+
63
+ predicate = subject.qname('ruby-lang.org')
64
+ expect(predicate.(message)).to be false
65
+ end
66
+
67
+ it 'qtype works' do
68
+ message = Dnsruby::Message.new('amazon.com', 'A')
69
+
70
+ predicate = subject.qtype('A')
71
+ expect(predicate.(message)).to be true
72
+
73
+ predicate = subject.qtype('SOA')
74
+ expect(predicate.(message)).to be false
75
+
76
+ end
77
+
78
+ it 'qclass works' do
79
+ message = Dnsruby::Message.new('amazon.com', 'A', 'CH')
80
+
81
+ predicate = subject.qclass('CH')
82
+ expect(predicate.(message)).to be true
83
+
84
+ predicate = subject.qclass('ZZ')
85
+ expect(predicate.(message)).to be false
86
+ end
87
+
88
+ it 'soa works' do
89
+ message = Dnsruby::Message.new('com', 'SOA')
90
+ expect(subject.soa.(message)).to be true
91
+
92
+ message = Dnsruby::Message.new('x.com', 'A')
93
+ expect(subject.soa.(message)).to be false
94
+ end
95
+
96
+ it 'axfr works' do
97
+ message = Dnsruby::Message.new('com', 'AXFR')
98
+ expect(subject.axfr.(message)).to be true
99
+
100
+ message = Dnsruby::Message.new('x.com', 'A')
101
+ expect(subject.axfr.(message)).to be false
102
+ end
103
+
104
+ it 'ixfr works' do
105
+ message = Dnsruby::Message.new('com', 'IXFR')
106
+ expect(subject.ixfr.(message)).to be true
107
+
108
+ message = Dnsruby::Message.new('x.com', 'A')
109
+ expect(subject.ixfr.(message)).to be false
110
+ end
111
+
112
+
113
+ it 'qtype_and_qname works' do
114
+ message = Dnsruby::Message.new('com', 'SOA')
115
+ predicate = subject.qtype_and_qname('SOA', 'com')
116
+ expect(predicate.(message)).to be true
117
+ end
118
+
119
+ it 'a_request works' do
120
+ message = Dnsruby::Message.new('amazon.com', 'A')
121
+
122
+ predicate = subject.a_request('amazon.com')
123
+ expect(predicate.(message)).to be true
124
+
125
+ predicate = subject.a_request('ruby-lang.org')
126
+ expect(predicate.(message)).to be false
127
+
128
+ predicate = subject.qname('ruby-lang.org')
129
+ expect(predicate.(message)).to be false
130
+
131
+ message = Dnsruby::Message.new('amazon.com', 'SOA')
132
+ predicate = subject.a_request('amazon.com')
133
+ expect(predicate.(message)).to be false
134
+ end
135
+
136
+
137
+ it 'is case insensitive' do
138
+ message = Dnsruby::Message.new('AmAzOn.CoM', 'a')
139
+
140
+ expect(subject.qtype('A').(message)).to be true
141
+ expect(subject.qname('amazon.com').(message)).to be true
142
+ end
143
+ end
144
+
145
+
146
+
147
+ end