resolv 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/test.yml +11 -5
- data/.gitignore +2 -0
- data/Gemfile +3 -0
- data/lib/resolv.rb +496 -19
- data/resolv.gemspec +9 -2
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d76dc1156c60da694ce1f0a96165e6d5a7e29d662ad7140b443e9aa735082f31
|
4
|
+
data.tar.gz: 7b532fc449aff2e5a63cb6a31892a45da5b41b6090d454f94d3c3e5165c94271
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 685ce620cc9c83f1d5827de0a374d71f98fbff7629256d2ab7ed65d66eee062a5bc081a869caba31aeb67b4c292ca6dfc53fa4d71180ce15dd82f39d6fb5992b
|
7
|
+
data.tar.gz: 9897929fbfc67bc3a8133d3d9b2f99e7c9350802cf86d4cc0d8f061f397393f91308d6d1d6d35e46d497a3beb7fcf6424dc47ae92d5f7f39e572ac04b2677409
|
data/.github/workflows/test.yml
CHANGED
@@ -3,20 +3,26 @@ name: test
|
|
3
3
|
on: [push, pull_request]
|
4
4
|
|
5
5
|
jobs:
|
6
|
+
ruby-versions:
|
7
|
+
uses: ruby/actions/.github/workflows/ruby_versions.yml@master
|
8
|
+
with:
|
9
|
+
engine: cruby
|
10
|
+
min_version: 2.5
|
11
|
+
|
6
12
|
build:
|
13
|
+
needs: ruby-versions
|
7
14
|
name: build (${{ matrix.ruby }} / ${{ matrix.os }})
|
8
15
|
strategy:
|
9
16
|
matrix:
|
10
|
-
ruby:
|
17
|
+
ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }}
|
11
18
|
os: [ ubuntu-latest, macos-latest ]
|
12
19
|
runs-on: ${{ matrix.os }}
|
13
20
|
steps:
|
14
|
-
- uses: actions/checkout@
|
21
|
+
- uses: actions/checkout@v4
|
15
22
|
- name: Set up Ruby
|
16
23
|
uses: ruby/setup-ruby@v1
|
17
24
|
with:
|
18
25
|
ruby-version: ${{ matrix.ruby }}
|
19
|
-
|
20
|
-
run: bundle install
|
26
|
+
bundler-cache: true
|
21
27
|
- name: Run test
|
22
|
-
run: rake test
|
28
|
+
run: bundle exec rake test
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/lib/resolv.rb
CHANGED
@@ -37,6 +37,8 @@ end
|
|
37
37
|
|
38
38
|
class Resolv
|
39
39
|
|
40
|
+
VERSION = "0.3.0"
|
41
|
+
|
40
42
|
##
|
41
43
|
# Looks up the first IP address for +name+.
|
42
44
|
|
@@ -82,8 +84,8 @@ class Resolv
|
|
82
84
|
##
|
83
85
|
# Creates a new Resolv using +resolvers+.
|
84
86
|
|
85
|
-
def initialize(resolvers=
|
86
|
-
@resolvers = resolvers
|
87
|
+
def initialize(resolvers=nil, use_ipv6: nil)
|
88
|
+
@resolvers = resolvers || [Hosts.new, DNS.new(DNS::Config.default_config_hash.merge(use_ipv6: use_ipv6))]
|
87
89
|
end
|
88
90
|
|
89
91
|
##
|
@@ -196,7 +198,7 @@ class Resolv
|
|
196
198
|
next unless addr
|
197
199
|
@addr2name[addr] = [] unless @addr2name.include? addr
|
198
200
|
@addr2name[addr] << hostname
|
199
|
-
@addr2name[addr]
|
201
|
+
@addr2name[addr].concat(aliases)
|
200
202
|
@name2addr[hostname] = [] unless @name2addr.include? hostname
|
201
203
|
@name2addr[hostname] << addr
|
202
204
|
aliases.each {|n|
|
@@ -310,6 +312,8 @@ class Resolv
|
|
310
312
|
# String:: Path to a file using /etc/resolv.conf's format.
|
311
313
|
# Hash:: Must contain :nameserver, :search and :ndots keys.
|
312
314
|
# :nameserver_port can be used to specify port number of nameserver address.
|
315
|
+
# :raise_timeout_errors can be used to raise timeout errors
|
316
|
+
# as exceptions instead of treating the same as an NXDOMAIN response.
|
313
317
|
#
|
314
318
|
# The value of :nameserver should be an address string or
|
315
319
|
# an array of address strings.
|
@@ -406,6 +410,11 @@ class Resolv
|
|
406
410
|
end
|
407
411
|
|
408
412
|
def use_ipv6? # :nodoc:
|
413
|
+
use_ipv6 = @config.use_ipv6?
|
414
|
+
unless use_ipv6.nil?
|
415
|
+
return use_ipv6
|
416
|
+
end
|
417
|
+
|
409
418
|
begin
|
410
419
|
list = Socket.ip_address_list
|
411
420
|
rescue NotImplementedError
|
@@ -748,7 +757,7 @@ class Resolv
|
|
748
757
|
next if @socks_hash[bind_host]
|
749
758
|
begin
|
750
759
|
sock = UDPSocket.new(af)
|
751
|
-
rescue Errno::EAFNOSUPPORT
|
760
|
+
rescue Errno::EAFNOSUPPORT, Errno::EPROTONOSUPPORT
|
752
761
|
next # The kernel doesn't support the address family.
|
753
762
|
end
|
754
763
|
@socks << sock
|
@@ -965,7 +974,7 @@ class Resolv
|
|
965
974
|
next unless keyword
|
966
975
|
case keyword
|
967
976
|
when 'nameserver'
|
968
|
-
nameserver
|
977
|
+
nameserver.concat(args)
|
969
978
|
when 'domain'
|
970
979
|
next if args.empty?
|
971
980
|
search = [args[0]]
|
@@ -1004,6 +1013,7 @@ class Resolv
|
|
1004
1013
|
@mutex.synchronize {
|
1005
1014
|
unless @initialized
|
1006
1015
|
@nameserver_port = []
|
1016
|
+
@use_ipv6 = nil
|
1007
1017
|
@search = nil
|
1008
1018
|
@ndots = 1
|
1009
1019
|
case @config_info
|
@@ -1028,8 +1038,12 @@ class Resolv
|
|
1028
1038
|
if config_hash.include? :nameserver_port
|
1029
1039
|
@nameserver_port = config_hash[:nameserver_port].map {|ns, port| [ns, (port || Port)] }
|
1030
1040
|
end
|
1041
|
+
if config_hash.include? :use_ipv6
|
1042
|
+
@use_ipv6 = config_hash[:use_ipv6]
|
1043
|
+
end
|
1031
1044
|
@search = config_hash[:search] if config_hash.include? :search
|
1032
1045
|
@ndots = config_hash[:ndots] if config_hash.include? :ndots
|
1046
|
+
@raise_timeout_errors = config_hash[:raise_timeout_errors]
|
1033
1047
|
|
1034
1048
|
if @nameserver_port.empty?
|
1035
1049
|
@nameserver_port << ['0.0.0.0', Port]
|
@@ -1083,6 +1097,10 @@ class Resolv
|
|
1083
1097
|
@nameserver_port
|
1084
1098
|
end
|
1085
1099
|
|
1100
|
+
def use_ipv6?
|
1101
|
+
@use_ipv6
|
1102
|
+
end
|
1103
|
+
|
1086
1104
|
def generate_candidates(name)
|
1087
1105
|
candidates = nil
|
1088
1106
|
name = Name.create(name)
|
@@ -1116,6 +1134,7 @@ class Resolv
|
|
1116
1134
|
def resolv(name)
|
1117
1135
|
candidates = generate_candidates(name)
|
1118
1136
|
timeouts = @timeouts || generate_timeouts
|
1137
|
+
timeout_error = false
|
1119
1138
|
begin
|
1120
1139
|
candidates.each {|candidate|
|
1121
1140
|
begin
|
@@ -1127,11 +1146,13 @@ class Resolv
|
|
1127
1146
|
end
|
1128
1147
|
}
|
1129
1148
|
}
|
1149
|
+
timeout_error = true
|
1130
1150
|
raise ResolvError.new("DNS resolv timeout: #{name}")
|
1131
1151
|
rescue NXDomain
|
1132
1152
|
end
|
1133
1153
|
}
|
1134
1154
|
rescue ResolvError
|
1155
|
+
raise if @raise_timeout_errors && timeout_error
|
1135
1156
|
end
|
1136
1157
|
end
|
1137
1158
|
|
@@ -1487,14 +1508,14 @@ class Resolv
|
|
1487
1508
|
}
|
1488
1509
|
end
|
1489
1510
|
|
1490
|
-
def put_name(d)
|
1491
|
-
put_labels(d.to_a)
|
1511
|
+
def put_name(d, compress: true)
|
1512
|
+
put_labels(d.to_a, compress: compress)
|
1492
1513
|
end
|
1493
1514
|
|
1494
|
-
def put_labels(d)
|
1515
|
+
def put_labels(d, compress: true)
|
1495
1516
|
d.each_index {|i|
|
1496
1517
|
domain = d[i..-1]
|
1497
|
-
if idx = @names[domain]
|
1518
|
+
if compress && idx = @names[domain]
|
1498
1519
|
self.put_pack("n", 0xc000 | idx)
|
1499
1520
|
return
|
1500
1521
|
else
|
@@ -1518,13 +1539,15 @@ class Resolv
|
|
1518
1539
|
id, flag, qdcount, ancount, nscount, arcount =
|
1519
1540
|
msg.get_unpack('nnnnnn')
|
1520
1541
|
o.id = id
|
1542
|
+
o.tc = (flag >> 9) & 1
|
1543
|
+
o.rcode = flag & 15
|
1544
|
+
return o unless o.tc.zero?
|
1545
|
+
|
1521
1546
|
o.qr = (flag >> 15) & 1
|
1522
1547
|
o.opcode = (flag >> 11) & 15
|
1523
1548
|
o.aa = (flag >> 10) & 1
|
1524
|
-
o.tc = (flag >> 9) & 1
|
1525
1549
|
o.rd = (flag >> 8) & 1
|
1526
1550
|
o.ra = (flag >> 7) & 1
|
1527
|
-
o.rcode = flag & 15
|
1528
1551
|
(1..qdcount).each {
|
1529
1552
|
name, typeclass = msg.get_question
|
1530
1553
|
o.add_question(name, typeclass)
|
@@ -1616,6 +1639,14 @@ class Resolv
|
|
1616
1639
|
strings
|
1617
1640
|
end
|
1618
1641
|
|
1642
|
+
def get_list
|
1643
|
+
[].tap do |values|
|
1644
|
+
while @index < @limit
|
1645
|
+
values << yield
|
1646
|
+
end
|
1647
|
+
end
|
1648
|
+
end
|
1649
|
+
|
1619
1650
|
def get_name
|
1620
1651
|
return Name.new(self.get_labels)
|
1621
1652
|
end
|
@@ -1676,6 +1707,378 @@ class Resolv
|
|
1676
1707
|
end
|
1677
1708
|
end
|
1678
1709
|
|
1710
|
+
##
|
1711
|
+
# SvcParams for service binding RRs. [RFC9460]
|
1712
|
+
|
1713
|
+
class SvcParams
|
1714
|
+
include Enumerable
|
1715
|
+
|
1716
|
+
##
|
1717
|
+
# Create a list of SvcParams with the given initial content.
|
1718
|
+
#
|
1719
|
+
# +params+ has to be an enumerable of +SvcParam+s.
|
1720
|
+
# If its content has +SvcParam+s with the duplicate key,
|
1721
|
+
# the one appears last takes precedence.
|
1722
|
+
|
1723
|
+
def initialize(params = [])
|
1724
|
+
@params = {}
|
1725
|
+
|
1726
|
+
params.each do |param|
|
1727
|
+
add param
|
1728
|
+
end
|
1729
|
+
end
|
1730
|
+
|
1731
|
+
##
|
1732
|
+
# Get SvcParam for the given +key+ in this list.
|
1733
|
+
|
1734
|
+
def [](key)
|
1735
|
+
@params[canonical_key(key)]
|
1736
|
+
end
|
1737
|
+
|
1738
|
+
##
|
1739
|
+
# Get the number of SvcParams in this list.
|
1740
|
+
|
1741
|
+
def count
|
1742
|
+
@params.count
|
1743
|
+
end
|
1744
|
+
|
1745
|
+
##
|
1746
|
+
# Get whether this list is empty.
|
1747
|
+
|
1748
|
+
def empty?
|
1749
|
+
@params.empty?
|
1750
|
+
end
|
1751
|
+
|
1752
|
+
##
|
1753
|
+
# Add the SvcParam +param+ to this list, overwriting the existing one with the same key.
|
1754
|
+
|
1755
|
+
def add(param)
|
1756
|
+
@params[param.class.key_number] = param
|
1757
|
+
end
|
1758
|
+
|
1759
|
+
##
|
1760
|
+
# Remove the +SvcParam+ with the given +key+ and return it.
|
1761
|
+
|
1762
|
+
def delete(key)
|
1763
|
+
@params.delete(canonical_key(key))
|
1764
|
+
end
|
1765
|
+
|
1766
|
+
##
|
1767
|
+
# Enumerate the +SvcParam+s in this list.
|
1768
|
+
|
1769
|
+
def each(&block)
|
1770
|
+
return enum_for(:each) unless block
|
1771
|
+
@params.each_value(&block)
|
1772
|
+
end
|
1773
|
+
|
1774
|
+
def encode(msg) # :nodoc:
|
1775
|
+
@params.keys.sort.each do |key|
|
1776
|
+
msg.put_pack('n', key)
|
1777
|
+
msg.put_length16 do
|
1778
|
+
@params.fetch(key).encode(msg)
|
1779
|
+
end
|
1780
|
+
end
|
1781
|
+
end
|
1782
|
+
|
1783
|
+
def self.decode(msg) # :nodoc:
|
1784
|
+
params = msg.get_list do
|
1785
|
+
key, = msg.get_unpack('n')
|
1786
|
+
msg.get_length16 do
|
1787
|
+
SvcParam::ClassHash[key].decode(msg)
|
1788
|
+
end
|
1789
|
+
end
|
1790
|
+
|
1791
|
+
return self.new(params)
|
1792
|
+
end
|
1793
|
+
|
1794
|
+
private
|
1795
|
+
|
1796
|
+
def canonical_key(key) # :nodoc:
|
1797
|
+
case key
|
1798
|
+
when Integer
|
1799
|
+
key
|
1800
|
+
when /\Akey(\d+)\z/
|
1801
|
+
Integer($1)
|
1802
|
+
when Symbol
|
1803
|
+
SvcParam::ClassHash[key].key_number
|
1804
|
+
else
|
1805
|
+
raise TypeError, 'key must be either String or Symbol'
|
1806
|
+
end
|
1807
|
+
end
|
1808
|
+
end
|
1809
|
+
|
1810
|
+
|
1811
|
+
##
|
1812
|
+
# Base class for SvcParam. [RFC9460]
|
1813
|
+
|
1814
|
+
class SvcParam
|
1815
|
+
|
1816
|
+
##
|
1817
|
+
# Get the presentation name of the SvcParamKey.
|
1818
|
+
|
1819
|
+
def self.key_name
|
1820
|
+
const_get(:KeyName)
|
1821
|
+
end
|
1822
|
+
|
1823
|
+
##
|
1824
|
+
# Get the registered number of the SvcParamKey.
|
1825
|
+
|
1826
|
+
def self.key_number
|
1827
|
+
const_get(:KeyNumber)
|
1828
|
+
end
|
1829
|
+
|
1830
|
+
ClassHash = Hash.new do |h, key| # :nodoc:
|
1831
|
+
case key
|
1832
|
+
when Integer
|
1833
|
+
Generic.create(key)
|
1834
|
+
when /\Akey(?<key>\d+)\z/
|
1835
|
+
Generic.create(key.to_int)
|
1836
|
+
when Symbol
|
1837
|
+
raise KeyError, "unknown key #{key}"
|
1838
|
+
else
|
1839
|
+
raise TypeError, 'key must be either String or Symbol'
|
1840
|
+
end
|
1841
|
+
end
|
1842
|
+
|
1843
|
+
##
|
1844
|
+
# Generic SvcParam abstract class.
|
1845
|
+
|
1846
|
+
class Generic < SvcParam
|
1847
|
+
|
1848
|
+
##
|
1849
|
+
# SvcParamValue in wire-format byte string.
|
1850
|
+
|
1851
|
+
attr_reader :value
|
1852
|
+
|
1853
|
+
##
|
1854
|
+
# Create generic SvcParam
|
1855
|
+
|
1856
|
+
def initialize(value)
|
1857
|
+
@value = value
|
1858
|
+
end
|
1859
|
+
|
1860
|
+
def encode(msg) # :nodoc:
|
1861
|
+
msg.put_bytes(@value)
|
1862
|
+
end
|
1863
|
+
|
1864
|
+
def self.decode(msg) # :nodoc:
|
1865
|
+
return self.new(msg.get_bytes)
|
1866
|
+
end
|
1867
|
+
|
1868
|
+
def self.create(key_number)
|
1869
|
+
c = Class.new(Generic)
|
1870
|
+
key_name = :"key#{key_number}"
|
1871
|
+
c.const_set(:KeyName, key_name)
|
1872
|
+
c.const_set(:KeyNumber, key_number)
|
1873
|
+
self.const_set(:"Key#{key_number}", c)
|
1874
|
+
ClassHash[key_name] = ClassHash[key_number] = c
|
1875
|
+
return c
|
1876
|
+
end
|
1877
|
+
end
|
1878
|
+
|
1879
|
+
##
|
1880
|
+
# "mandatory" SvcParam -- Mandatory keys in service binding RR
|
1881
|
+
|
1882
|
+
class Mandatory < SvcParam
|
1883
|
+
KeyName = :mandatory
|
1884
|
+
KeyNumber = 0
|
1885
|
+
ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
|
1886
|
+
|
1887
|
+
##
|
1888
|
+
# Mandatory keys.
|
1889
|
+
|
1890
|
+
attr_reader :keys
|
1891
|
+
|
1892
|
+
##
|
1893
|
+
# Initialize "mandatory" ScvParam.
|
1894
|
+
|
1895
|
+
def initialize(keys)
|
1896
|
+
@keys = keys.map(&:to_int)
|
1897
|
+
end
|
1898
|
+
|
1899
|
+
def encode(msg) # :nodoc:
|
1900
|
+
@keys.sort.each do |key|
|
1901
|
+
msg.put_pack('n', key)
|
1902
|
+
end
|
1903
|
+
end
|
1904
|
+
|
1905
|
+
def self.decode(msg) # :nodoc:
|
1906
|
+
keys = msg.get_list { msg.get_unpack('n')[0] }
|
1907
|
+
return self.new(keys)
|
1908
|
+
end
|
1909
|
+
end
|
1910
|
+
|
1911
|
+
##
|
1912
|
+
# "alpn" SvcParam -- Additional supported protocols
|
1913
|
+
|
1914
|
+
class ALPN < SvcParam
|
1915
|
+
KeyName = :alpn
|
1916
|
+
KeyNumber = 1
|
1917
|
+
ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
|
1918
|
+
|
1919
|
+
##
|
1920
|
+
# Supported protocol IDs.
|
1921
|
+
|
1922
|
+
attr_reader :protocol_ids
|
1923
|
+
|
1924
|
+
##
|
1925
|
+
# Initialize "alpn" ScvParam.
|
1926
|
+
|
1927
|
+
def initialize(protocol_ids)
|
1928
|
+
@protocol_ids = protocol_ids.map(&:to_str)
|
1929
|
+
end
|
1930
|
+
|
1931
|
+
def encode(msg) # :nodoc:
|
1932
|
+
msg.put_string_list(@protocol_ids)
|
1933
|
+
end
|
1934
|
+
|
1935
|
+
def self.decode(msg) # :nodoc:
|
1936
|
+
return self.new(msg.get_string_list)
|
1937
|
+
end
|
1938
|
+
end
|
1939
|
+
|
1940
|
+
##
|
1941
|
+
# "no-default-alpn" SvcParam -- No support for default protocol
|
1942
|
+
|
1943
|
+
class NoDefaultALPN < SvcParam
|
1944
|
+
KeyName = :'no-default-alpn'
|
1945
|
+
KeyNumber = 2
|
1946
|
+
ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
|
1947
|
+
|
1948
|
+
def encode(msg) # :nodoc:
|
1949
|
+
# no payload
|
1950
|
+
end
|
1951
|
+
|
1952
|
+
def self.decode(msg) # :nodoc:
|
1953
|
+
return self.new
|
1954
|
+
end
|
1955
|
+
end
|
1956
|
+
|
1957
|
+
##
|
1958
|
+
# "port" SvcParam -- Port for alternative endpoint
|
1959
|
+
|
1960
|
+
class Port < SvcParam
|
1961
|
+
KeyName = :port
|
1962
|
+
KeyNumber = 3
|
1963
|
+
ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
|
1964
|
+
|
1965
|
+
##
|
1966
|
+
# Port number.
|
1967
|
+
|
1968
|
+
attr_reader :port
|
1969
|
+
|
1970
|
+
##
|
1971
|
+
# Initialize "port" ScvParam.
|
1972
|
+
|
1973
|
+
def initialize(port)
|
1974
|
+
@port = port.to_int
|
1975
|
+
end
|
1976
|
+
|
1977
|
+
def encode(msg) # :nodoc:
|
1978
|
+
msg.put_pack('n', @port)
|
1979
|
+
end
|
1980
|
+
|
1981
|
+
def self.decode(msg) # :nodoc:
|
1982
|
+
port, = msg.get_unpack('n')
|
1983
|
+
return self.new(port)
|
1984
|
+
end
|
1985
|
+
end
|
1986
|
+
|
1987
|
+
##
|
1988
|
+
# "ipv4hint" SvcParam -- IPv4 address hints
|
1989
|
+
|
1990
|
+
class IPv4Hint < SvcParam
|
1991
|
+
KeyName = :ipv4hint
|
1992
|
+
KeyNumber = 4
|
1993
|
+
ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
|
1994
|
+
|
1995
|
+
##
|
1996
|
+
# Set of IPv4 addresses.
|
1997
|
+
|
1998
|
+
attr_reader :addresses
|
1999
|
+
|
2000
|
+
##
|
2001
|
+
# Initialize "ipv4hint" ScvParam.
|
2002
|
+
|
2003
|
+
def initialize(addresses)
|
2004
|
+
@addresses = addresses.map {|address| IPv4.create(address) }
|
2005
|
+
end
|
2006
|
+
|
2007
|
+
def encode(msg) # :nodoc:
|
2008
|
+
@addresses.each do |address|
|
2009
|
+
msg.put_bytes(address.address)
|
2010
|
+
end
|
2011
|
+
end
|
2012
|
+
|
2013
|
+
def self.decode(msg) # :nodoc:
|
2014
|
+
addresses = msg.get_list { IPv4.new(msg.get_bytes(4)) }
|
2015
|
+
return self.new(addresses)
|
2016
|
+
end
|
2017
|
+
end
|
2018
|
+
|
2019
|
+
##
|
2020
|
+
# "ipv6hint" SvcParam -- IPv6 address hints
|
2021
|
+
|
2022
|
+
class IPv6Hint < SvcParam
|
2023
|
+
KeyName = :ipv6hint
|
2024
|
+
KeyNumber = 6
|
2025
|
+
ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
|
2026
|
+
|
2027
|
+
##
|
2028
|
+
# Set of IPv6 addresses.
|
2029
|
+
|
2030
|
+
attr_reader :addresses
|
2031
|
+
|
2032
|
+
##
|
2033
|
+
# Initialize "ipv6hint" ScvParam.
|
2034
|
+
|
2035
|
+
def initialize(addresses)
|
2036
|
+
@addresses = addresses.map {|address| IPv6.create(address) }
|
2037
|
+
end
|
2038
|
+
|
2039
|
+
def encode(msg) # :nodoc:
|
2040
|
+
@addresses.each do |address|
|
2041
|
+
msg.put_bytes(address.address)
|
2042
|
+
end
|
2043
|
+
end
|
2044
|
+
|
2045
|
+
def self.decode(msg) # :nodoc:
|
2046
|
+
addresses = msg.get_list { IPv6.new(msg.get_bytes(16)) }
|
2047
|
+
return self.new(addresses)
|
2048
|
+
end
|
2049
|
+
end
|
2050
|
+
|
2051
|
+
##
|
2052
|
+
# "dohpath" SvcParam -- DNS over HTTPS path template [RFC9461]
|
2053
|
+
|
2054
|
+
class DoHPath < SvcParam
|
2055
|
+
KeyName = :dohpath
|
2056
|
+
KeyNumber = 7
|
2057
|
+
ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
|
2058
|
+
|
2059
|
+
##
|
2060
|
+
# URI template for DoH queries.
|
2061
|
+
|
2062
|
+
attr_reader :template
|
2063
|
+
|
2064
|
+
##
|
2065
|
+
# Initialize "dohpath" ScvParam.
|
2066
|
+
|
2067
|
+
def initialize(template)
|
2068
|
+
@template = template.encode('utf-8')
|
2069
|
+
end
|
2070
|
+
|
2071
|
+
def encode(msg) # :nodoc:
|
2072
|
+
msg.put_bytes(@template)
|
2073
|
+
end
|
2074
|
+
|
2075
|
+
def self.decode(msg) # :nodoc:
|
2076
|
+
template = msg.get_bytes.force_encoding('utf-8')
|
2077
|
+
return self.new(template)
|
2078
|
+
end
|
2079
|
+
end
|
2080
|
+
end
|
2081
|
+
|
1679
2082
|
##
|
1680
2083
|
# A DNS query abstract class.
|
1681
2084
|
|
@@ -2328,7 +2731,7 @@ class Resolv
|
|
2328
2731
|
msg.put_pack("n", @priority)
|
2329
2732
|
msg.put_pack("n", @weight)
|
2330
2733
|
msg.put_pack("n", @port)
|
2331
|
-
msg.put_name(@target)
|
2734
|
+
msg.put_name(@target, compress: false)
|
2332
2735
|
end
|
2333
2736
|
|
2334
2737
|
def self.decode_rdata(msg) # :nodoc:
|
@@ -2339,6 +2742,84 @@ class Resolv
|
|
2339
2742
|
return self.new(priority, weight, port, target)
|
2340
2743
|
end
|
2341
2744
|
end
|
2745
|
+
|
2746
|
+
##
|
2747
|
+
# Common implementation for SVCB-compatible resource records.
|
2748
|
+
|
2749
|
+
class ServiceBinding
|
2750
|
+
|
2751
|
+
##
|
2752
|
+
# Create a service binding resource record.
|
2753
|
+
|
2754
|
+
def initialize(priority, target, params = [])
|
2755
|
+
@priority = priority.to_int
|
2756
|
+
@target = Name.create(target)
|
2757
|
+
@params = SvcParams.new(params)
|
2758
|
+
end
|
2759
|
+
|
2760
|
+
##
|
2761
|
+
# The priority of this target host.
|
2762
|
+
#
|
2763
|
+
# The range is 0-65535.
|
2764
|
+
# If set to 0, this RR is in AliasMode. Otherwise, it is in ServiceMode.
|
2765
|
+
|
2766
|
+
attr_reader :priority
|
2767
|
+
|
2768
|
+
##
|
2769
|
+
# The domain name of the target host.
|
2770
|
+
|
2771
|
+
attr_reader :target
|
2772
|
+
|
2773
|
+
##
|
2774
|
+
# The service paramters for the target host.
|
2775
|
+
|
2776
|
+
attr_reader :params
|
2777
|
+
|
2778
|
+
##
|
2779
|
+
# Whether this RR is in AliasMode.
|
2780
|
+
|
2781
|
+
def alias_mode?
|
2782
|
+
self.priority == 0
|
2783
|
+
end
|
2784
|
+
|
2785
|
+
##
|
2786
|
+
# Whether this RR is in ServiceMode.
|
2787
|
+
|
2788
|
+
def service_mode?
|
2789
|
+
!alias_mode?
|
2790
|
+
end
|
2791
|
+
|
2792
|
+
def encode_rdata(msg) # :nodoc:
|
2793
|
+
msg.put_pack("n", @priority)
|
2794
|
+
msg.put_name(@target, compress: false)
|
2795
|
+
@params.encode(msg)
|
2796
|
+
end
|
2797
|
+
|
2798
|
+
def self.decode_rdata(msg) # :nodoc:
|
2799
|
+
priority, = msg.get_unpack("n")
|
2800
|
+
target = msg.get_name
|
2801
|
+
params = SvcParams.decode(msg)
|
2802
|
+
return self.new(priority, target, params)
|
2803
|
+
end
|
2804
|
+
end
|
2805
|
+
|
2806
|
+
##
|
2807
|
+
# SVCB resource record [RFC9460]
|
2808
|
+
|
2809
|
+
class SVCB < ServiceBinding
|
2810
|
+
TypeValue = 64
|
2811
|
+
ClassValue = IN::ClassValue
|
2812
|
+
ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
|
2813
|
+
end
|
2814
|
+
|
2815
|
+
##
|
2816
|
+
# HTTPS resource record [RFC9460]
|
2817
|
+
|
2818
|
+
class HTTPS < ServiceBinding
|
2819
|
+
TypeValue = 65
|
2820
|
+
ClassValue = IN::ClassValue
|
2821
|
+
ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
|
2822
|
+
end
|
2342
2823
|
end
|
2343
2824
|
end
|
2344
2825
|
end
|
@@ -2464,7 +2945,7 @@ class Resolv
|
|
2464
2945
|
Regex_8HexLinkLocal = /\A
|
2465
2946
|
[Ff][Ee]80
|
2466
2947
|
(?::[0-9A-Fa-f]{1,4}){7}
|
2467
|
-
%[0-9A-Za-z]+
|
2948
|
+
%[-0-9A-Za-z._~]+
|
2468
2949
|
\z/x
|
2469
2950
|
|
2470
2951
|
##
|
@@ -2478,7 +2959,7 @@ class Resolv
|
|
2478
2959
|
|
|
2479
2960
|
:((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)
|
2480
2961
|
)?
|
2481
|
-
:[0-9A-Fa-f]{1,4}%[0-9A-Za-z.]+
|
2962
|
+
:[0-9A-Fa-f]{1,4}%[-0-9A-Za-z._~]+
|
2482
2963
|
\z/x
|
2483
2964
|
|
2484
2965
|
##
|
@@ -2558,11 +3039,7 @@ class Resolv
|
|
2558
3039
|
attr_reader :address
|
2559
3040
|
|
2560
3041
|
def to_s # :nodoc:
|
2561
|
-
|
2562
|
-
unless address.sub!(/(^|:)0(:0)+(:|$)/, '::')
|
2563
|
-
address.sub!(/(^|:)0(:|$)/, '::')
|
2564
|
-
end
|
2565
|
-
return address
|
3042
|
+
sprintf("%x:%x:%x:%x:%x:%x:%x:%x", *@address.unpack("nnnnnnnn")).sub(/(^|:)0(:0)+(:|$)/, '::')
|
2566
3043
|
end
|
2567
3044
|
|
2568
3045
|
def inspect # :nodoc:
|
data/resolv.gemspec
CHANGED
@@ -1,6 +1,13 @@
|
|
1
|
+
name = File.basename(__FILE__, ".gemspec")
|
2
|
+
version = ["lib", Array.new(name.count("-")+1).join("/")].find do |dir|
|
3
|
+
break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")) do |line|
|
4
|
+
/^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
|
5
|
+
end rescue nil
|
6
|
+
end
|
7
|
+
|
1
8
|
Gem::Specification.new do |spec|
|
2
|
-
spec.name =
|
3
|
-
spec.version =
|
9
|
+
spec.name = name
|
10
|
+
spec.version = version
|
4
11
|
spec.authors = ["Tanaka Akira"]
|
5
12
|
spec.email = ["akr@fsij.org"]
|
6
13
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resolv
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tanaka Akira
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-12-13 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Thread-aware DNS resolver library in Ruby.
|
14
14
|
email:
|
@@ -17,6 +17,7 @@ executables: []
|
|
17
17
|
extensions: []
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
|
+
- ".github/dependabot.yml"
|
20
21
|
- ".github/workflows/test.yml"
|
21
22
|
- ".gitignore"
|
22
23
|
- Gemfile
|
@@ -49,7 +50,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
49
50
|
- !ruby/object:Gem::Version
|
50
51
|
version: '0'
|
51
52
|
requirements: []
|
52
|
-
rubygems_version: 3.
|
53
|
+
rubygems_version: 3.5.0.dev
|
53
54
|
signing_key:
|
54
55
|
specification_version: 4
|
55
56
|
summary: Thread-aware DNS resolver library in Ruby.
|