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,385 @@
1
+ require 'spec_helper'
2
+ require 'mock_dns_server/serial_history'
3
+ require 'mock_dns_server/message_builder'
4
+
5
+ module MockDnsServer
6
+
7
+ describe SerialHistory do
8
+
9
+ include MessageBuilder
10
+
11
+ subject { SerialHistory.new('ruby-lang.org', 1001) }
12
+
13
+ let(:rrs) do [
14
+ rr('A', 'foo.ruby-lang.org', '1.2.3.4'),
15
+ rr('A', 'foo.ruby-lang.org', '5.6.7.8')
16
+ ] end
17
+
18
+ # Note: the data intended to be stored is RR records,
19
+ # but we're using Fixnum's to test the behavior.
20
+
21
+ it 'adds additions to a serial' do
22
+ subject.set_serial_additions(1111, [1, 2])
23
+ expect(subject.serial_additions(1111)).to eq([1, 2])
24
+ end
25
+
26
+ it 'adds deletions to a serial' do
27
+ subject.set_serial_deletions(2222, [3, 4])
28
+ expect(subject.serial_deletions(2222)).to eq([3, 4])
29
+ end
30
+
31
+
32
+ # Test RR comparison that will be used for detecting records to delete.
33
+ context "SerialHistory#rr_compare" do
34
+
35
+ it 'returns correct values when comparing types' do
36
+ rr1 = rr('A', 'abc.com', '1.2.3.4')
37
+ rr2 = rr('NS', 'abc.com', '1.2.3.4')
38
+ expect(subject.rr_compare(rr1, rr2) < 0).to be true
39
+ expect(subject.rr_equivalent(rr1, rr2)).to be false
40
+ end
41
+
42
+ it 'returns correct values when comparing names' do
43
+ rr1 = rr('A', 'abc.com', '1.2.3.4')
44
+ rr2 = rr('A', 'nbc.com', '1.2.3.4')
45
+ expect(subject.rr_compare(rr1, rr2) < 0).to be true
46
+ expect(subject.rr_equivalent(rr1, rr2)).to be false
47
+ end
48
+
49
+ it 'returns correct values when comparing rdata' do
50
+ rr1 = rr('A', 'abc.com', '1.2.3.4')
51
+ rr2 = rr('A', 'abc.com', '2.2.3.4')
52
+ expect(subject.rr_compare(rr1, rr2) < 0).to be true
53
+ expect(subject.rr_equivalent(rr1, rr2)).to be false
54
+ end
55
+
56
+ it 'returns equal given 2 records with the same type, name, and rdata' do
57
+ rr = rr('A', 'abc.com', '1.2.3.4')
58
+ expect(subject.rr_compare(rr, rr)).to eq(0)
59
+ expect(subject.rr_equivalent(rr, rr)).to be true
60
+ end
61
+ end
62
+
63
+
64
+ context "SerialHistory#data_at_serial" do
65
+
66
+ let (:history) do
67
+ history = SerialHistory.new('ruby-lang.org', 1001)
68
+ history.set_serial_additions(1002, rrs.first)
69
+ history.set_serial_additions(1003, rrs.last)
70
+ history
71
+ end
72
+
73
+ it 'removes records from the initial data correctly' do
74
+ history = SerialHistory.new('ruby-lang.org', 1001, rrs)
75
+ expect(history.data_at_serial(1001)).to eq(rrs)
76
+
77
+ history.set_serial_deletions(1002, [rrs.first])
78
+ history.serial_deletions(1002) << rrs.last
79
+ expect(history.data_at_serial(1002)).to eq([])
80
+ end
81
+
82
+ it 'adds records to the initial data correctly' do
83
+ history = SerialHistory.new('ruby-lang.org', 1001)
84
+ expect(history.data_at_serial(1001)).to eq([])
85
+
86
+ history.set_serial_additions(1002, [rrs.first])
87
+ history.serial_additions(1002) << rrs.last
88
+ expect(history.data_at_serial(1002)).to eq(rrs)
89
+ end
90
+
91
+ it 'accommodates a single RR instead of an array in the set methods' do
92
+ history = SerialHistory.new('ruby-lang.org', 1001)
93
+
94
+ history.set_serial_additions(1002, rrs.first)
95
+ expect(history.data_at_serial(1002)).to eq([rrs.first])
96
+
97
+ history.set_serial_deletions(1003, rrs.first)
98
+ expect(history.data_at_serial(1003)).to eq([])
99
+ end
100
+
101
+ it 'does not include serials greater than requested' do
102
+ history = SerialHistory.new('ruby-lang.org', 1001)
103
+ history.set_serial_additions(1002, rrs.last)
104
+ history.set_serial_additions(1003, rrs.first)
105
+ expect(history.data_at_serial(1002)).to eq([rrs.last])
106
+ end
107
+
108
+ it 'includes all serials if data_at_serial is called with :current' do
109
+ expect(history.data_at_serial(:current)).to eq(rrs)
110
+ end
111
+
112
+ it 'includes all serials when current_data is called' do
113
+ expect(history.current_data).to eq(rrs)
114
+ end
115
+
116
+ it 'raises an error if the requested serial is too high' do
117
+ expect(->() { history.data_at_serial(1004) }).to raise_error
118
+ end
119
+
120
+ it 'raises an error if the requested serial is too low' do
121
+ expect(->() { history.data_at_serial(1000) }).to raise_error
122
+ end
123
+
124
+ it 'raises an error if the requested serial is nil' do
125
+ expect(->() { history.data_at_serial(nil) }).to raise_error
126
+ end
127
+ end
128
+
129
+
130
+ context 'SerialHistory#previous_serial' do
131
+
132
+ let (:history) do
133
+ history = SerialHistory.new('ruby-lang.org', 1001)
134
+ history.set_serial_additions(1002, rrs.first)
135
+ history.set_serial_additions(1003, rrs.last)
136
+ history
137
+ end
138
+
139
+ it 'correctly finds the previous serial in the txns' do
140
+ expect(history.previous_serial(1003).value).to eq(1002)
141
+ end
142
+
143
+ it 'correctly determines the previous serial when it is the initial serial' do
144
+ expect(history.previous_serial(1002).value).to eq(1001)
145
+ end
146
+
147
+ it 'returns nil when passed the initial serial' do
148
+ expect(history.previous_serial(1001)).to be_nil
149
+ end
150
+
151
+ it 'returns nil when passed a too-low serial' do
152
+ expect(history.previous_serial(1000)).to be_nil
153
+ end
154
+
155
+ it 'returns nil when passed a too-high serial' do
156
+ expect(history.previous_serial(1004)).to be_nil
157
+ end
158
+ end
159
+
160
+
161
+ context "SerialHistory#ixfr_records" do
162
+
163
+ let (:history) do
164
+ history = SerialHistory.new('ruby-lang.org', 1001)
165
+ history.set_serial_additions(1002, rrs.first)
166
+ history.set_serial_additions(1003, rrs.last)
167
+ history.set_serial_additions(1004, rr('A', 'foo.ruby-lang.org', '9.10.11.12'))
168
+ history.set_serial_deletions(1004, rrs.first)
169
+ history
170
+ end
171
+
172
+ it 'returns a correct set of records' do
173
+ records = history.ixfr_records(1004)
174
+ check_soa = check_soa_fn(records, 'ruby-lang.org')
175
+ check_a = check_a_fn( records, 'foo.ruby-lang.org')
176
+
177
+ check_soa.( 0, 1004)
178
+ check_soa.( 1, 1001)
179
+ check_soa.( 2, 1002)
180
+ check_a.( 3, rrs.first.address)
181
+ check_soa.( 4, 1002)
182
+ check_soa.( 5, 1003)
183
+ check_a.( 6, rrs.last.address)
184
+ check_soa.( 7, 1003)
185
+ check_a.( 8, rrs.first.address)
186
+ check_soa.( 9, 1004)
187
+ check_a.( 10, '9.10.11.12')
188
+ end
189
+ end
190
+
191
+
192
+ context "SerialHistory#axfr_records" do
193
+
194
+ let (:history) do
195
+ history = SerialHistory.new('ruby-lang.org', 1001)
196
+ history.set_serial_additions(1002, rrs.first)
197
+ history.set_serial_additions(1003, rrs.last)
198
+ history.set_serial_additions(1004, rr('A', 'foo.ruby-lang.org', '9.10.11.12'))
199
+ history.set_serial_deletions(1004, rrs.first)
200
+ history
201
+ end
202
+
203
+ it 'returns an AXFR record set if serial <= start_serial' do
204
+ records = history.axfr_records
205
+
206
+ check_soa = check_soa_fn(records, 'ruby-lang.org')
207
+ check_a = check_a_fn( records, 'foo.ruby-lang.org')
208
+
209
+ check_soa.(0, 1004)
210
+ check_a.( 1, rrs.last.address)
211
+ check_a.( 2, '9.10.11.12')
212
+ check_soa.(3, 1004)
213
+ end
214
+ end
215
+
216
+
217
+ context "SerialHistory#check_new_serial" do
218
+
219
+ it 'raises an error if the new serial < the initial serial of the history' do
220
+ history = SerialHistory.new('ruby-lang.org', 1001)
221
+ expect(->() { history.check_serial(999) }).to raise_error
222
+ end
223
+
224
+ it 'raises an error if the new serial is not higher than the highest serial of the history' do
225
+ history = SerialHistory.new('ruby-lang.org', 1001)
226
+ history.set_serial_additions(1002, rrs.first)
227
+ expect(->() { history.check_serial(1001) }).to raise_error
228
+ end
229
+ end
230
+
231
+
232
+ context 'SerialHistory#xfr_array_type' do
233
+
234
+ let (:history) do
235
+ history = SerialHistory.new('ruby-lang.org', 1001)
236
+ history.set_serial_additions(1002, rrs.first)
237
+ history.set_serial_additions(1003, rrs.last)
238
+ history.set_serial_additions(1004, rr('A', 'abc.ruby-lang.org', '9.10.11.12'))
239
+ history.set_serial_deletions(1004, rrs.first)
240
+ history
241
+ end
242
+
243
+ it 'should return :axfr for AXFR records' do
244
+ expect(history.xfr_array_type(history.axfr_records)).to eq(:axfr)
245
+ end
246
+
247
+ it 'should return :ixfr for IXFR records' do
248
+ expect(history.xfr_array_type(history.ixfr_records(1001))).to eq(:ixfr)
249
+ end
250
+
251
+ it 'should return :error for an empty array' do
252
+ expect(history.xfr_array_type([])).to eq(:error)
253
+ end
254
+ end
255
+
256
+
257
+ context 'SerialHistory#ixfr_response_style' do
258
+
259
+ let(:zone) { 'ruby-lang.org' }
260
+ let(:history) do
261
+ h = SerialHistory.new(zone, 1111)
262
+ h.set_serial_additions(1112, rr('A', 'abc.ruby-lang.org', '1.1.1.2'))
263
+ h.set_serial_additions(1113, rr('A', 'abc.ruby-lang.org', '1.1.1.3'))
264
+ h.set_serial_additions(1114, rr('A', 'abc.ruby-lang.org', '1.1.1.4'))
265
+ h.set_serial_deletions(1114, rr('A', 'abc.ruby-lang.org', '1.1.1.3'))
266
+ h
267
+ end
268
+
269
+ it "defaults to :never when a ixfr_response_uses_axfr_style value is not provided" do
270
+ expect(SerialHistory.new('ruby-lang.org', 1001).ixfr_response_uses_axfr_style).to eq(:never)
271
+ end
272
+
273
+ it "returns :single_soa when the query's serial == the highest_serial" do
274
+ history.ixfr_response_uses_axfr_style = :never
275
+ expect(history.ixfr_response_style(1114)).to eq(:single_soa)
276
+
277
+ history.ixfr_response_uses_axfr_style = :auto
278
+ expect(history.ixfr_response_style(1114)).to eq(:single_soa)
279
+
280
+ history.ixfr_response_uses_axfr_style = :always
281
+ expect(history.ixfr_response_style(1114)).to eq(:single_soa)
282
+ end
283
+
284
+ it "returns :single_soa when the query's serial > the highest_serial" do
285
+ history.ixfr_response_uses_axfr_style = :never
286
+ expect(history.ixfr_response_style(1115)).to eq(:single_soa)
287
+
288
+ history.ixfr_response_uses_axfr_style = :auto
289
+ expect(history.ixfr_response_style(1115)).to eq(:single_soa)
290
+
291
+ history.ixfr_response_uses_axfr_style = :always
292
+ expect(history.ixfr_response_style(1115)).to eq(:single_soa)
293
+ end
294
+
295
+ it "returns :ixfr for :never when the serial is tracked and < the highest serial" do
296
+ expect(history.ixfr_response_style(1111)).to eq(:ixfr)
297
+ expect(history.ixfr_response_style(1113)).to eq(:ixfr)
298
+ end
299
+
300
+ it "returns :xfer_failed for :never when the serial number is NOT tracked and < the highest serial" do
301
+ expect(history.ixfr_response_style(1110)).to eq(:xfer_failed)
302
+ end
303
+
304
+ it "returns :ixfr for :auto when the serial is tracked and < the highest serial" do
305
+ history.ixfr_response_uses_axfr_style = :auto
306
+ expect(history.ixfr_response_style(1111)).to eq(:ixfr)
307
+ expect(history.ixfr_response_style(1113)).to eq(:ixfr)
308
+ end
309
+
310
+ it "returns :axfr_style_ixfr for :auto when the serial is NOT tracked and < the highest serial" do
311
+ history.ixfr_response_uses_axfr_style = :auto
312
+ expect(history.ixfr_response_style(1110)).to eq(:axfr_style_ixfr)
313
+ end
314
+
315
+ it "returns :axfr_style_ixfr for :always when the serial is NOT tracked and < the highest serial" do
316
+ history.ixfr_response_uses_axfr_style = :always
317
+ expect(history.ixfr_response_style(1110)).to eq(:axfr_style_ixfr)
318
+ end
319
+
320
+ it "returns :axfr_style_ixfr for :always when the serial is tracked and < the highest serial" do
321
+ history.ixfr_response_uses_axfr_style = :always
322
+ expect(history.ixfr_response_style(1112)).to eq(:axfr_style_ixfr)
323
+ end
324
+
325
+ end
326
+
327
+
328
+ context "SerialHistory#is_tracked_serial" do
329
+
330
+ let(:history) do
331
+ h = SerialHistory.new('ruby-lang.org', 1111)
332
+ h.set_serial_additions(1112, rr('A', 'abc.ruby-lang.org', '1.1.1.2'))
333
+ h.set_serial_additions(1113, rr('A', 'abc.ruby-lang.org', '1.1.1.3'))
334
+ h
335
+ end
336
+
337
+ it 'considers the low serial tracked' do
338
+ expect(history.is_tracked_serial(1111)).to be true
339
+ end
340
+
341
+ it 'considers the high serial tracked' do
342
+ expect(history.is_tracked_serial(1113)).to be true
343
+ end
344
+
345
+ it 'considers a too-low serial not tracked' do
346
+ expect(history.is_tracked_serial(1110)).to be false
347
+ end
348
+
349
+ it 'considers a too-high serial not tracked' do
350
+ expect(history.is_tracked_serial(1114)).to be false
351
+ end
352
+ end
353
+
354
+
355
+ context 'handles 0xFFFF_FFFF rollover' do
356
+ specify 'handles rollover from initial number' do
357
+ h = SerialHistory.new('ruby-lang.org', 0xFFFF_FFFF)
358
+ expect(->() { h.set_serial_additions(0, rr('A', 'abc.ruby-lang.org', '1.1.1.2')) }).not_to raise_error
359
+ expect(h.low_serial.value).to eq(0xFFFF_FFFF)
360
+ expect(h.high_serial.value).to eq(0)
361
+ end
362
+
363
+ specify 'handles rollover from initial number' do
364
+ h = SerialHistory.new('ruby-lang.org', 0xFFFF_FFFE)
365
+ h.set_serial_additions(0xFFFF_FFFF, rr('A', 'abc.ruby-lang.org', '1.1.1.2'))
366
+ h.set_serial_additions(0, rr('A', 'abc.ruby-lang.org', '1.1.1.3'))
367
+ expect(h.low_serial.value).to eq(0xFFFF_FFFE)
368
+ expect(h.high_serial.value).to eq(0)
369
+ end
370
+ end
371
+
372
+ context "SerialHistory#next_serial_value" do
373
+
374
+ specify 'offers the correct next serial value for 0' do
375
+ h = SerialHistory.new('ruby-lang.org', 0)
376
+ expect(h.next_serial_value).to eq(1)
377
+ end
378
+
379
+ specify 'offers the correct next serial value for 0xFFFF_FFFF' do
380
+ h = SerialHistory.new('ruby-lang.org', 0xFFFF_FFFF)
381
+ expect(h.next_serial_value).to eq(0)
382
+ end
383
+ end
384
+ end
385
+ end
@@ -0,0 +1,119 @@
1
+ require_relative '../spec_helper'
2
+ require 'mock_dns_server/serial_number'
3
+
4
+ describe SerialNumber do
5
+
6
+ SSS = SerialNumber
7
+
8
+
9
+ context 'initial value validation' do
10
+
11
+ def verify_init_error(value)
12
+ expect(->() { SerialNumber.new(value) }).to raise_error
13
+ end
14
+
15
+ def verify_init_ok(value)
16
+ expect(->() { SerialNumber.new(value) }).not_to raise_error
17
+ end
18
+
19
+ specify '-1 is not ok' do
20
+ verify_init_error(-1)
21
+ end
22
+
23
+ specify '0 is ok' do
24
+ verify_init_ok(0)
25
+ end
26
+
27
+ specify '1 is ok' do
28
+ verify_init_ok(0)
29
+ end
30
+
31
+ specify '0xFFFF_FFFF is ok' do
32
+ verify_init_ok(0xFFFF_FFFF)
33
+ end
34
+
35
+ specify '0x1_0000_0000 is not ok' do
36
+ verify_init_error(0x1_0000_0000)
37
+ end
38
+ end
39
+
40
+ context 'SequenceSpaceSerial#<=>' do
41
+
42
+ specify '0 should == 0' do
43
+ expect(SSS.new(0)).to eq(SSS.new(0))
44
+ end
45
+
46
+ specify '0 should < 0x7FFF_FFFF' do
47
+ expect(SSS.new(0)).to be < SSS.new(0x7FFF_FFFF)
48
+ end
49
+
50
+ specify '0 <=> 0x8000_0000 should raise an error' do
51
+ expect(->() { SSS.new(0) <=> SSS.new(0x8000_0000) }).to raise_error
52
+ end
53
+
54
+ specify '0 should > 0x8000_0001' do
55
+ expect(SSS.new(0)).to be > SSS.new(0x8000_0001)
56
+ end
57
+
58
+ specify '0xB000_0000 <=> 0xB000_0000 should == 0' do
59
+ expect(SSS.new(0xB000_0000) <=> SSS.new(0xB000_0000)).to eq(0)
60
+ end
61
+
62
+ specify '0xB000_0000 should == 0xB000_0000' do
63
+ expect(SSS.new(0xB000_0000)).to eq(SSS.new(0xB000_0000))
64
+ end
65
+
66
+ specify '0xB000_0000 should < 0x2FFF_FFFF' do
67
+ expect(SSS.new(0xB000_0000)).to be < SSS.new(0x2FFF_FFFF)
68
+ end
69
+
70
+ specify '0xB000_0000 <=> 0x3000_0000 should raise an error' do
71
+ expect(->() { SSS.new(0xB000_0000) <=> SSS.new(0x3000_0000) }).to raise_error
72
+ end
73
+
74
+ specify '0xB000_0000 should > 0x3000_0001' do
75
+ expect(SSS.new(0xB000_0000)).to be > SSS.new(0x3000_0001)
76
+ end
77
+ end
78
+
79
+
80
+ context 'SequenceSpaceSerial.next_serial_value' do
81
+
82
+ specify 'next of 0 to be 1' do
83
+ expect(SSS.next_serial_value(0)).to eq(1)
84
+ end
85
+
86
+ specify 'next of -1 to raise error' do
87
+ expect(->() { SSS.next_serial_value(-1) }).to raise_error
88
+ end
89
+
90
+ specify 'next of 0x1_0000_0000 to raise error' do
91
+ expect(->() { SSS.next_serial_value(0x1_0000_0000) }).to raise_error
92
+ end
93
+
94
+ specify 'next of 0xFFFF_FFFF to be 0' do
95
+ expect(SSS.next_serial_value(0xFFFF_FFFF)).to eq(0)
96
+ end
97
+
98
+ specify 'next of 0x1234_5678 to be 0x1234_5679' do
99
+ expect(SSS.next_serial_value(0x1234_5678)).to eq(0x1234_5679)
100
+ end
101
+
102
+ end
103
+
104
+
105
+ context 'SequenceSpaceSerial#next_serial' do
106
+
107
+ specify 'next of 0 to be 1' do
108
+ expect(SSS.new(0).next_serial).to eq(SSS.new(1))
109
+ end
110
+
111
+ specify 'next of 0xFFFF_FFFF to be 0' do
112
+ expect(SSS.new(0xFFFF_FFFF).next_serial).to eq(SSS.new(0))
113
+ end
114
+
115
+ specify 'next of 0x1234_5678 to be 0x1234_5679' do
116
+ expect(SSS.new(0x1234_5678).next_serial).to eq(SSS.new(0x1234_5679))
117
+ end
118
+ end
119
+ end