rubysl-resolv 1.0.0 → 2.0.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.
- 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
|
- - '>='
|