@@ -64,6 +64,50 @@ def with_udp(host, port)
64
64
end
65
65
end
66
66
67
+ def with_udp_and_tcp ( host , port )
68
+ if port == 0
69
+ # Automatic port; we might need to retry until we find a port which is free on both UDP _and_ TCP.
70
+ retries_remaining = 5
71
+ t = nil
72
+ u = nil
73
+ begin
74
+ begin
75
+ u = UDPSocket . new
76
+ u . bind ( host , 0 )
77
+ _ , udp_port , _ , _ = u . addr
78
+ t = TCPServer . new ( host , udp_port )
79
+ t . listen ( 1 )
80
+ rescue Errno ::EADDRINUSE , Errno ::EACCES
81
+ # ADDRINUSE is what should get thrown if we try and bind a port which is already bound on UNIXen,
82
+ # but windows can sometimes throw EACCESS.
83
+ # See: https://stackoverflow.com/questions/48478869/cannot-bind-to-some-ports-due-to-permission-denied
84
+ retries_remaining -= 1
85
+ if retries_remaining > 0
86
+ t &.close
87
+ t = nil
88
+ u &.close
89
+ u = nil
90
+ retry
91
+ end
92
+ raise
93
+ end
94
+
95
+ # If we get to this point, we have a valid t & u socket
96
+ yield u , t
97
+ ensure
98
+ t &.close
99
+ u &.close
100
+ end
101
+ else
102
+ # Explicitly specified port, don't retry the bind.
103
+ with_udp ( host , port ) do |u |
104
+ with_tcp ( host , port ) do |t |
105
+ yield u , t
106
+ end
107
+ end
108
+ end
109
+ end
110
+
67
111
# [ruby-core:65836]
68
112
def test_resolve_with_2_ndots
69
113
conf = Resolv ::DNS ::Config . new :nameserver => [ '127.0.0.1' ] , :ndots => 2
@@ -176,156 +220,154 @@ def test_query_ipv4_address_truncated_tcp_fallback
176
220
177
221
num_records = 50
178
222
179
- with_udp ( '127.0.0.1' , 0 ) { |u |
223
+ with_udp_and_tcp ( '127.0.0.1' , 0 ) { |u , t |
180
224
_ , server_port , _ , server_address = u . addr
181
- with_tcp ( '127.0.0.1' , server_port ) { |t |
182
- client_thread = Thread . new {
183
- Resolv ::DNS . open ( :nameserver_port => [ [ server_address , server_port ] ] ) { |dns |
184
- dns . getresources ( "foo.example.org" , Resolv ::DNS ::Resource ::IN ::A )
185
- }
186
- }
187
- udp_server_thread = Thread . new {
188
- msg , ( _ , client_port , _ , client_address ) = Timeout . timeout ( 5 ) { u . recvfrom ( 4096 ) }
189
- id , word2 , qdcount , ancount , nscount , arcount = msg . unpack ( "nnnnnn" )
190
- qr = ( word2 & 0x8000 ) >> 15
191
- opcode = ( word2 & 0x7800 ) >> 11
192
- aa = ( word2 & 0x0400 ) >> 10
193
- tc = ( word2 & 0x0200 ) >> 9
194
- rd = ( word2 & 0x0100 ) >> 8
195
- ra = ( word2 & 0x0080 ) >> 7
196
- z = ( word2 & 0x0070 ) >> 4
197
- rcode = word2 & 0x000f
198
- rest = msg [ 12 ..-1 ]
199
- assert_equal ( 0 , qr ) # 0:query 1:response
200
- assert_equal ( 0 , opcode ) # 0:QUERY 1:IQUERY 2:STATUS
201
- assert_equal ( 0 , aa ) # Authoritative Answer
202
- assert_equal ( 0 , tc ) # TrunCation
203
- assert_equal ( 1 , rd ) # Recursion Desired
204
- assert_equal ( 0 , ra ) # Recursion Available
205
- assert_equal ( 0 , z ) # Reserved for future use
206
- assert_equal ( 0 , rcode ) # 0:No-error 1:Format-error 2:Server-failure 3:Name-Error 4:Not-Implemented 5:Refused
207
- assert_equal ( 1 , qdcount ) # number of entries in the question section.
208
- assert_equal ( 0 , ancount ) # number of entries in the answer section.
209
- assert_equal ( 0 , nscount ) # number of entries in the authority records section.
210
- assert_equal ( 0 , arcount ) # number of entries in the additional records section.
211
- name = [ 3 , "foo" , 7 , "example" , 3 , "org" , 0 ] . pack ( "Ca*Ca*Ca*C" )
212
- assert_operator ( rest , :start_with? , name )
213
- rest = rest [ name . length ..-1 ]
214
- assert_equal ( 4 , rest . length )
215
- qtype , _ = rest . unpack ( "nn" )
216
- assert_equal ( 1 , qtype ) # A
217
- assert_equal ( 1 , qtype ) # IN
218
- id = id
219
- qr = 1
220
- opcode = opcode
221
- aa = 0
222
- tc = 1
223
- rd = rd
224
- ra = 1
225
- z = 0
226
- rcode = 0
227
- qdcount = 0
228
- ancount = num_records
229
- nscount = 0
230
- arcount = 0
231
- word2 = ( qr << 15 ) |
232
- ( opcode << 11 ) |
233
- ( aa << 10 ) |
234
- ( tc << 9 ) |
235
- ( rd << 8 ) |
236
- ( ra << 7 ) |
237
- ( z << 4 ) |
238
- rcode
239
- msg = [ id , word2 , qdcount , ancount , nscount , arcount ] . pack ( "nnnnnn" )
240
- type = 1
241
- klass = 1
242
- ttl = 3600
243
- rdlength = 4
244
- num_records . times do |i |
245
- rdata = [ 192 , 0 , 2 , i ] . pack ( "CCCC" ) # 192.0.2.x (TEST-NET address) RFC 3330
246
- rr = [ name , type , klass , ttl , rdlength , rdata ] . pack ( "a*nnNna*" )
247
- msg << rr
248
- end
249
- u . send ( msg [ 0 ...512 ] , 0 , client_address , client_port )
250
- }
251
- tcp_server_thread = Thread . new {
252
- ct = t . accept
253
- msg = ct . recv ( 512 )
254
- msg . slice! ( 0 ..1 ) # Size (only for TCP)
255
- id , word2 , qdcount , ancount , nscount , arcount = msg . unpack ( "nnnnnn" )
256
- qr = ( word2 & 0x8000 ) >> 15
257
- opcode = ( word2 & 0x7800 ) >> 11
258
- aa = ( word2 & 0x0400 ) >> 10
259
- tc = ( word2 & 0x0200 ) >> 9
260
- rd = ( word2 & 0x0100 ) >> 8
261
- ra = ( word2 & 0x0080 ) >> 7
262
- z = ( word2 & 0x0070 ) >> 4
263
- rcode = word2 & 0x000f
264
- rest = msg [ 12 ..-1 ]
265
- assert_equal ( 0 , qr ) # 0:query 1:response
266
- assert_equal ( 0 , opcode ) # 0:QUERY 1:IQUERY 2:STATUS
267
- assert_equal ( 0 , aa ) # Authoritative Answer
268
- assert_equal ( 0 , tc ) # TrunCation
269
- assert_equal ( 1 , rd ) # Recursion Desired
270
- assert_equal ( 0 , ra ) # Recursion Available
271
- assert_equal ( 0 , z ) # Reserved for future use
272
- assert_equal ( 0 , rcode ) # 0:No-error 1:Format-error 2:Server-failure 3:Name-Error 4:Not-Implemented 5:Refused
273
- assert_equal ( 1 , qdcount ) # number of entries in the question section.
274
- assert_equal ( 0 , ancount ) # number of entries in the answer section.
275
- assert_equal ( 0 , nscount ) # number of entries in the authority records section.
276
- assert_equal ( 0 , arcount ) # number of entries in the additional records section.
277
- name = [ 3 , "foo" , 7 , "example" , 3 , "org" , 0 ] . pack ( "Ca*Ca*Ca*C" )
278
- assert_operator ( rest , :start_with? , name )
279
- rest = rest [ name . length ..-1 ]
280
- assert_equal ( 4 , rest . length )
281
- qtype , _ = rest . unpack ( "nn" )
282
- assert_equal ( 1 , qtype ) # A
283
- assert_equal ( 1 , qtype ) # IN
284
- id = id
285
- qr = 1
286
- opcode = opcode
287
- aa = 0
288
- tc = 0
289
- rd = rd
290
- ra = 1
291
- z = 0
292
- rcode = 0
293
- qdcount = 0
294
- ancount = num_records
295
- nscount = 0
296
- arcount = 0
297
- word2 = ( qr << 15 ) |
298
- ( opcode << 11 ) |
299
- ( aa << 10 ) |
300
- ( tc << 9 ) |
301
- ( rd << 8 ) |
302
- ( ra << 7 ) |
303
- ( z << 4 ) |
304
- rcode
305
- msg = [ id , word2 , qdcount , ancount , nscount , arcount ] . pack ( "nnnnnn" )
306
- type = 1
307
- klass = 1
308
- ttl = 3600
309
- rdlength = 4
310
- num_records . times do |i |
311
- rdata = [ 192 , 0 , 2 , i ] . pack ( "CCCC" ) # 192.0.2.x (TEST-NET address) RFC 3330
312
- rr = [ name , type , klass , ttl , rdlength , rdata ] . pack ( "a*nnNna*" )
313
- msg << rr
314
- end
315
- msg = "#{ [ msg . bytesize ] . pack ( "n" ) } #{ msg } " # Prefix with size
316
- ct . send ( msg , 0 )
317
- ct . close
225
+ client_thread = Thread . new {
226
+ Resolv ::DNS . open ( :nameserver_port => [ [ server_address , server_port ] ] ) { |dns |
227
+ dns . getresources ( "foo.example.org" , Resolv ::DNS ::Resource ::IN ::A )
318
228
}
319
- result , _ = assert_join_threads ( [ client_thread , udp_server_thread , tcp_server_thread ] )
320
- assert_instance_of ( Array , result )
321
- assert_equal ( 50 , result . length )
322
- result . each_with_index do |rr , i |
323
- assert_instance_of ( Resolv ::DNS ::Resource ::IN ::A , rr )
324
- assert_instance_of ( Resolv ::IPv4 , rr . address )
325
- assert_equal ( "192.0.2.#{ i } " , rr . address . to_s )
326
- assert_equal ( 3600 , rr . ttl )
229
+ }
230
+ udp_server_thread = Thread . new {
231
+ msg , ( _ , client_port , _ , client_address ) = Timeout . timeout ( 5 ) { u . recvfrom ( 4096 ) }
232
+ id , word2 , qdcount , ancount , nscount , arcount = msg . unpack ( "nnnnnn" )
233
+ qr = ( word2 & 0x8000 ) >> 15
234
+ opcode = ( word2 & 0x7800 ) >> 11
235
+ aa = ( word2 & 0x0400 ) >> 10
236
+ tc = ( word2 & 0x0200 ) >> 9
237
+ rd = ( word2 & 0x0100 ) >> 8
238
+ ra = ( word2 & 0x0080 ) >> 7
239
+ z = ( word2 & 0x0070 ) >> 4
240
+ rcode = word2 & 0x000f
241
+ rest = msg [ 12 ..-1 ]
242
+ assert_equal ( 0 , qr ) # 0:query 1:response
243
+ assert_equal ( 0 , opcode ) # 0:QUERY 1:IQUERY 2:STATUS
244
+ assert_equal ( 0 , aa ) # Authoritative Answer
245
+ assert_equal ( 0 , tc ) # TrunCation
246
+ assert_equal ( 1 , rd ) # Recursion Desired
247
+ assert_equal ( 0 , ra ) # Recursion Available
248
+ assert_equal ( 0 , z ) # Reserved for future use
249
+ assert_equal ( 0 , rcode ) # 0:No-error 1:Format-error 2:Server-failure 3:Name-Error 4:Not-Implemented 5:Refused
250
+ assert_equal ( 1 , qdcount ) # number of entries in the question section.
251
+ assert_equal ( 0 , ancount ) # number of entries in the answer section.
252
+ assert_equal ( 0 , nscount ) # number of entries in the authority records section.
253
+ assert_equal ( 0 , arcount ) # number of entries in the additional records section.
254
+ name = [ 3 , "foo" , 7 , "example" , 3 , "org" , 0 ] . pack ( "Ca*Ca*Ca*C" )
255
+ assert_operator ( rest , :start_with? , name )
256
+ rest = rest [ name . length ..-1 ]
257
+ assert_equal ( 4 , rest . length )
258
+ qtype , _ = rest . unpack ( "nn" )
259
+ assert_equal ( 1 , qtype ) # A
260
+ assert_equal ( 1 , qtype ) # IN
261
+ id = id
262
+ qr = 1
263
+ opcode = opcode
264
+ aa = 0
265
+ tc = 1
266
+ rd = rd
267
+ ra = 1
268
+ z = 0
269
+ rcode = 0
270
+ qdcount = 0
271
+ ancount = num_records
272
+ nscount = 0
273
+ arcount = 0
274
+ word2 = ( qr << 15 ) |
275
+ ( opcode << 11 ) |
276
+ ( aa << 10 ) |
277
+ ( tc << 9 ) |
278
+ ( rd << 8 ) |
279
+ ( ra << 7 ) |
280
+ ( z << 4 ) |
281
+ rcode
282
+ msg = [ id , word2 , qdcount , ancount , nscount , arcount ] . pack ( "nnnnnn" )
283
+ type = 1
284
+ klass = 1
285
+ ttl = 3600
286
+ rdlength = 4
287
+ num_records . times do |i |
288
+ rdata = [ 192 , 0 , 2 , i ] . pack ( "CCCC" ) # 192.0.2.x (TEST-NET address) RFC 3330
289
+ rr = [ name , type , klass , ttl , rdlength , rdata ] . pack ( "a*nnNna*" )
290
+ msg << rr
327
291
end
292
+ u . send ( msg [ 0 ...512 ] , 0 , client_address , client_port )
328
293
}
294
+ tcp_server_thread = Thread . new {
295
+ ct = t . accept
296
+ msg = ct . recv ( 512 )
297
+ msg . slice! ( 0 ..1 ) # Size (only for TCP)
298
+ id , word2 , qdcount , ancount , nscount , arcount = msg . unpack ( "nnnnnn" )
299
+ qr = ( word2 & 0x8000 ) >> 15
300
+ opcode = ( word2 & 0x7800 ) >> 11
301
+ aa = ( word2 & 0x0400 ) >> 10
302
+ tc = ( word2 & 0x0200 ) >> 9
303
+ rd = ( word2 & 0x0100 ) >> 8
304
+ ra = ( word2 & 0x0080 ) >> 7
305
+ z = ( word2 & 0x0070 ) >> 4
306
+ rcode = word2 & 0x000f
307
+ rest = msg [ 12 ..-1 ]
308
+ assert_equal ( 0 , qr ) # 0:query 1:response
309
+ assert_equal ( 0 , opcode ) # 0:QUERY 1:IQUERY 2:STATUS
310
+ assert_equal ( 0 , aa ) # Authoritative Answer
311
+ assert_equal ( 0 , tc ) # TrunCation
312
+ assert_equal ( 1 , rd ) # Recursion Desired
313
+ assert_equal ( 0 , ra ) # Recursion Available
314
+ assert_equal ( 0 , z ) # Reserved for future use
315
+ assert_equal ( 0 , rcode ) # 0:No-error 1:Format-error 2:Server-failure 3:Name-Error 4:Not-Implemented 5:Refused
316
+ assert_equal ( 1 , qdcount ) # number of entries in the question section.
317
+ assert_equal ( 0 , ancount ) # number of entries in the answer section.
318
+ assert_equal ( 0 , nscount ) # number of entries in the authority records section.
319
+ assert_equal ( 0 , arcount ) # number of entries in the additional records section.
320
+ name = [ 3 , "foo" , 7 , "example" , 3 , "org" , 0 ] . pack ( "Ca*Ca*Ca*C" )
321
+ assert_operator ( rest , :start_with? , name )
322
+ rest = rest [ name . length ..-1 ]
323
+ assert_equal ( 4 , rest . length )
324
+ qtype , _ = rest . unpack ( "nn" )
325
+ assert_equal ( 1 , qtype ) # A
326
+ assert_equal ( 1 , qtype ) # IN
327
+ id = id
328
+ qr = 1
329
+ opcode = opcode
330
+ aa = 0
331
+ tc = 0
332
+ rd = rd
333
+ ra = 1
334
+ z = 0
335
+ rcode = 0
336
+ qdcount = 0
337
+ ancount = num_records
338
+ nscount = 0
339
+ arcount = 0
340
+ word2 = ( qr << 15 ) |
341
+ ( opcode << 11 ) |
342
+ ( aa << 10 ) |
343
+ ( tc << 9 ) |
344
+ ( rd << 8 ) |
345
+ ( ra << 7 ) |
346
+ ( z << 4 ) |
347
+ rcode
348
+ msg = [ id , word2 , qdcount , ancount , nscount , arcount ] . pack ( "nnnnnn" )
349
+ type = 1
350
+ klass = 1
351
+ ttl = 3600
352
+ rdlength = 4
353
+ num_records . times do |i |
354
+ rdata = [ 192 , 0 , 2 , i ] . pack ( "CCCC" ) # 192.0.2.x (TEST-NET address) RFC 3330
355
+ rr = [ name , type , klass , ttl , rdlength , rdata ] . pack ( "a*nnNna*" )
356
+ msg << rr
357
+ end
358
+ msg = "#{ [ msg . bytesize ] . pack ( "n" ) } #{ msg } " # Prefix with size
359
+ ct . send ( msg , 0 )
360
+ ct . close
361
+ }
362
+ result , _ = assert_join_threads ( [ client_thread , udp_server_thread , tcp_server_thread ] )
363
+ assert_instance_of ( Array , result )
364
+ assert_equal ( 50 , result . length )
365
+ result . each_with_index do |rr , i |
366
+ assert_instance_of ( Resolv ::DNS ::Resource ::IN ::A , rr )
367
+ assert_instance_of ( Resolv ::IPv4 , rr . address )
368
+ assert_equal ( "192.0.2.#{ i } " , rr . address . to_s )
369
+ assert_equal ( 3600 , rr . ttl )
370
+ end
329
371
}
330
372
end
331
373
0 commit comments