resolv 0.7.2 → 0.8.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 38de4f14ddb183350d35593550caa34f9694644fb9d2fc79bd275d92d17ee338
4
- data.tar.gz: 00d4977e89fb7cd8ec4a772c481d63e49fd4441b90c7ec5be42a79294ab3da19
3
+ metadata.gz: 6d41ba56feaadbca7be99976683ea0272a2e9e87234b24d1c96a841b48c8fab8
4
+ data.tar.gz: 07e1a1cacdf7385307d1be4f276db467703b642a1227a3f264b62be73846d23e
5
5
  SHA512:
6
- metadata.gz: 175ca63aa1f82ebabc499bbcd8eedf17c33ff220c439187a1c5778ca2e74926d15651a720c0bb5f615a698747252d8f9cd82c5db5fb25d28a4d1e63cb696acf1
7
- data.tar.gz: 8789721febc5b7a96a8bcfdc51dd503afefa648a6d7057e6951b77d1fb514c613e35524ef9680e4ef6d4ad49e310a0fa2d1e92a972eae0e8f23969519c7db1d9
6
+ metadata.gz: 9a0a5b43154d5f3653de9705843b8c37205913df18dba719023b729711b32527c08544a340e73a2adf3835f5c0a0f1f6876f68ac2005c4305647a033177ac1fc
7
+ data.tar.gz: 3ee24cfd9b10cef67fd53e62a1cbfb9e63dd4709ec6248c9b65ffd020ec70b8eb71aa8acd39704cb068be4a6ce301709bff0cca2ff3b783f3b786965bb7422e4
@@ -0,0 +1,3 @@
1
+ # AUTOGENERATED DEPENDENCIES START
2
+ resolv.o: resolv.c
3
+ # AUTOGENERATED DEPENDENCIES END
@@ -6,8 +6,9 @@
6
6
 
7
7
  require 'win32/resolv.so'
8
8
 
9
+ # Generic namespace for Windows platform-specific features.
9
10
  module Win32
10
- module Resolv
11
+ module Resolv # :nodoc:
11
12
  # Error at Win32 API
12
13
  class Error < StandardError
13
14
  # +code+ Win32 Error code
@@ -53,6 +53,7 @@ wchar_to_utf8(const WCHAR *w, int n)
53
53
  return str;
54
54
  }
55
55
 
56
+ /* :nodoc: */
56
57
  static VALUE
57
58
  get_dns_server_list(VALUE self)
58
59
  {
@@ -81,7 +82,6 @@ get_dns_server_list(VALUE self)
81
82
  return nameservers;
82
83
  }
83
84
 
84
-
85
85
  static const WCHAR TCPIP_Params[] = L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
86
86
 
87
87
  static void
@@ -116,6 +116,7 @@ reg_open_key(VALUE klass, HKEY hkey, const WCHAR *wname)
116
116
  return rb_ensure(rb_yield, k, hkey_close, k);
117
117
  }
118
118
 
119
+ /* :nodoc: */
119
120
  static VALUE
120
121
  tcpip_params_open(VALUE klass)
121
122
  {
@@ -246,11 +247,11 @@ InitVM_resolv(void)
246
247
 
247
248
  reg_key_class = regkey;
248
249
  rb_undef_alloc_func(regkey);
249
- rb_define_private_method(singl, "get_dns_server_list", get_dns_server_list, 0);
250
- rb_define_private_method(singl, "tcpip_params", tcpip_params_open, 0);
251
- rb_define_method(regkey, "open", reg_open, 1);
252
- rb_define_method(regkey, "each_key", reg_each_key, 0);
253
- rb_define_method(regkey, "value", reg_value, 1);
250
+ rb_define_private_method(singl, "get_dns_server_list", RUBY_METHOD_FUNC(get_dns_server_list), 0);
251
+ rb_define_private_method(singl, "tcpip_params", RUBY_METHOD_FUNC(tcpip_params_open), 0);
252
+ rb_define_method(regkey, "open", RUBY_METHOD_FUNC(reg_open), 1);
253
+ rb_define_method(regkey, "each_key", RUBY_METHOD_FUNC(reg_each_key), 0);
254
+ rb_define_method(regkey, "value", RUBY_METHOD_FUNC(reg_value), 1);
254
255
  }
