resolv 0.7.1 → 0.8.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/ext/win32/resolv/depend +3 -0
- data/ext/win32/resolv/lib/resolv.rb +2 -1
- data/ext/win32/resolv/resolv.c +2 -1
- data/lib/resolv.rb +263 -82
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '017129811e9d2a2c49065d8f95e96f73acba713f6e1e2ee1cd65abb448bea1ce'
|
|
4
|
+
data.tar.gz: 9fe320e19d45e74055b648326efc8bc975b9cbc5aad2a4cd47e1b3660fb81f5e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9632f2e80d4ce1d581f3271b70913e2acef6af77a0289539d0954b191382234d6d53fa190f79bd27468724e917b4ba68fb27e2c8e247ce89f0cabccec1c956a5
|
|
7
|
+
data.tar.gz: 59623cf40d63ab667ce5a39301e2eff5186d8c98306cec5c41fd720aad5b74bca7fcfce877ea44b54de297809d41c4822d7bdd5603964c4e772544998a6a5ef7
|
data/ext/win32/resolv/resolv.c
CHANGED
|
@@ -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
|
{
|
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.
|
|
38
|
+
VERSION = "0.8.0"
|
|
39
39
|
|
|
40
40
|
##
|
|
41
41
|
# Looks up the first IP address for +name+.
|
|
@@ -562,7 +562,19 @@ class Resolv
|
|
|
562
562
|
next if !sender
|
|
563
563
|
senders[[candidate, requester, nameserver, port]] = sender
|
|
564
564
|
end
|
|
565
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
##
|
|
@@ -1089,7 +1191,7 @@ class Resolv
|
|
|
1089
1191
|
if /\./ =~ hostname
|
|
1090
1192
|
@search = [Label.split($')]
|
|
1091
1193
|
else
|
|
1092
|
-
@search = [
|
|
1194
|
+
@search = []
|
|
1093
1195
|
end
|
|
1094
1196
|
end
|
|
1095
1197
|
|
|
@@ -1136,23 +1238,18 @@ class Resolv
|
|
|
1136
1238
|
end
|
|
1137
1239
|
|
|
1138
1240
|
def generate_candidates(name)
|
|
1139
|
-
candidates = nil
|
|
1140
1241
|
name = Name.create(name)
|
|
1141
1242
|
if name.absolute?
|
|
1142
|
-
|
|
1243
|
+
[name]
|
|
1143
1244
|
else
|
|
1245
|
+
abs = Name.new(name.to_a)
|
|
1246
|
+
search = @search.map {|domain| Name.new(name.to_a + domain)}
|
|
1144
1247
|
if @ndots <= name.length - 1
|
|
1145
|
-
|
|
1248
|
+
[abs, *search].uniq
|
|
1146
1249
|
else
|
|
1147
|
-
|
|
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
|
|
1250
|
+
[*search, abs].uniq
|
|
1153
1251
|
end
|
|
1154
1252
|
end
|
|
1155
|
-
return candidates
|
|
1156
1253
|
end
|
|
1157
1254
|
|
|
1158
1255
|
InitialTimeout = 5
|
|
@@ -1253,6 +1350,13 @@ class Resolv
|
|
|
1253
1350
|
|
|
1254
1351
|
class Str # :nodoc:
|
|
1255
1352
|
def initialize(string)
|
|
1353
|
+
# A label is limited to 63 octets. [RFC 1035 2.3.4] Checking it here
|
|
1354
|
+
# makes it an invariant of the object: every label, however it was
|
|
1355
|
+
# built, fits in its length octet and cannot wrap it. Callers turn
|
|
1356
|
+
# this into the error their own contract promises.
|
|
1357
|
+
if string.bytesize > 63
|
|
1358
|
+
raise ArgumentError, "DNS label is too long (#{string.bytesize} bytes, max 63): #{string.inspect}"
|
|
1359
|
+
end
|
|
1256
1360
|
@string = string
|
|
1257
1361
|
# case insensivity of DNS labels doesn't apply non-ASCII characters. [RFC 4343]
|
|
1258
1362
|
# This assumes @string is given in ASCII compatible encoding.
|
|
@@ -1298,7 +1402,26 @@ class Resolv
|
|
|
1298
1402
|
when Name
|
|
1299
1403
|
return arg
|
|
1300
1404
|
when String
|
|
1301
|
-
|
|
1405
|
+
# A hostname is runtime data rather than a programming mistake, so
|
|
1406
|
+
# both size limits surface as ResolvError to stay rescuable alongside
|
|
1407
|
+
# the rest of name resolution. The type check below is a caller
|
|
1408
|
+
# mistake and keeps raising ArgumentError.
|
|
1409
|
+
begin
|
|
1410
|
+
labels = Label.split(arg)
|
|
1411
|
+
rescue ArgumentError => e
|
|
1412
|
+
raise ResolvError.new(e.message)
|
|
1413
|
+
end
|
|
1414
|
+
# Label::Str enforces the per-label limit. Only the total is knowable
|
|
1415
|
+
# here, and it counts the encoded form, so size starts at 1 for the
|
|
1416
|
+
# root label's terminating zero octet. [RFC 1035 2.3.4, 3.1]
|
|
1417
|
+
size = 1
|
|
1418
|
+
labels.each do |label|
|
|
1419
|
+
size += 1 + label.string.bytesize
|
|
1420
|
+
if size > 255
|
|
1421
|
+
raise ResolvError.new("DNS name is too long (#{size} octets, max 255): #{arg.inspect}")
|
|
1422
|
+
end
|
|
1423
|
+
end
|
|
1424
|
+
return Name.new(labels, /\.\z/ =~ arg ? true : false)
|
|
1302
1425
|
else
|
|
1303
1426
|
raise ArgumentError.new("cannot interpret as DNS name: #{arg.inspect}")
|
|
1304
1427
|
end
|
|
@@ -1420,12 +1543,24 @@ class Resolv
|
|
|
1420
1543
|
@rd == other.rd &&
|
|
1421
1544
|
@ra == other.ra &&
|
|
1422
1545
|
@rcode == other.rcode &&
|
|
1423
|
-
|
|
1546
|
+
question_equal?(other.question) &&
|
|
1424
1547
|
@answer == other.answer &&
|
|
1425
1548
|
@authority == other.authority &&
|
|
1426
1549
|
@additional == other.additional
|
|
1427
1550
|
end
|
|
1428
1551
|
|
|
1552
|
+
# A question holds the resource class itself, and decoding creates a fresh
|
|
1553
|
+
# class for each unknown type, so the classes cannot be compared by
|
|
1554
|
+
# identity alone.
|
|
1555
|
+
private def question_equal?(other_question) # :nodoc:
|
|
1556
|
+
return false unless @question.length == other_question.length
|
|
1557
|
+
@question.zip(other_question) {|(name, typeclass), (o_name, o_typeclass)|
|
|
1558
|
+
return false unless name == o_name &&
|
|
1559
|
+
Resource::Generic.type_class_equal?(typeclass, o_typeclass)
|
|
1560
|
+
}
|
|
1561
|
+
return true
|
|
1562
|
+
end
|
|
1563
|
+
|
|
1429
1564
|
def add_question(name, typeclass)
|
|
1430
1565
|
@question << [Name.create(name), typeclass]
|
|
1431
1566
|
end
|
|
@@ -1532,8 +1667,15 @@ class Resolv
|
|
|
1532
1667
|
end
|
|
1533
1668
|
|
|
1534
1669
|
def put_string(d)
|
|
1535
|
-
|
|
1536
|
-
|
|
1670
|
+
s = d.to_s
|
|
1671
|
+
# A character-string is prefixed by a single length octet, so it can
|
|
1672
|
+
# hold at most 255 octets. [RFC 1035 3.3] Reject anything longer to
|
|
1673
|
+
# avoid silently truncating the length to its low 8 bits (mod 256).
|
|
1674
|
+
if s.bytesize > 255
|
|
1675
|
+
raise ArgumentError, "character-string is too long (#{s.bytesize} bytes, max 255): #{s.inspect}"
|
|
1676
|
+
end
|
|
1677
|
+
self.put_pack("C", s.bytesize)
|
|
1678
|
+
@data << s
|
|
1537
1679
|
end
|
|
1538
1680
|
|
|
1539
1681
|
def put_string_list(ds)
|
|
@@ -1563,7 +1705,17 @@ class Resolv
|
|
|
1563
1705
|
end
|
|
1564
1706
|
|
|
1565
1707
|
def put_label(d)
|
|
1566
|
-
|
|
1708
|
+
s = d.to_s
|
|
1709
|
+
# Label::Str applies this limit when a label is built, so what is left
|
|
1710
|
+
# for here is a raw string handed straight to put_labels. The two ways
|
|
1711
|
+
# an over-long label goes wrong differ: 64 to 255 octets write a length
|
|
1712
|
+
# octet in the reserved or compression pointer range, and 256 or more
|
|
1713
|
+
# wrap it mod 256. Either way the encoded name stops being the name the
|
|
1714
|
+
# caller asked for. [RFC 1035 2.3.4, 4.1.4]
|
|
1715
|
+
if s.bytesize > 63
|
|
1716
|
+
raise ArgumentError, "DNS label is too long (#{s.bytesize} bytes, max 63): #{s.inspect}"
|
|
1717
|
+
end
|
|
1718
|
+
self.put_string(s)
|
|
1567
1719
|
end
|
|
1568
1720
|
end
|
|
1569
1721
|
|
|
@@ -1689,7 +1841,9 @@ class Resolv
|
|
|
1689
1841
|
prev_index = @index
|
|
1690
1842
|
save_index = nil
|
|
1691
1843
|
d = []
|
|
1692
|
-
size
|
|
1844
|
+
# size counts the encoded form, so it starts at 1 for the root
|
|
1845
|
+
# label's terminating zero octet. [RFC 1035 3.1]
|
|
1846
|
+
size = 1
|
|
1693
1847
|
while true
|
|
1694
1848
|
raise DecodeError.new("limit exceeded") if @limit <= @index
|
|
1695
1849
|
case @data.getbyte(@index)
|
|
@@ -1720,6 +1874,11 @@ class Resolv
|
|
|
1720
1874
|
|
|
1721
1875
|
def get_label
|
|
1722
1876
|
return Label::Str.new(self.get_string)
|
|
1877
|
+
rescue ArgumentError => e
|
|
1878
|
+
# A length octet of 64..191 is reserved rather than a label length,
|
|
1879
|
+
# but this decoder used to read it as one. [RFC 1035 4.1.4] Report it
|
|
1880
|
+
# the way the rest of a malformed message is reported.
|
|
1881
|
+
raise DecodeError.new(e.message)
|
|
1723
1882
|
end
|
|
1724
1883
|
|
|
1725
1884
|
def get_question
|
|
@@ -1907,8 +2066,9 @@ class Resolv
|
|
|
1907
2066
|
key_name = :"key#{key_number}"
|
|
1908
2067
|
c.const_set(:KeyName, key_name)
|
|
1909
2068
|
c.const_set(:KeyNumber, key_number)
|
|
1910
|
-
|
|
1911
|
-
|
|
2069
|
+
# Not registered in a constant or in ClassHash. ClassHash creates a
|
|
2070
|
+
# class for every unknown SvcParamKey, so registering them
|
|
2071
|
+
# permanently would let a malicious response exhaust memory.
|
|
1912
2072
|
return c
|
|
1913
2073
|
end
|
|
1914
2074
|
end
|
|
@@ -2215,12 +2375,28 @@ class Resolv
|
|
|
2215
2375
|
return self.new(msg.get_bytes)
|
|
2216
2376
|
end
|
|
2217
2377
|
|
|
2378
|
+
# create makes a fresh class for each decoded resource, so the type and
|
|
2379
|
+
# class values have to be compared instead of the class itself.
|
|
2380
|
+
def self.type_class_equal?(klass, other) # :nodoc:
|
|
2381
|
+
return true if klass.equal?(other)
|
|
2382
|
+
Generic > klass && Generic > other &&
|
|
2383
|
+
klass::TypeValue == other::TypeValue &&
|
|
2384
|
+
klass::ClassValue == other::ClassValue
|
|
2385
|
+
end
|
|
2386
|
+
|
|
2387
|
+
def ==(other) # :nodoc:
|
|
2388
|
+
return other.is_a?(Generic) &&
|
|
2389
|
+
Generic.type_class_equal?(self.class, other.class) &&
|
|
2390
|
+
@data == other.data
|
|
2391
|
+
end
|
|
2392
|
+
|
|
2218
2393
|
def self.create(type_value, class_value) # :nodoc:
|
|
2219
2394
|
c = Class.new(Generic)
|
|
2220
2395
|
c.const_set(:TypeValue, type_value)
|
|
2221
2396
|
c.const_set(:ClassValue, class_value)
|
|
2222
|
-
|
|
2223
|
-
|
|
2397
|
+
# Not registered in a constant or in ClassHash. get_class creates a
|
|
2398
|
+
# class for every unknown (type, class) pair, so registering them
|
|
2399
|
+
# permanently would let a malicious response exhaust memory.
|
|
2224
2400
|
return c
|
|
2225
2401
|
end
|
|
2226
2402
|
end
|
|
@@ -3268,7 +3444,7 @@ class Resolv
|
|
|
3268
3444
|
|
|
3269
3445
|
# Regular expression LOC size must match.
|
|
3270
3446
|
|
|
3271
|
-
Regex =
|
|
3447
|
+
Regex = /\A0*(\d{1,8}(?:\.\d+)?)m\z/
|
|
3272
3448
|
|
|
3273
3449
|
##
|
|
3274
3450
|
# Creates a new LOC::Size from +arg+ which may be:
|
|
@@ -3281,13 +3457,14 @@ class Resolv
|
|
|
3281
3457
|
when Size
|
|
3282
3458
|
return arg
|
|
3283
3459
|
when String
|
|
3284
|
-
|
|
3285
|
-
if Regex =~ arg
|
|
3286
|
-
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")
|
|
3287
|
-
else
|
|
3460
|
+
unless Regex =~ arg
|
|
3288
3461
|
raise ArgumentError.new("not a properly formed Size string: " + arg)
|
|
3289
3462
|
end
|
|
3290
|
-
|
|
3463
|
+
unless (0.0...1e8) === (scalar = $1.to_f)
|
|
3464
|
+
raise ArgumentError.new("out of range as Size: #{arg}")
|
|
3465
|
+
end
|
|
3466
|
+
str = (scalar * 100).to_i.to_s
|
|
3467
|
+
return new([(str[0].to_i << 4) + (str.bytesize-1)].pack("C"))
|
|
3291
3468
|
else
|
|
3292
3469
|
raise ArgumentError.new("cannot interpret as Size: #{arg.inspect}")
|
|
3293
3470
|
end
|
|
@@ -3304,8 +3481,8 @@ class Resolv
|
|
|
3304
3481
|
attr_reader :scalar
|
|
3305
3482
|
|
|
3306
3483
|
def to_s # :nodoc:
|
|
3307
|
-
s = @scalar.unpack("
|
|
3308
|
-
return (
|
|
3484
|
+
s, = @scalar.unpack("C")
|
|
3485
|
+
return "#{(s >> 4) * (10.0 ** ((s & 0xf) - 2))}m"
|
|
3309
3486
|
end
|
|
3310
3487
|
|
|
3311
3488
|
def inspect # :nodoc:
|
|
@@ -3333,7 +3510,10 @@ class Resolv
|
|
|
3333
3510
|
|
|
3334
3511
|
# Regular expression LOC Coord must match.
|
|
3335
3512
|
|
|
3336
|
-
Regex =
|
|
3513
|
+
Regex = /\A0*(\d{1,3})\s([0-5]?\d)\s([0-5]?\d(?:\.\d+)?)\s([NESW])\z/
|
|
3514
|
+
|
|
3515
|
+
# Bias for the equator/prime meridian, in thousandths of a second of arc.
|
|
3516
|
+
Bias = 1 << 31
|
|
3337
3517
|
|
|
3338
3518
|
##
|
|
3339
3519
|
# Creates a new LOC::Coord from +arg+ which may be:
|
|
@@ -3346,17 +3526,19 @@ class Resolv
|
|
|
3346
3526
|
when Coord
|
|
3347
3527
|
return arg
|
|
3348
3528
|
when String
|
|
3349
|
-
|
|
3350
|
-
if Regex =~ arg && $1.to_f < 180
|
|
3351
|
-
m = $~
|
|
3352
|
-
hemi = (m[4][/[NE]/]) || (m[4][/[SW]/]) ? 1 : -1
|
|
3353
|
-
coordinates = [ ((m[1].to_i*(36e5)) + (m[2].to_i*(6e4)) +
|
|
3354
|
-
(m[3].to_f*(1e3))) * hemi+(2**31) ].pack("N")
|
|
3355
|
-
orientation = m[4][/[NS]/] ? 'lat' : 'lon'
|
|
3356
|
-
else
|
|
3529
|
+
unless m = Regex.match(arg)
|
|
3357
3530
|
raise ArgumentError.new("not a properly formed Coord string: " + arg)
|
|
3358
3531
|
end
|
|
3359
|
-
|
|
3532
|
+
|
|
3533
|
+
arc = (m[1].to_i * 3_600_000) + (m[2].to_i * 60_000) + (m[3].to_f * 1_000).to_i
|
|
3534
|
+
dir = m[4]
|
|
3535
|
+
lat = dir[/[NS]/]
|
|
3536
|
+
unless arc <= (lat ? 324_000_000 : 648_000_000) # (lat ? 90 : 180) * 3_600_000
|
|
3537
|
+
raise ArgumentError.new("out of range as Coord: #{arg}")
|
|
3538
|
+
end
|
|
3539
|
+
|
|
3540
|
+
hemi = dir[/[NE]/] ? 1 : -1
|
|
3541
|
+
return new([arc * hemi + Bias].pack("N"), lat ? "lat" : "lon")
|
|
3360
3542
|
else
|
|
3361
3543
|
raise ArgumentError.new("cannot interpret as Coord: #{arg.inspect}")
|
|
3362
3544
|
end
|
|
@@ -3364,10 +3546,10 @@ class Resolv
|
|
|
3364
3546
|
|
|
3365
3547
|
# Internal use; use self.create.
|
|
3366
3548
|
def initialize(coordinates,orientation)
|
|
3367
|
-
unless coordinates.kind_of?(String)
|
|
3549
|
+
unless coordinates.kind_of?(String) and coordinates.bytesize == 4
|
|
3368
3550
|
raise ArgumentError.new("Coord must be a 32bit unsigned integer in hex format: #{coordinates.inspect}")
|
|
3369
3551
|
end
|
|
3370
|
-
unless orientation
|
|
3552
|
+
unless orientation == "lon" || orientation == "lat"
|
|
3371
3553
|
raise ArgumentError.new('Coord expects orientation to be a String argument of "lat" or "lon"')
|
|
3372
3554
|
end
|
|
3373
3555
|
@coordinates = coordinates
|
|
@@ -3384,22 +3566,17 @@ class Resolv
|
|
|
3384
3566
|
attr_reader :orientation
|
|
3385
3567
|
|
|
3386
3568
|
def to_s # :nodoc:
|
|
3387
|
-
c = @coordinates.unpack("N")
|
|
3388
|
-
val
|
|
3389
|
-
fracsecs = (
|
|
3390
|
-
val
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
degs = (val / 60).to_i.to_s
|
|
3395
|
-
posi = (c >= 2**31)
|
|
3396
|
-
case posi
|
|
3397
|
-
when true
|
|
3398
|
-
hemi = @orientation[/^lat$/] ? "N" : "E"
|
|
3569
|
+
c, = @coordinates.unpack("N")
|
|
3570
|
+
val = (c -= Bias).abs
|
|
3571
|
+
val, fracsecs = val.divmod(1000)
|
|
3572
|
+
val, secs = val.divmod(60)
|
|
3573
|
+
degs, mins = val.divmod(60)
|
|
3574
|
+
hemi = if c.negative?
|
|
3575
|
+
@orientation == "lon" ? "W" : "S"
|
|
3399
3576
|
else
|
|
3400
|
-
|
|
3577
|
+
@orientation == "lat" ? "N" : "E"
|
|
3401
3578
|
end
|
|
3402
|
-
|
|
3579
|
+
format("%d %02d %02d.%03d %s", degs, mins, secs, fracsecs, hemi)
|
|
3403
3580
|
end
|
|
3404
3581
|
|
|
3405
3582
|
def inspect # :nodoc:
|
|
@@ -3427,7 +3604,10 @@ class Resolv
|
|
|
3427
3604
|
|
|
3428
3605
|
# Regular expression LOC Alt must match.
|
|
3429
3606
|
|
|
3430
|
-
Regex =
|
|
3607
|
+
Regex = /\A([+-]?0*\d{1,8}(?:\.\d+)?)m\z/
|
|
3608
|
+
|
|
3609
|
+
# Bias to a base of 100,000m below the WGS 84 reference spheroid.
|
|
3610
|
+
Bias = 100_000_00
|
|
3431
3611
|
|
|
3432
3612
|
##
|
|
3433
3613
|
# Creates a new LOC::Alt from +arg+ which may be:
|
|
@@ -3440,13 +3620,14 @@ class Resolv
|
|
|
3440
3620
|
when Alt
|
|
3441
3621
|
return arg
|
|
3442
3622
|
when String
|
|
3443
|
-
|
|
3444
|
-
if Regex =~ arg
|
|
3445
|
-
altitude = [($1.to_f*(1e2))+(1e7)].pack("N")
|
|
3446
|
-
else
|
|
3623
|
+
unless Regex =~ arg
|
|
3447
3624
|
raise ArgumentError.new("not a properly formed Alt string: " + arg)
|
|
3448
3625
|
end
|
|
3449
|
-
|
|
3626
|
+
altitude = ($1.to_f * 100).to_i + Bias
|
|
3627
|
+
unless (0...0x1_0000_0000) === altitude
|
|
3628
|
+
raise ArgumentError.new("out of raise as Alt: #{arg}")
|
|
3629
|
+
end
|
|
3630
|
+
return new([altitude].pack("N"))
|
|
3450
3631
|
else
|
|
3451
3632
|
raise ArgumentError.new("cannot interpret as Alt: #{arg.inspect}")
|
|
3452
3633
|
end
|
|
@@ -3463,8 +3644,8 @@ class Resolv
|
|
|
3463
3644
|
attr_reader :altitude
|
|
3464
3645
|
|
|
3465
3646
|
def to_s # :nodoc:
|
|
3466
|
-
a = @altitude.unpack("N")
|
|
3467
|
-
return (
|
|
3647
|
+
a, = @altitude.unpack("N")
|
|
3648
|
+
return "#{(a - Bias).fdiv(100)}m"
|
|
3468
3649
|
end
|
|
3469
3650
|
|
|
3470
3651
|
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.
|
|
4
|
+
version: 0.8.0
|
|
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
|