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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0c986938060ce85ff5f500d024c015bb3fb76aac
4
- data.tar.gz: 3adf0ca646fbbc18fff5f059f46fb27e6497de96
3
+ metadata.gz: 1f910ca03a2f2e7fc5ba345a15f7e35786dd07b5
4
+ data.tar.gz: 95ab27ed98857c6cc1055b92a6c47d008a9d3ec0
5
5
  SHA512:
6
- metadata.gz: 2f96573ce73b14781b85f0a058ec9a0343b3f16edc9a6216821bd5d7a9a25a6be63815c2b05d589734c2b05cd24e1896114bd26ca84810752a6221d69eba9091
7
- data.tar.gz: ad2990b751425197cc1be5f2cacd81f0940ec9ca397ac52c303796341e427e9f91cac3316f7c3bc6788a51763e8e62799200b071e182c0b748be490e2e5ed07f
6
+ metadata.gz: 8be3e9e9377c375c5bd7a88f3d6c48a7536af1fe6133e939b738f946af57525b6f70fc48993a015c1a5db04e440890bfbe78ce5dc22c9061c058b628ae18a24a
7
+ data.tar.gz: ec78bb44fc70c12de9caee8c9cfa7c9d8542f83b7eda0e43f7bd9dfa86ac38adafd6a7fba69c281993ce82673669534827584f3df96d0763ba28b69678ab387b
data/.travis.yml CHANGED
@@ -3,5 +3,5 @@ env:
3
3
  - RUBYLIB=lib
4
4
  script: bundle exec mspec
5
5
  rvm:
6
- - 1.8.7
7
- - rbx-nightly-18mode
6
+ - 1.9.3
7
+ - rbx-nightly-19mode
@@ -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. The ruby
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 # Resolv.
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
- # DNS::Hosts is a hostname resolver that uses the system hosts file.
165
+ # Resolv::Hosts is a hostname resolver that uses the system hosts file.
166
166
 
167
167
  class Hosts
168
- if /mswin32|mingw|bccwin/ =~ RUBY_PLATFORM
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 DNS::Hosts, using +filename+ for its data source.
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
- each_resource(name, Resource::IN::AAAA) {|resource| yield resource.address}
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 = make_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
- extract_resources(reply, reply_name, typeclass, &proc)
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 make_requester # :nodoc:
506
- if nameserver = @config.single?
507
- Requester::ConnectedUDP.new(nameserver)
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("", port)
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
- @sock = nil
656
+ @socks = nil
608
657
  end
609
658
 
610
659
  def request(sender, tout)
611
- timelimit = Time.now + tout
660
+ start = Time.now
661
+ timelimit = start + tout
612
662
  sender.send
613
- while (now = Time.now) < timelimit
614
- timeout = timelimit - now
615
- if !IO.select([@sock], nil, nil, timeout)
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
- sock = @sock
635
- @sock = nil
636
- sock.close if sock
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
- @sock = UDPSocket.new
651
- @sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::F_SETFD
652
- DNS.bind_random_port(@sock)
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 = @sock.recvfrom(UDPSize)
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, @sock, host, port)
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
- @sock = UDPSocket.new(host.index(':') ? Socket::AF_INET6 : Socket::AF_INET)
696
- DNS.bind_random_port(@sock)
697
- @sock.connect(host, port)
698
- @sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::F_SETFD
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 = @sock.recv(UDPSize)
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, @sock)
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
- @sock = TCPSocket.new(@host, @port)
737
- @sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::F_SETFD
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 = @sock.read(2).unpack('n')[0]
743
- reply = @sock.read(len)
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, @sock)
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 /mswin32|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM
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
- @nameserver = []
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
- @nameserver = config_hash[:nameserver] if config_hash.include? :nameserver
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
- @nameserver = ['0.0.0.0'] if @nameserver.empty?
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 !@nameserver.kind_of?(Array) ||
875
- !@nameserver.all? {|ns| String === ns }
876
- raise ArgumentError.new("invalid nameserver config: #{@nameserver.inspect}")
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 @nameserver.length == 1
897
- return @nameserver[0]
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 / @nameserver.length
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
- @nameserver.each {|nameserver|
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
- return name, ttl, self.get_length16 {typeclass.decode_rdata(self)}
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
- self.instance_variables == other.instance_variables &&
1496
- self.instance_variables.collect {|name| self.instance_eval name} ==
1497
- other.instance_variables.collect {|name| other.instance_eval name}
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.each {|name|
1507
- h ^= self.instance_eval("#{name}.hash")
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
- Regex = /\A(\d+)\.(\d+)\.(\d+)\.(\d+)\z/
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) && address.length == 4
2058
- raise ArgumentError.new('IPv4 address must be 4 bytes')
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
@@ -1,5 +1,5 @@
1
1
  module RubySL
2
2
  module Resolv
3
- VERSION = "1.0.0"
3
+ VERSION = "2.0.0"
4
4
  end
5
5
  end
@@ -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: 1.0.0
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-27 00:00:00.000000000 Z
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
  - - '>='