255
256
 
256
257
  void
data/lib/resolv.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'socket'
4
4
  require 'timeout'
5
- require 'io/wait'
5
+ require 'io/wait' if RUBY_VERSION < '3.2'
6
6
  require 'securerandom'
7
7
  require 'rbconfig'
8
8
 
@@ -35,7 +35,7 @@ require 'rbconfig'
35
35
  class Resolv
36
36
 
37
37
  # The version string
38
- VERSION = "0.7.2"
38
+ VERSION = "0.8.1"
39
39
 
40
40
  ##
41
41
  # Looks up the first IP address for +name+.
@@ -179,7 +179,7 @@ class Resolv
179
179
  # Resolv::Hosts is a hostname resolver that uses the system hosts file.
180
180
 
181
181
  class Hosts
182
- if /mswin|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM || ::RbConfig::CONFIG['host_os'] =~ /mswin/
182
+ if /mswin|cygwin|mingw/ =~ RUBY_PLATFORM || ::RbConfig::CONFIG['host_os'] =~ /mswin/
183
183
  begin
184
184
  require 'win32/resolv' unless defined?(Win32::Resolv)
185
185
  hosts = Win32::Resolv.get_hosts_path || IO::NULL
@@ -562,7 +562,19 @@ class Resolv
562
562
  next if !sender
563
563
  senders[[candidate, requester, nameserver, port]] = sender
564
564
  end
565
- reply, reply_name = requester.request(sender, tout)
565
+ begin
566
+ reply, reply_name = requester.request(sender, tout)
567
+ rescue ResolvTimeout
568
+ # Giving up part way through a frame loses stream sync, and a peer
569
+ # seen going away leaves the socket dead. Either way the requester
570
+ # says so, and the next attempt has to open a fresh connection. A
571
+ # timeout with the stream still on a frame boundary keeps it.
572
+ unless requester.reusable?
573
+ requesters.delete([nameserver, port])
574
+ requester.close
575
+ end
576
+ raise
577
+ end
566
578
  case reply.rcode
567
579
  when RCode::NoError
568
580
  if reply.tc == 1 and not Requester::TCP === requester
@@ -698,13 +710,27 @@ class Resolv
698
710
  @socks = nil
699
711
  end
700
712
 
713
+ # Whether another request may be sent over the same transport. Only a
714
+ # stream transport can end up in a state that rules this out.
715
+ def reusable?
716
+ true
717
+ end
718
+
719
+ # A request could not be written. Only a stream transport can be left
720
+ # unusable by that; see #reusable?.
721
+ def send_failed
722
+ end
723
+
701
724
  def request(sender, tout)
702
725
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
703
726
  timelimit = start + tout
704
727
  begin
705
728
  sender.send
706
729
  rescue Errno::EHOSTUNREACH, # multi-homed IPv6 may generate this
707
- Errno::ENETUNREACH
730
+ Errno::ENETUNREACH,
731
+ Errno::EPIPE, # a peer that went away between requests
732
+ Errno::ECONNRESET # the same, as Windows reports it
733
+ send_failed
708
734
  raise ResolvTimeout
709
735
  end
710
736
  while true
@@ -724,7 +750,7 @@ class Resolv
724
750
  raise ResolvTimeout
725
751
  end
726
752
  begin
727
- reply, from = recv_reply(select_result[0])
753
+ reply, from = recv_reply(select_result[0], timelimit)
728
754
  rescue Errno::ECONNREFUSED, # GNU/Linux, FreeBSD
729
755
  Errno::ECONNRESET, # Windows
