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