mock_dns_server 0.3.0

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.
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