730
756
  EOFError
@@ -801,7 +827,7 @@ class Resolv
801
827
  self
802
828
  end
803
829
 
804
- def recv_reply(readable_socks)
830
+ def recv_reply(readable_socks, timelimit = nil)
805
831
  lazy_initialize
806
832
  reply, from = readable_socks[0].recvfrom(UDPSize)
807
833
  return reply, [from[3],from[1]]
@@ -860,20 +886,37 @@ class Resolv
860
886
  @mutex.synchronize {
861
887
  next if @initialized
862
888
  @initialized = true
863
- is_ipv6 = @host.index(':')
864
- sock = UDPSocket.new(is_ipv6 ? Socket::AF_INET6 : Socket::AF_INET)
865
- @socks = [sock]
866
- sock.do_not_reverse_lookup = true
867
- DNS.bind_random_port(sock, is_ipv6 ? "::" : "0.0.0.0")
868
- sock.connect(@host, @port)
889
+ connect_socket
869
890
  }
870
891
  self
871
892
  end
872
893
 
873
- def recv_reply(readable_socks)
894
+ # The socket to talk to the nameserver over, opening one if there is
895
+ # none yet. #recv_reply may have replaced it since a sender was
896
+ # created, so senders ask for it per request instead of holding on to
897
+ # one.
898
+ def sock
899
+ lazy_initialize
900
+ @socks[0]
901
+ end
902
+
903
+ def recv_reply(readable_socks, timelimit = nil)
874
904
  lazy_initialize
875
905
  reply = readable_socks[0].recv(UDPSize)
876
906
  return reply, nil
907
+ rescue Errno::ECONNREFUSED, Errno::ECONNRESET
908
+ # The kernel reports these from an ICMP message, and a second one for
909
+ # the same pair of endpoints is not always passed on: macOS 26.1 and
910
+ # later deliver every other one. A retry over this socket would then
911
+ # wait out its whole timeout rather than fail at once, so start over
912
+ # from a new source port.
913
+ @mutex.synchronize {
914
+ if @initialized
915
+ @socks&.each(&:close)
916
+ connect_socket
917
+ end
918
+ }
919
+ raise
877
920
  end
878
921
 
879
922
  def sender(msg, data, host=@host, port=@port)
@@ -884,7 +927,7 @@ class Resolv
884
927
  id = DNS.allocate_request_id(@host, @port)
885
928
  request = msg.encode
886
929
  request[0,2] = [id].pack('n')
887
- return @senders[[nil,id]] = Sender.new(request, data, @socks[0])
930
+ return @senders[[nil,id]] = Sender.new(request, data, self)
888
931
  end
889
932
 
890
933
  def close
@@ -899,10 +942,23 @@ class Resolv
899
942
  end
900
943
  end
901
944
 
945
+ private def connect_socket
946
+ is_ipv6 = @host.index(':')
947
+ sock = UDPSocket.new(is_ipv6 ? Socket::AF_INET6 : Socket::AF_INET)
948
+ @socks = [sock]
949
+ sock.do_not_reverse_lookup = true
950
+ DNS.bind_random_port(sock, is_ipv6 ? "::" : "0.0.0.0")
951
+ sock.connect(@host, @port)
952
+ end
953
+
902
954
  class Sender < Requester::Sender # :nodoc:
955
+ def initialize(msg, data, requester)
956
+ super(msg, data, nil)
957
+ @requester = requester
958
+ end
959
+
903
960
  def send
904
- raise "@sock is nil." if @sock.nil?
905
- @sock.send(@msg, 0)
961
+ @requester.sock.send(@msg, 0)
906
962
  end
907
963
  attr_reader :data
908
964
  end
@@ -933,15 +989,32 @@ class Resolv
933
989
  sock = TCPSocket.new(@host, @port)
934
990
  @socks = [sock]
935
991
  @senders = {}
