rubysl-resolv 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/lib/rubysl/resolv/resolv.rb +239 -100
- data/lib/rubysl/resolv/version.rb +1 -1
- data/rubysl-resolv.gemspec +2 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f910ca03a2f2e7fc5ba345a15f7e35786dd07b5
|
4
|
+
data.tar.gz: 95ab27ed98857c6cc1055b92a6c47d008a9d3ec0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8be3e9e9377c375c5bd7a88f3d6c48a7536af1fe6133e939b738f946af57525b6f70fc48993a015c1a5db04e440890bfbe78ce5dc22c9061c058b628ae18a24a
|
7
|
+
data.tar.gz: ec78bb44fc70c12de9caee8c9cfa7c9d8542f83b7eda0e43f7bd9dfa86ac38adafd6a7fba69c281993ce82673669534827584f3df96d0763ba28b69678ab387b
|
data/.travis.yml
CHANGED
data/lib/rubysl/resolv/resolv.rb
CHANGED
@@ -9,28 +9,28 @@ rescue LoadError
|
|
9
9
|
end
|
10
10
|
|
11
11
|
# Resolv is a thread-aware DNS resolver library written in Ruby. Resolv can
|
12
|
-
# handle multiple DNS requests concurrently without blocking
|
12
|
+
# handle multiple DNS requests concurrently without blocking the entire ruby
|
13
13
|
# interpreter.
|
14
14
|
#
|
15
|
-
# See also resolv-replace.rb to replace the libc resolver with
|
16
|
-
#
|
15
|
+
# See also resolv-replace.rb to replace the libc resolver with Resolv.
|
16
|
+
#
|
17
17
|
# Resolv can look up various DNS resources using the DNS module directly.
|
18
|
-
#
|
18
|
+
#
|
19
19
|
# Examples:
|
20
|
-
#
|
20
|
+
#
|
21
21
|
# p Resolv.getaddress "www.ruby-lang.org"
|
22
22
|
# p Resolv.getname "210.251.121.214"
|
23
|
-
#
|
23
|
+
#
|
24
24
|
# Resolv::DNS.open do |dns|
|
25
25
|
# ress = dns.getresources "www.ruby-lang.org", Resolv::DNS::Resource::IN::A
|
26
26
|
# p ress.map { |r| r.address }
|
27
27
|
# ress = dns.getresources "ruby-lang.org", Resolv::DNS::Resource::IN::MX
|
28
28
|
# p ress.map { |r| [r.exchange.to_s, r.preference] }
|
29
29
|
# end
|
30
|
-
#
|
31
|
-
#
|
30
|
+
#
|
31
|
+
#
|
32
32
|
# == Bugs
|
33
|
-
#
|
33
|
+
#
|
34
34
|
# * NIS is not supported.
|
35
35
|
# * /etc/nsswitch.conf is not supported.
|
36
36
|
|
@@ -38,14 +38,14 @@ class Resolv
|
|
38
38
|
|
39
39
|
##
|
40
40
|
# Looks up the first IP address for +name+.
|
41
|
-
|
41
|
+
|
42
42
|
def self.getaddress(name)
|
43
43
|
DefaultResolver.getaddress(name)
|
44
44
|
end
|
45
45
|
|
46
46
|
##
|
47
47
|
# Looks up all IP address for +name+.
|
48
|
-
|
48
|
+
|
49
49
|
def self.getaddresses(name)
|
50
50
|
DefaultResolver.getaddresses(name)
|
51
51
|
end
|
@@ -87,7 +87,7 @@ class Resolv
|
|
87
87
|
|
88
88
|
##
|
89
89
|
# Looks up the first IP address for +name+.
|
90
|
-
|
90
|
+
|
91
91
|
def getaddress(name)
|
92
92
|
each_address(name) {|address| return address}
|
93
93
|
raise ResolvError.new("no address for #{name}")
|
@@ -95,7 +95,7 @@ class Resolv
|
|
95
95
|
|
96
96
|
##
|
97
97
|
# Looks up all IP address for +name+.
|
98
|
-
|
98
|
+
|
99
99
|
def getaddresses(name)
|
100
100
|
ret = []
|
101
101
|
each_address(name) {|address| ret << address}
|
@@ -162,10 +162,10 @@ class Resolv
|
|
162
162
|
class ResolvTimeout < TimeoutError; end
|
163
163
|
|
164
164
|
##
|
165
|
-
#
|
165
|
+
# Resolv::Hosts is a hostname resolver that uses the system hosts file.
|
166
166
|
|
167
167
|
class Hosts
|
168
|
-
if /
|
168
|
+
if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM
|
169
169
|
require 'win32/resolv'
|
170
170
|
DefaultFileName = Win32::Resolv.get_hosts_path
|
171
171
|
else
|
@@ -173,7 +173,7 @@ class Resolv
|
|
173
173
|
end
|
174
174
|
|
175
175
|
##
|
176
|
-
# Creates a new
|
176
|
+
# Creates a new Resolv::Hosts, using +filename+ for its data source.
|
177
177
|
|
178
178
|
def initialize(filename = DefaultFileName)
|
179
179
|
@filename = filename
|
@@ -289,11 +289,6 @@ class Resolv
|
|
289
289
|
|
290
290
|
UDPSize = 512
|
291
291
|
|
292
|
-
##
|
293
|
-
# Group of DNS resolver threads (obsolete)
|
294
|
-
|
295
|
-
DNSThreadGroup = ThreadGroup.new
|
296
|
-
|
297
292
|
##
|
298
293
|
# Creates a new DNS resolver. See Resolv::DNS.new for argument details.
|
299
294
|
#
|
@@ -314,10 +309,20 @@ class Resolv
|
|
314
309
|
# Creates a new DNS resolver.
|
315
310
|
#
|
316
311
|
# +config_info+ can be:
|
317
|
-
#
|
312
|
+
#
|
318
313
|
# nil:: Uses /etc/resolv.conf.
|
319
314
|
# String:: Path to a file using /etc/resolv.conf's format.
|
320
315
|
# Hash:: Must contain :nameserver, :search and :ndots keys.
|
316
|
+
# :nameserver_port can be used to specify port number of nameserver address.
|
317
|
+
#
|
318
|
+
# The value of :nameserver should be an address string or
|
319
|
+
# an array of address strings.
|
320
|
+
# - :nameserver => '8.8.8.8'
|
321
|
+
# - :nameserver => ['8.8.8.8', '8.8.4.4']
|
322
|
+
#
|
323
|
+
# The value of :nameserver_port should be an array of
|
324
|
+
# pair of nameserver address and port number.
|
325
|
+
# - :nameserver_port => [['8.8.8.8', 53], ['8.8.4.4', 53]]
|
321
326
|
#
|
322
327
|
# Example:
|
323
328
|
#
|
@@ -331,6 +336,21 @@ class Resolv
|
|
331
336
|
@initialized = nil
|
332
337
|
end
|
333
338
|
|
339
|
+
# Sets the resolver timeouts. This may be a single positive number
|
340
|
+
# or an array of positive numbers representing timeouts in seconds.
|
341
|
+
# If an array is specified, a DNS request will retry and wait for
|
342
|
+
# each successive interval in the array until a successful response
|
343
|
+
# is received. Specifying +nil+ reverts to the default timeouts:
|
344
|
+
# [ 5, second = 5 * 2 / nameserver_count, 2 * second, 4 * second ]
|
345
|
+
#
|
346
|
+
# Example:
|
347
|
+
#
|
348
|
+
# dns.timeouts = 3
|
349
|
+
#
|
350
|
+
def timeouts=(values)
|
351
|
+
@config.timeouts = values
|
352
|
+
end
|
353
|
+
|
334
354
|
def lazy_initialize # :nodoc:
|
335
355
|
@mutex.synchronize {
|
336
356
|
unless @initialized
|
@@ -384,9 +404,21 @@ class Resolv
|
|
384
404
|
|
385
405
|
def each_address(name)
|
386
406
|
each_resource(name, Resource::IN::A) {|resource| yield resource.address}
|
387
|
-
|
407
|
+
if use_ipv6?
|
408
|
+
each_resource(name, Resource::IN::AAAA) {|resource| yield resource.address}
|
409
|
+
end
|
388
410
|
end
|
389
411
|
|
412
|
+
def use_ipv6? # :nodoc:
|
413
|
+
begin
|
414
|
+
list = Socket.ip_address_list
|
415
|
+
rescue NotImplementedError
|
416
|
+
return true
|
417
|
+
end
|
418
|
+
list.any? {|a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? }
|
419
|
+
end
|
420
|
+
private :use_ipv6?
|
421
|
+
|
390
422
|
##
|
391
423
|
# Gets the hostname for +address+ from the DNS resolver.
|
392
424
|
#
|
@@ -462,7 +494,7 @@ class Resolv
|
|
462
494
|
##
|
463
495
|
# Looks up all +typeclass+ DNS resources for +name+. See #getresource for
|
464
496
|
# argument details.
|
465
|
-
|
497
|
+
|
466
498
|
def getresources(name, typeclass)
|
467
499
|
ret = []
|
468
500
|
each_resource(name, typeclass) {|resource| ret << resource}
|
@@ -472,24 +504,36 @@ class Resolv
|
|
472
504
|
##
|
473
505
|
# Iterates over all +typeclass+ DNS resources for +name+. See
|
474
506
|
# #getresource for argument details.
|
475
|
-
|
507
|
+
|
476
508
|
def each_resource(name, typeclass, &proc)
|
477
509
|
lazy_initialize
|
478
|
-
requester =
|
510
|
+
requester = make_udp_requester
|
479
511
|
senders = {}
|
480
512
|
begin
|
481
|
-
@config.resolv(name) {|candidate, tout, nameserver|
|
513
|
+
@config.resolv(name) {|candidate, tout, nameserver, port|
|
482
514
|
msg = Message.new
|
483
515
|
msg.rd = 1
|
484
516
|
msg.add_question(candidate, typeclass)
|
485
|
-
unless sender = senders[[candidate, nameserver]]
|
486
|
-
sender = senders[[candidate, nameserver]] =
|
487
|
-
requester.sender(msg, candidate, nameserver)
|
517
|
+
unless sender = senders[[candidate, nameserver, port]]
|
518
|
+
sender = senders[[candidate, nameserver, port]] =
|
519
|
+
requester.sender(msg, candidate, nameserver, port)
|
488
520
|
end
|
489
521
|
reply, reply_name = requester.request(sender, tout)
|
490
522
|
case reply.rcode
|
491
523
|
when RCode::NoError
|
492
|
-
|
524
|
+
if reply.tc == 1 and not Requester::TCP === requester
|
525
|
+
requester.close
|
526
|
+
# Retry via TCP:
|
527
|
+
requester = make_tcp_requester(nameserver, port)
|
528
|
+
senders = {}
|
529
|
+
# This will use TCP for all remaining candidates (assuming the
|
530
|
+
# current candidate does not already respond successfully via
|
531
|
+
# TCP). This makes sense because we already know the full
|
532
|
+
# response will not fit in an untruncated UDP packet.
|
533
|
+
redo
|
534
|
+
else
|
535
|
+
extract_resources(reply, reply_name, typeclass, &proc)
|
536
|
+
end
|
493
537
|
return
|
494
538
|
when RCode::NXDomain
|
495
539
|
raise Config::NXDomain.new(reply_name.to_s)
|
@@ -502,14 +546,19 @@ class Resolv
|
|
502
546
|
end
|
503
547
|
end
|
504
548
|
|
505
|
-
def
|
506
|
-
|
507
|
-
|
549
|
+
def make_udp_requester # :nodoc:
|
550
|
+
nameserver_port = @config.nameserver_port
|
551
|
+
if nameserver_port.length == 1
|
552
|
+
Requester::ConnectedUDP.new(*nameserver_port[0])
|
508
553
|
else
|
509
|
-
Requester::UnconnectedUDP.new
|
554
|
+
Requester::UnconnectedUDP.new(*nameserver_port)
|
510
555
|
end
|
511
556
|
end
|
512
557
|
|
558
|
+
def make_tcp_requester(host, port) # :nodoc:
|
559
|
+
return Requester::TCP.new(host, port)
|
560
|
+
end
|
561
|
+
|
513
562
|
def extract_resources(msg, name, typeclass) # :nodoc:
|
514
563
|
if typeclass < Resource::ANY
|
515
564
|
n0 = Name.create(name)
|
@@ -565,8 +614,8 @@ class Resolv
|
|
565
614
|
base + random(len)
|
566
615
|
end
|
567
616
|
|
568
|
-
RequestID = {}
|
569
|
-
RequestIDMutex = Mutex.new
|
617
|
+
RequestID = {} # :nodoc:
|
618
|
+
RequestIDMutex = Mutex.new # :nodoc:
|
570
619
|
|
571
620
|
def self.allocate_request_id(host, port) # :nodoc:
|
572
621
|
id = nil
|
@@ -574,7 +623,7 @@ class Resolv
|
|
574
623
|
h = (RequestID[[host, port]] ||= {})
|
575
624
|
begin
|
576
625
|
id = rangerand(0x0000..0xffff)
|
577
|
-
end while h[id]
|
626
|
+
end while h[id]
|
578
627
|
h[id] = true
|
579
628
|
}
|
580
629
|
id
|
@@ -592,10 +641,10 @@ class Resolv
|
|
592
641
|
}
|
593
642
|
end
|
594
643
|
|
595
|
-
def self.bind_random_port(udpsock) # :nodoc:
|
644
|
+
def self.bind_random_port(udpsock, bind_host="0.0.0.0") # :nodoc:
|
596
645
|
begin
|
597
646
|
port = rangerand(1024..65535)
|
598
|
-
udpsock.bind(
|
647
|
+
udpsock.bind(bind_host, port)
|
599
648
|
rescue Errno::EADDRINUSE
|
600
649
|
retry
|
601
650
|
end
|
@@ -604,18 +653,33 @@ class Resolv
|
|
604
653
|
class Requester # :nodoc:
|
605
654
|
def initialize
|
606
655
|
@senders = {}
|
607
|
-
@
|
656
|
+
@socks = nil
|
608
657
|
end
|
609
658
|
|
610
659
|
def request(sender, tout)
|
611
|
-
|
660
|
+
start = Time.now
|
661
|
+
timelimit = start + tout
|
612
662
|
sender.send
|
613
|
-
while
|
614
|
-
|
615
|
-
|
663
|
+
while true
|
664
|
+
before_select = Time.now
|
665
|
+
timeout = timelimit - before_select
|
666
|
+
if timeout <= 0
|
667
|
+
raise ResolvTimeout
|
668
|
+
end
|
669
|
+
select_result = IO.select(@socks, nil, nil, timeout)
|
670
|
+
if !select_result
|
671
|
+
after_select = Time.now
|
672
|
+
next if after_select < timelimit
|
673
|
+
raise ResolvTimeout
|
674
|
+
end
|
675
|
+
begin
|
676
|
+
reply, from = recv_reply(select_result[0])
|
677
|
+
rescue Errno::ECONNREFUSED, # GNU/Linux, FreeBSD
|
678
|
+
Errno::ECONNRESET # Windows
|
679
|
+
# No name server running on the server?
|
680
|
+
# Don't wait anymore.
|
616
681
|
raise ResolvTimeout
|
617
682
|
end
|
618
|
-
reply, from = recv_reply
|
619
683
|
begin
|
620
684
|
msg = Message.decode(reply)
|
621
685
|
rescue DecodeError
|
@@ -631,9 +695,11 @@ class Resolv
|
|
631
695
|
end
|
632
696
|
|
633
697
|
def close
|
634
|
-
|
635
|
-
@
|
636
|
-
|
698
|
+
socks = @socks
|
699
|
+
@socks = nil
|
700
|
+
if socks
|
701
|
+
socks.each {|sock| sock.close }
|
702
|
+
end
|
637
703
|
end
|
638
704
|
|
639
705
|
class Sender # :nodoc:
|
@@ -645,15 +711,31 @@ class Resolv
|
|
645
711
|
end
|
646
712
|
|
647
713
|
class UnconnectedUDP < Requester # :nodoc:
|
648
|
-
def initialize
|
714
|
+
def initialize(*nameserver_port)
|
649
715
|
super()
|
650
|
-
@
|
651
|
-
@
|
652
|
-
|
716
|
+
@nameserver_port = nameserver_port
|
717
|
+
@socks_hash = {}
|
718
|
+
@socks = []
|
719
|
+
nameserver_port.each {|host, port|
|
720
|
+
if host.index(':')
|
721
|
+
bind_host = "::"
|
722
|
+
af = Socket::AF_INET6
|
723
|
+
else
|
724
|
+
bind_host = "0.0.0.0"
|
725
|
+
af = Socket::AF_INET
|
726
|
+
end
|
727
|
+
next if @socks_hash[bind_host]
|
728
|
+
sock = UDPSocket.new(af)
|
729
|
+
sock.do_not_reverse_lookup = true
|
730
|
+
sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::F_SETFD
|
731
|
+
DNS.bind_random_port(sock, bind_host)
|
732
|
+
@socks << sock
|
733
|
+
@socks_hash[bind_host] = sock
|
734
|
+
}
|
653
735
|
end
|
654
736
|
|
655
|
-
def recv_reply
|
656
|
-
reply, from =
|
737
|
+
def recv_reply(readable_socks)
|
738
|
+
reply, from = readable_socks[0].recvfrom(UDPSize)
|
657
739
|
return reply, [from[3],from[1]]
|
658
740
|
end
|
659
741
|
|
@@ -662,8 +744,9 @@ class Resolv
|
|
662
744
|
id = DNS.allocate_request_id(host, port)
|
663
745
|
request = msg.encode
|
664
746
|
request[0,2] = [id].pack('n')
|
747
|
+
sock = @socks_hash[host.index(':') ? "::" : "0.0.0.0"]
|
665
748
|
return @senders[[service, id]] =
|
666
|
-
Sender.new(request, data,
|
749
|
+
Sender.new(request, data, sock, host, port)
|
667
750
|
end
|
668
751
|
|
669
752
|
def close
|
@@ -692,14 +775,17 @@ class Resolv
|
|
692
775
|
super()
|
693
776
|
@host = host
|
694
777
|
@port = port
|
695
|
-
|
696
|
-
|
697
|
-
@sock
|
698
|
-
|
778
|
+
is_ipv6 = host.index(':')
|
779
|
+
sock = UDPSocket.new(is_ipv6 ? Socket::AF_INET6 : Socket::AF_INET)
|
780
|
+
@socks = [sock]
|
781
|
+
sock.do_not_reverse_lookup = true
|
782
|
+
sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::F_SETFD
|
783
|
+
DNS.bind_random_port(sock, is_ipv6 ? "::" : "0.0.0.0")
|
784
|
+
sock.connect(host, port)
|
699
785
|
end
|
700
786
|
|
701
|
-
def recv_reply
|
702
|
-
reply =
|
787
|
+
def recv_reply(readable_socks)
|
788
|
+
reply = readable_socks[0].recv(UDPSize)
|
703
789
|
return reply, nil
|
704
790
|
end
|
705
791
|
|
@@ -710,7 +796,7 @@ class Resolv
|
|
710
796
|
id = DNS.allocate_request_id(@host, @port)
|
711
797
|
request = msg.encode
|
712
798
|
request[0,2] = [id].pack('n')
|
713
|
-
return @senders[[nil,id]] = Sender.new(request, data, @
|
799
|
+
return @senders[[nil,id]] = Sender.new(request, data, @socks[0])
|
714
800
|
end
|
715
801
|
|
716
802
|
def close
|
@@ -733,14 +819,15 @@ class Resolv
|
|
733
819
|
super()
|
734
820
|
@host = host
|
735
821
|
@port = port
|
736
|
-
|
737
|
-
@
|
822
|
+
sock = TCPSocket.new(@host, @port)
|
823
|
+
@socks = [sock]
|
824
|
+
sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::F_SETFD
|
738
825
|
@senders = {}
|
739
826
|
end
|
740
827
|
|
741
|
-
def recv_reply
|
742
|
-
len =
|
743
|
-
reply = @
|
828
|
+
def recv_reply(readable_socks)
|
829
|
+
len = readable_socks[0].read(2).unpack('n')[0]
|
830
|
+
reply = @socks[0].read(len)
|
744
831
|
return reply, nil
|
745
832
|
end
|
746
833
|
|
@@ -751,7 +838,7 @@ class Resolv
|
|
751
838
|
id = DNS.allocate_request_id(@host, @port)
|
752
839
|
request = msg.encode
|
753
840
|
request[0,2] = [request.length, id].pack('nn')
|
754
|
-
return @senders[[nil,id]] = Sender.new(request, data, @
|
841
|
+
return @senders[[nil,id]] = Sender.new(request, data, @socks[0])
|
755
842
|
end
|
756
843
|
|
757
844
|
class Sender < Requester::Sender # :nodoc:
|
@@ -782,6 +869,20 @@ class Resolv
|
|
782
869
|
@mutex = Mutex.new
|
783
870
|
@config_info = config_info
|
784
871
|
@initialized = nil
|
872
|
+
@timeouts = nil
|
873
|
+
end
|
874
|
+
|
875
|
+
def timeouts=(values)
|
876
|
+
if values
|
877
|
+
values = Array(values)
|
878
|
+
values.each do |t|
|
879
|
+
Numeric === t or raise ArgumentError, "#{t.inspect} is not numeric"
|
880
|
+
t > 0.0 or raise ArgumentError, "timeout=#{t} must be postive"
|
881
|
+
end
|
882
|
+
@timeouts = values
|
883
|
+
else
|
884
|
+
@timeouts = nil
|
885
|
+
end
|
785
886
|
end
|
786
887
|
|
787
888
|
def Config.parse_resolv_conf(filename)
|
@@ -822,7 +923,7 @@ class Resolv
|
|
822
923
|
if File.exist? filename
|
823
924
|
config_hash = Config.parse_resolv_conf(filename)
|
824
925
|
else
|
825
|
-
if /
|
926
|
+
if /mswin|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM
|
826
927
|
require 'win32/resolv'
|
827
928
|
search, nameserver = Win32::Resolv.get_resolv_info
|
828
929
|
config_hash = {}
|
@@ -830,13 +931,13 @@ class Resolv
|
|
830
931
|
config_hash[:search] = [search].flatten if search
|
831
932
|
end
|
832
933
|
end
|
833
|
-
config_hash
|
934
|
+
config_hash || {}
|
834
935
|
end
|
835
936
|
|
836
937
|
def lazy_initialize
|
837
938
|
@mutex.synchronize {
|
838
939
|
unless @initialized
|
839
|
-
@
|
940
|
+
@nameserver_port = []
|
840
941
|
@search = nil
|
841
942
|
@ndots = 1
|
842
943
|
case @config_info
|
@@ -855,11 +956,18 @@ class Resolv
|
|
855
956
|
else
|
856
957
|
raise ArgumentError.new("invalid resolv configuration: #{@config_info.inspect}")
|
857
958
|
end
|
858
|
-
|
959
|
+
if config_hash.include? :nameserver
|
960
|
+
@nameserver_port = config_hash[:nameserver].map {|ns| [ns, Port] }
|
961
|
+
end
|
962
|
+
if config_hash.include? :nameserver_port
|
963
|
+
@nameserver_port = config_hash[:nameserver_port].map {|ns, port| [ns, (port || Port)] }
|
964
|
+
end
|
859
965
|
@search = config_hash[:search] if config_hash.include? :search
|
860
966
|
@ndots = config_hash[:ndots] if config_hash.include? :ndots
|
861
967
|
|
862
|
-
|
968
|
+
if @nameserver_port.empty?
|
969
|
+
@nameserver_port << ['0.0.0.0', Port]
|
970
|
+
end
|
863
971
|
if @search
|
864
972
|
@search = @search.map {|arg| Label.split(arg) }
|
865
973
|
else
|
@@ -871,9 +979,14 @@ class Resolv
|
|
871
979
|
end
|
872
980
|
end
|
873
981
|
|
874
|
-
if !@
|
875
|
-
|
876
|
-
|
982
|
+
if !@nameserver_port.kind_of?(Array) ||
|
983
|
+
@nameserver_port.any? {|ns_port|
|
984
|
+
!(Array === ns_port) ||
|
985
|
+
ns_port.length != 2
|
986
|
+
!(String === ns_port[0]) ||
|
987
|
+
!(Integer === ns_port[1])
|
988
|
+
}
|
989
|
+
raise ArgumentError.new("invalid nameserver config: #{@nameserver_port.inspect}")
|
877
990
|
end
|
878
991
|
|
879
992
|
if !@search.kind_of?(Array) ||
|
@@ -893,13 +1006,17 @@ class Resolv
|
|
893
1006
|
|
894
1007
|
def single?
|
895
1008
|
lazy_initialize
|
896
|
-
if @
|
897
|
-
return @
|
1009
|
+
if @nameserver_port.length == 1
|
1010
|
+
return @nameserver_port[0]
|
898
1011
|
else
|
899
1012
|
return nil
|
900
1013
|
end
|
901
1014
|
end
|
902
1015
|
|
1016
|
+
def nameserver_port
|
1017
|
+
@nameserver_port
|
1018
|
+
end
|
1019
|
+
|
903
1020
|
def generate_candidates(name)
|
904
1021
|
candidates = nil
|
905
1022
|
name = Name.create(name)
|
@@ -920,7 +1037,7 @@ class Resolv
|
|
920
1037
|
|
921
1038
|
def generate_timeouts
|
922
1039
|
ts = [InitialTimeout]
|
923
|
-
ts << ts[-1] * 2 / @
|
1040
|
+
ts << ts[-1] * 2 / @nameserver_port.length
|
924
1041
|
ts << ts[-1] * 2
|
925
1042
|
ts << ts[-1] * 2
|
926
1043
|
return ts
|
@@ -928,14 +1045,14 @@ class Resolv
|
|
928
1045
|
|
929
1046
|
def resolv(name)
|
930
1047
|
candidates = generate_candidates(name)
|
931
|
-
timeouts = generate_timeouts
|
1048
|
+
timeouts = @timeouts || generate_timeouts
|
932
1049
|
begin
|
933
1050
|
candidates.each {|candidate|
|
934
1051
|
begin
|
935
1052
|
timeouts.each {|tout|
|
936
|
-
@
|
1053
|
+
@nameserver_port.each {|nameserver, port|
|
937
1054
|
begin
|
938
|
-
yield candidate, tout, nameserver
|
1055
|
+
yield candidate, tout, nameserver, port
|
939
1056
|
rescue ResolvTimeout
|
940
1057
|
end
|
941
1058
|
}
|
@@ -1042,7 +1159,7 @@ class Resolv
|
|
1042
1159
|
# A representation of a DNS name.
|
1043
1160
|
|
1044
1161
|
class Name
|
1045
|
-
|
1162
|
+
|
1046
1163
|
##
|
1047
1164
|
# Creates a new DNS name from +arg+. +arg+ can be:
|
1048
1165
|
#
|
@@ -1353,6 +1470,10 @@ class Resolv
|
|
1353
1470
|
yield self
|
1354
1471
|
end
|
1355
1472
|
|
1473
|
+
def inspect
|
1474
|
+
"\#<#{self.class}: #{@data[0, @index].inspect} #{@data[@index..-1].inspect}>"
|
1475
|
+
end
|
1476
|
+
|
1356
1477
|
def get_length16
|
1357
1478
|
len, = self.get_unpack('n')
|
1358
1479
|
save_limit = @limit
|
@@ -1376,6 +1497,7 @@ class Resolv
|
|
1376
1497
|
def get_unpack(template)
|
1377
1498
|
len = 0
|
1378
1499
|
template.each_byte {|byte|
|
1500
|
+
byte = "%c" % byte
|
1379
1501
|
case byte
|
1380
1502
|
when ?c, ?C
|
1381
1503
|
len += 1
|
@@ -1394,7 +1516,7 @@ class Resolv
|
|
1394
1516
|
end
|
1395
1517
|
|
1396
1518
|
def get_string
|
1397
|
-
len = @data[@index]
|
1519
|
+
len = @data[@index].ord
|
1398
1520
|
raise DecodeError.new("limit exceeded") if @limit < @index + 1 + len
|
1399
1521
|
d = @data[@index + 1, len]
|
1400
1522
|
@index += 1 + len
|
@@ -1417,7 +1539,7 @@ class Resolv
|
|
1417
1539
|
limit = @index if !limit || @index < limit
|
1418
1540
|
d = []
|
1419
1541
|
while true
|
1420
|
-
case @data[@index]
|
1542
|
+
case @data[@index].ord
|
1421
1543
|
when 0
|
1422
1544
|
@index += 1
|
1423
1545
|
return d
|
@@ -1452,7 +1574,9 @@ class Resolv
|
|
1452
1574
|
name = self.get_name
|
1453
1575
|
type, klass, ttl = self.get_unpack('nnN')
|
1454
1576
|
typeclass = Resource.get_class(type, klass)
|
1455
|
-
|
1577
|
+
res = self.get_length16 { typeclass.decode_rdata self }
|
1578
|
+
res.instance_variable_set :@ttl, ttl
|
1579
|
+
return name, ttl, res
|
1456
1580
|
end
|
1457
1581
|
end
|
1458
1582
|
end
|
@@ -1462,11 +1586,11 @@ class Resolv
|
|
1462
1586
|
|
1463
1587
|
class Query
|
1464
1588
|
def encode_rdata(msg) # :nodoc:
|
1465
|
-
raise EncodeError.new("#{self.class} is query.")
|
1589
|
+
raise EncodeError.new("#{self.class} is query.")
|
1466
1590
|
end
|
1467
1591
|
|
1468
1592
|
def self.decode_rdata(msg) # :nodoc:
|
1469
|
-
raise DecodeError.new("#{self.class} is query.")
|
1593
|
+
raise DecodeError.new("#{self.class} is query.")
|
1470
1594
|
end
|
1471
1595
|
end
|
1472
1596
|
|
@@ -1491,10 +1615,16 @@ class Resolv
|
|
1491
1615
|
end
|
1492
1616
|
|
1493
1617
|
def ==(other) # :nodoc:
|
1494
|
-
return self.class == other.class
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1618
|
+
return false unless self.class == other.class
|
1619
|
+
s_ivars = self.instance_variables
|
1620
|
+
s_ivars.sort!
|
1621
|
+
s_ivars.delete "@ttl"
|
1622
|
+
o_ivars = other.instance_variables
|
1623
|
+
o_ivars.sort!
|
1624
|
+
o_ivars.delete "@ttl"
|
1625
|
+
return s_ivars == o_ivars &&
|
1626
|
+
s_ivars.collect {|name| self.instance_variable_get name} ==
|
1627
|
+
o_ivars.collect {|name| other.instance_variable_get name}
|
1498
1628
|
end
|
1499
1629
|
|
1500
1630
|
def eql?(other) # :nodoc:
|
@@ -1503,8 +1633,10 @@ class Resolv
|
|
1503
1633
|
|
1504
1634
|
def hash # :nodoc:
|
1505
1635
|
h = 0
|
1506
|
-
self.instance_variables
|
1507
|
-
|
1636
|
+
vars = self.instance_variables
|
1637
|
+
vars.delete "@ttl"
|
1638
|
+
vars.each {|name|
|
1639
|
+
h ^= self.instance_variable_get(name).hash
|
1508
1640
|
}
|
1509
1641
|
return h
|
1510
1642
|
end
|
@@ -1933,7 +2065,7 @@ class Resolv
|
|
1933
2065
|
def initialize(address)
|
1934
2066
|
@address = IPv6.create(address)
|
1935
2067
|
end
|
1936
|
-
|
2068
|
+
|
1937
2069
|
##
|
1938
2070
|
# The Resolv::IPv6 address for this AAAA.
|
1939
2071
|
|
@@ -1950,7 +2082,7 @@ class Resolv
|
|
1950
2082
|
|
1951
2083
|
##
|
1952
2084
|
# SRV resource record defined in RFC 2782
|
1953
|
-
#
|
2085
|
+
#
|
1954
2086
|
# These records identify the hostname and port that a service is
|
1955
2087
|
# available at.
|
1956
2088
|
|
@@ -2033,7 +2165,11 @@ class Resolv
|
|
2033
2165
|
##
|
2034
2166
|
# Regular expression IPv4 addresses must match.
|
2035
2167
|
|
2036
|
-
|
2168
|
+
Regex256 = /0
|
2169
|
+
|1(?:[0-9][0-9]?)?
|
2170
|
+
|2(?:[0-4][0-9]?|5[0-5]?|[6-9])?
|
2171
|
+
|[3-9][0-9]?/x
|
2172
|
+
Regex = /\A(#{Regex256})\.(#{Regex256})\.(#{Regex256})\.(#{Regex256})\z/
|
2037
2173
|
|
2038
2174
|
def self.create(arg)
|
2039
2175
|
case arg
|
@@ -2054,8 +2190,11 @@ class Resolv
|
|
2054
2190
|
end
|
2055
2191
|
|
2056
2192
|
def initialize(address) # :nodoc:
|
2057
|
-
unless address.kind_of?(String)
|
2058
|
-
raise ArgumentError
|
2193
|
+
unless address.kind_of?(String)
|
2194
|
+
raise ArgumentError, 'IPv4 address must be a string'
|
2195
|
+
end
|
2196
|
+
unless address.length == 4
|
2197
|
+
raise ArgumentError, "IPv4 address expects 4 bytes but #{address.length} bytes"
|
2059
2198
|
end
|
2060
2199
|
@address = address
|
2061
2200
|
end
|
data/rubysl-resolv.gemspec
CHANGED
@@ -16,6 +16,8 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
17
17
|
spec.require_paths = ["lib"]
|
18
18
|
|
19
|
+
spec.required_ruby_version = "~> 2.0"
|
20
|
+
|
19
21
|
spec.add_development_dependency "bundler", "~> 1.3"
|
20
22
|
spec.add_development_dependency "rake", "~> 10.0"
|
21
23
|
spec.add_development_dependency "mspec", "~> 1.5"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubysl-resolv
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Shirai
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-08-
|
11
|
+
date: 2013-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -84,9 +84,9 @@ require_paths:
|
|
84
84
|
- lib
|
85
85
|
required_ruby_version: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- -
|
87
|
+
- - ~>
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
89
|
+
version: '2.0'
|
90
90
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
91
|
requirements:
|
92
92
|
- - '>='
|