992
+ @reusable = true
993
+ end
994
+
995
+ def reusable?
996
+ @reusable
997
+ end
998
+
999
+ def send_failed
1000
+ @reusable = false
936
1001
  end
937
1002
 
938
- def recv_reply(readable_socks)
939
- len_data = readable_socks[0].read(2)
1003
+ def recv_reply(readable_socks, timelimit = nil)
1004
+ sock = readable_socks[0]
1005
+ len_data = read_exactly(sock, 2, timelimit)
940
1006
  raise EOFError if len_data.nil? || len_data.bytesize != 2
941
1007
  len = len_data.unpack('n')[0]
942
- reply = @socks[0].read(len)
1008
+ reply = read_exactly(sock, len, timelimit)
943
1009
  raise EOFError if reply.nil? || reply.bytesize != len
1010
+ @reusable = true
944
1011
  return reply, nil
1012
+ rescue EOFError, SystemCallError
1013
+ # Whatever the kernel reported, this socket cannot be trusted for
1014
+ # another frame. In practice that is the peer closing or resetting;
1015
+ # the rest is rare enough that erring towards reconnecting is right.
1016
+ @reusable = false
1017
+ raise
945
1018
  end
946
1019
 
947
1020
  def sender(msg, data, host=@host, port=@port)
@@ -963,11 +1036,40 @@ class Resolv
963
1036
  end
964
1037
 
965
1038
  def close
1039
+ @reusable = false
966
1040
  super
967
1041
  @senders.each_key {|from,id|
968
1042
  DNS.free_request_id(@host, @port, id)
969
1043
  }
970
1044
  end
1045
+
1046
+ private
1047
+
1048
+ # Read +len+ bytes, giving up with ResolvTimeout once +timelimit+ (a
1049
+ # CLOCK_MONOTONIC value) has passed. A shorter result means the peer
1050
+ # closed the connection, which the caller turns into an EOFError.
1051
+ # Consuming any byte marks the requester unusable until the whole frame
1052
+ # has been read, since giving up in between loses frame sync. Without a
1053
+ # +timelimit+ the read blocks instead and keeps no such mark; that is
1054
+ # only for a caller still using the one argument form of #recv_reply.
1055
+ def read_exactly(sock, len, timelimit)
1056
+ return sock.read(len) unless timelimit
1057
+ buf = String.new
1058
+ while buf.bytesize < len
1059
+ case chunk = sock.read_nonblock(len - buf.bytesize, exception: false)
1060
+ when :wait_readable
1061
+ remaining = timelimit - Process.clock_gettime(Process::CLOCK_MONOTONIC)
1062
+ raise ResolvTimeout if remaining <= 0
1063
+ sock.wait_readable(remaining) or raise ResolvTimeout
1064
+ when nil
1065
+ break
1066
+ else
1067
+ @reusable = false
1068
+ buf << chunk
1069
+ end
1070
+ end
1071
+ buf
1072
+ end
971
1073
  end
972
1074
 
973
1075
  ##
@@ -1030,14 +1132,15 @@ class Resolv
1030
1132
  end
1031
1133
 
1032
1134
  def Config.default_config_hash(filename="/etc/resolv.conf")
1033
- if File.exist? filename
1034
- Config.parse_resolv_conf(filename)
1035
- elsif defined?(Win32::Resolv)
1135
+ # Native Windows resolves the path against the current drive, while Cygwin's own resolver honors it.
1136
+ if defined?(Win32::Resolv) and !(/cygwin/ =~ RUBY_PLATFORM and File.exist?(filename))
1036
1137
  search, nameserver = Win32::Resolv.get_resolv_info
1037
1138
  config_hash = {}
1038
1139
  config_hash[:nameserver] = nameserver if nameserver
1039
1140
  config_hash[:search] = [search].flatten if search
1040
1141
  config_hash
1142
+ elsif File.exist? filename
1143
+ Config.parse_resolv_conf(filename)
1041
1144
  else
1042
1145
  {}
1043
1146
  end
@@ -1089,7 +1192,7 @@ class Resolv
1089
1192
  if /\./ =~ hostname
1090
1193
  @search = [Label.split($')]
1091
1194
  else
1092
- @search = [[]]
1195
+ @search = []
1093
1196
  end
1094
1197
  end
1095
1198
 
@@ -1136,23 +1239,18 @@ class Resolv
1136
1239
  end
1137
1240
 
1138
1241
  def generate_candidates(name)
1139
- candidates = nil
1140
1242
  name = Name.create(name)
1141
1243
  if name.absolute?
1142
- candidates = [name]
1244
+ [name]
1143
1245
  else
1246
+ abs = Name.new(name.to_a)
1247
+ search = @search.map {|domain| Name.new(name.to_a + domain)}
1144
1248
  if @ndots <= name.length - 1
1145
- candidates = [Name.new(name.to_a)]
1249
+ [abs, *search].uniq
1146
1250
  else
1147
- candidates = []
1148
- end
1149
- candidates.concat(@search.map {|domain| Name.new(name.to_a + domain)})
1150
- fname = Name.create("#{name}.")
1151
- if !candidates.include?(fname)
1152
- candidates << fname
1251
+ [*search, abs].uniq
1153
1252
  end
1154
1253
  end
1155
- return candidates
1156
1254
  end
1157
1255
 
1158
1256
  InitialTimeout = 5
@@ -1747,6 +1845,9 @@ class Resolv
1747
1845
  # size counts the encoded form, so it starts at 1 for the root
1748
1846
  # label's terminating zero octet. [RFC 1035 3.1]
1749
1847
  size = 1
1848
+ # A pointer chain decodes to few or no labels, so the 255-octet cap
1849
+ # never bounds its work; cap the pointers followed per name too.
1850
+ pointers = 0
1750
1851
  while true
1751
1852
  raise DecodeError.new("limit exceeded") if @limit <= @index
1752
1853
  case @data.getbyte(@index)
@@ -1757,6 +1858,8 @@ class Resolv
1757
1858
  end
1758
1859
  return d
1759
1860
  when 192..255
1861
+ pointers += 1
1862
+ raise DecodeError.new("too many compression pointers") if pointers > 128
1760
1863
  idx = self.get_unpack('n')[0] & 0x3fff
1761
1864
  if prev_index <= idx
1762
1865
  raise DecodeError.new("non-backward name pointer")
@@ -3347,7 +3450,7 @@ class Resolv
3347
3450
 
3348
3451
  # Regular expression LOC size must match.
3349
3452
 
3350
- Regex = /^(\d+\.*\d*)[m]$/
3453
+ Regex = /\A0*(\d{1,8}(?:\.\d+)?)m\z/
3351
3454
 
3352
3455
  ##
3353
3456
  # Creates a new LOC::Size from +arg+ which may be:
@@ -3360,13 +3463,14 @@ class Resolv
3360
3463
  when Size
3361
3464
  return arg
3362
3465
  when String
3363
- scalar = ''
3364
- if Regex =~ arg
3365
- scalar = [(($1.to_f*(1e2)).to_i.to_s[0].to_i*(2**4)+(($1.to_f*(1e2)).to_i.to_s.length-1))].pack("C")
3366
- else
3466
+ unless Regex =~ arg
3367
3467
  raise ArgumentError.new("not a properly formed Size string: " + arg)
3368
3468
  end
3369
- return Size.new(scalar)
3469
+ unless (0.0...1e8) === (scalar = $1.to_f)
3470
+ raise ArgumentError.new("out of range as Size: #{arg}")
3471
+ end
3472
+ str = (scalar * 100).to_i.to_s
3473
+ return new([(str[0].to_i << 4) + (str.bytesize-1)].pack("C"))
3370
3474
  else
3371
3475
  raise ArgumentError.new("cannot interpret as Size: #{arg.inspect}")
3372
3476
  end
@@ -3383,8 +3487,8 @@ class Resolv
3383
3487
  attr_reader :scalar
3384
3488
 
3385
3489
  def to_s # :nodoc:
3386
- s = @scalar.unpack("H2").join.to_s
3387
- return ((s[0].to_i)*(10**(s[1].to_i-2))).to_s << "m"
3490
+ s, = @scalar.unpack("C")
3491
+ return "#{(s >> 4) * (10.0 ** ((s & 0xf) - 2))}m"
3388
3492
  end
3389
3493
 
3390
3494
  def inspect # :nodoc:
@@ -3412,7 +3516,10 @@ class Resolv
3412
3516
 
3413
3517
  # Regular expression LOC Coord must match.
3414
3518
 
3415
- Regex = /^(\d+)\s(\d+)\s(\d+\.\d+)\s([NESW])$/
3519
+ Regex = /\A0*(\d{1,3})\s([0-5]?\d)\s([0-5]?\d(?:\.\d+)?)\s([NESW])\z/
3520
+
3521
+ # Bias for the equator/prime meridian, in thousandths of a second of arc.
3522
+ Bias = 1 << 31
3416
3523
 
3417
3524
  ##
3418
3525
  # Creates a new LOC::Coord from +arg+ which may be:
@@ -3425,17 +3532,19 @@ class Resolv
3425
3532
  when Coord
3426
3533
  return arg
3427
3534
  when String
3428
- coordinates = ''
3429
- if Regex =~ arg && $1.to_f < 180
3430
- m = $~
3431
- hemi = (m[4][/[NE]/]) || (m[4][/[SW]/]) ? 1 : -1
3432
- coordinates = [ ((m[1].to_i*(36e5)) + (m[2].to_i*(6e4)) +
3433
- (m[3].to_f*(1e3))) * hemi+(2**31) ].pack("N")
3434
- orientation = m[4][/[NS]/] ? 'lat' : 'lon'
3435
- else
3535
+ unless m = Regex.match(arg)
3436
3536
  raise ArgumentError.new("not a properly formed Coord string: " + arg)
3437
3537
  end
3438
- return Coord.new(coordinates,orientation)
3538
+
3539
+ arc = (m[1].to_i * 3_600_000) + (m[2].to_i * 60_000) + (m[3].to_f * 1_000).to_i
3540
+ dir = m[4]
3541
+ lat = dir[/[NS]/]
3542
+ unless arc <= (lat ? 324_000_000 : 648_000_000) # (lat ? 90 : 180) * 3_600_000
3543
+ raise ArgumentError.new("out of range as Coord: #{arg}")
3544
+ end
3545
+
3546
+ hemi = dir[/[NE]/] ? 1 : -1
3547
+ return new([arc * hemi + Bias].pack("N"), lat ? "lat" : "lon")
3439
3548
  else
3440
3549
  raise ArgumentError.new("cannot interpret as Coord: #{arg.inspect}")
3441
3550
  end
@@ -3443,10 +3552,10 @@ class Resolv
3443
3552
 
3444
3553
  # Internal use; use self.create.
3445
3554
  def initialize(coordinates,orientation)
3446
- unless coordinates.kind_of?(String)
3555
+ unless coordinates.kind_of?(String) and coordinates.bytesize == 4
3447
3556
  raise ArgumentError.new("Coord must be a 32bit unsigned integer in hex format: #{coordinates.inspect}")
3448
3557
  end
3449
- unless orientation.kind_of?(String) && orientation[/^lon$|^lat$/]
3558
+ unless orientation == "lon" || orientation == "lat"
3450
3559
  raise ArgumentError.new('Coord expects orientation to be a String argument of "lat" or "lon"')
3451
3560
  end
3452
3561
  @coordinates = coordinates
@@ -3463,22 +3572,17 @@ class Resolv
3463
3572
  attr_reader :orientation
3464
3573
 
3465
3574
  def to_s # :nodoc:
3466
- c = @coordinates.unpack("N").join.to_i
3467
- val = (c - (2**31)).abs
3468
- fracsecs = (val % 1e3).to_i.to_s
3469
- val = val / 1e3
3470
- secs = (val % 60).to_i.to_s
3471
- val = val / 60
3472
- mins = (val % 60).to_i.to_s
3473
- degs = (val / 60).to_i.to_s
3474
- posi = (c >= 2**31)
3475
- case posi
3476
- when true
3477
- hemi = @orientation[/^lat$/] ? "N" : "E"
3575
+ c, = @coordinates.unpack("N")
3576
+ val = (c -= Bias).abs
3577
+ val, fracsecs = val.divmod(1000)
3578
+ val, secs = val.divmod(60)
3579
+ degs, mins = val.divmod(60)
3580
+ hemi = if c.negative?
3581
+ @orientation == "lon" ? "W" : "S"
3478
3582
  else
3479
- hemi = @orientation[/^lon$/] ? "W" : "S"
3583
+ @orientation == "lat" ? "N" : "E"
3480
3584
  end
3481
- return degs << " " << mins << " " << secs << "." << fracsecs << " " << hemi
3585
+ format("%d %02d %02d.%03d %s", degs, mins, secs, fracsecs, hemi)
3482
3586
  end
3483
3587
 
3484
3588
  def inspect # :nodoc:
@@ -3506,7 +3610,10 @@ class Resolv
3506
3610
 
3507
3611
  # Regular expression LOC Alt must match.
3508
3612
 
3509
- Regex = /^([+-]*\d+\.*\d*)[m]$/
3613
+ Regex = /\A([+-]?0*\d{1,8}(?:\.\d+)?)m\z/
3614
+
3615
+ # Bias to a base of 100,000m below the WGS 84 reference spheroid.
3616
+ Bias = 100_000_00
3510
3617
 
3511
3618
  ##
3512
3619
  # Creates a new LOC::Alt from +arg+ which may be:
@@ -3519,13 +3626,14 @@ class Resolv
3519
3626
  when Alt
3520
3627
  return arg
3521
3628
  when String
3522
- altitude = ''
3523
- if Regex =~ arg
3524
- altitude = [($1.to_f*(1e2))+(1e7)].pack("N")
3525
- else
3629
+ unless Regex =~ arg
3526
3630
  raise ArgumentError.new("not a properly formed Alt string: " + arg)
3527
3631
  end
3528
- return Alt.new(altitude)
3632
+ altitude = ($1.to_f * 100).to_i + Bias
3633
+ unless (0...0x1_0000_0000) === altitude
3634
+ raise ArgumentError.new("out of raise as Alt: #{arg}")
3635
+ end
3636
+ return new([altitude].pack("N"))
3529
3637
  else
3530
3638
  raise ArgumentError.new("cannot interpret as Alt: #{arg.inspect}")
3531
3639
  end
@@ -3542,8 +3650,8 @@ class Resolv
3542
3650
  attr_reader :altitude
3543
3651
 
3544
3652
  def to_s # :nodoc:
3545
- a = @altitude.unpack("N").join.to_i
3546
- return ((a.to_f/1e2)-1e5).to_s + "m"
3653
+ a, = @altitude.unpack("N")
3654
+ return "#{(a - Bias).fdiv(100)}m"
3547
3655
  end
3548
3656
 
3549
3657
  def inspect # :nodoc:
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resolv
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tanaka Akira
@@ -21,6 +21,7 @@ files:
21
21
  - BSDL
22
22
  - COPYING
23
23
  - README.md
24
+ - ext/win32/resolv/depend
24
25
  - ext/win32/resolv/extconf.rb
25
26
  - ext/win32/resolv/lib/resolv.rb
26
27
  - ext/win32/resolv/resolv.c