resolv 0.2.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +13 -3
- data/.gitignore +2 -0
- data/Gemfile +3 -0
- data/Rakefile +0 -7
- data/lib/resolv.rb +559 -27
- data/resolv.gemspec +9 -2
- metadata +3 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56680c64e918a60193f43e3ca4a7093db20bbe66f516da79a159d9a3c05860d8
|
4
|
+
data.tar.gz: f017a2c89fbcdf946b69ce57d41a67537b73f54ec6e99d18cc62865fd6ec1633
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d57f866a30bc2b8da6dbcdb8c65d0a5db4630af4ec0244aa490e37370ba7b27c3031e153e76055e7f4f63114bd477944326b05dbb7319f97bf59478f8518d76a
|
7
|
+
data.tar.gz: f07e85287b3bb9973c90900c1fbdc272e193d91493e63b407e696cc9f52de29fc0716f2ec11aba56d37d89a12b6d3fec55d21bd8ce756f02d56577940c454b06
|
data/.github/workflows/test.yml
CHANGED
@@ -3,15 +3,25 @@ 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:
|
11
|
-
os: [ ubuntu-latest, macos-latest ]
|
17
|
+
ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }}
|
18
|
+
os: [ ubuntu-latest, macos-latest, windows-latest ]
|
19
|
+
include:
|
20
|
+
- ruby: mswin
|
21
|
+
os: windows-latest
|
12
22
|
runs-on: ${{ matrix.os }}
|
13
23
|
steps:
|
14
|
-
- uses: actions/checkout@
|
24
|
+
- uses: actions/checkout@v4
|
15
25
|
- name: Set up Ruby
|
16
26
|
uses: ruby/setup-ruby@v1
|
17
27
|
with:
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -7,11 +7,4 @@ Rake::TestTask.new(:test) do |t|
|
|
7
7
|
t.test_files = FileList["test/**/test_*.rb"]
|
8
8
|
end
|
9
9
|
|
10
|
-
task :sync_tool do
|
11
|
-
require 'fileutils'
|
12
|
-
FileUtils.cp "../ruby/tool/lib/core_assertions.rb", "./test/lib"
|
13
|
-
FileUtils.cp "../ruby/tool/lib/envutil.rb", "./test/lib"
|
14
|
-
FileUtils.cp "../ruby/tool/lib/find_executable.rb", "./test/lib"
|
15
|
-
end
|
16
|
-
|
17
10
|
task :default => :test
|
data/lib/resolv.rb
CHANGED
@@ -37,6 +37,8 @@ end
|
|
37
37
|
|
38
38
|
class Resolv
|
39
39
|
|
40
|
+
VERSION = "0.4.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
|
##
|
@@ -192,17 +194,10 @@ class Resolv
|
|
192
194
|
File.open(@filename, 'rb') {|f|
|
193
195
|
f.each {|line|
|
194
196
|
line.sub!(/#.*/, '')
|
195
|
-
addr,
|
197
|
+
addr, *hostnames = line.split(/\s+/)
|
196
198
|
next unless addr
|
197
|
-
@addr2name[addr]
|
198
|
-
@
|
199
|
-
@addr2name[addr] += aliases
|
200
|
-
@name2addr[hostname] = [] unless @name2addr.include? hostname
|
201
|
-
@name2addr[hostname] << addr
|
202
|
-
aliases.each {|n|
|
203
|
-
@name2addr[n] = [] unless @name2addr.include? n
|
204
|
-
@name2addr[n] << addr
|
205
|
-
}
|
199
|
+
(@addr2name[addr] ||= []).concat(hostnames)
|
200
|
+
hostnames.each {|hostname| (@name2addr[hostname] ||= []) << addr}
|
206
201
|
}
|
207
202
|
}
|
208
203
|
@name2addr.each {|name, arr| arr.reverse!}
|
@@ -310,6 +305,8 @@ class Resolv
|
|
310
305
|
# String:: Path to a file using /etc/resolv.conf's format.
|
311
306
|
# Hash:: Must contain :nameserver, :search and :ndots keys.
|
312
307
|
# :nameserver_port can be used to specify port number of nameserver address.
|
308
|
+
# :raise_timeout_errors can be used to raise timeout errors
|
309
|
+
# as exceptions instead of treating the same as an NXDOMAIN response.
|
313
310
|
#
|
314
311
|
# The value of :nameserver should be an address string or
|
315
312
|
# an array of address strings.
|
@@ -406,6 +403,11 @@ class Resolv
|
|
406
403
|
end
|
407
404
|
|
408
405
|
def use_ipv6? # :nodoc:
|
406
|
+
use_ipv6 = @config.use_ipv6?
|
407
|
+
unless use_ipv6.nil?
|
408
|
+
return use_ipv6
|
409
|
+
end
|
410
|
+
|
409
411
|
begin
|
410
412
|
list = Socket.ip_address_list
|
411
413
|
rescue NotImplementedError
|
@@ -748,7 +750,7 @@ class Resolv
|
|
748
750
|
next if @socks_hash[bind_host]
|
749
751
|
begin
|
750
752
|
sock = UDPSocket.new(af)
|
751
|
-
rescue Errno::EAFNOSUPPORT
|
753
|
+
rescue Errno::EAFNOSUPPORT, Errno::EPROTONOSUPPORT
|
752
754
|
next # The kernel doesn't support the address family.
|
753
755
|
end
|
754
756
|
@socks << sock
|
@@ -965,7 +967,7 @@ class Resolv
|
|
965
967
|
next unless keyword
|
966
968
|
case keyword
|
967
969
|
when 'nameserver'
|
968
|
-
nameserver
|
970
|
+
nameserver.concat(args)
|
969
971
|
when 'domain'
|
970
972
|
next if args.empty?
|
971
973
|
search = [args[0]]
|
@@ -1004,6 +1006,7 @@ class Resolv
|
|
1004
1006
|
@mutex.synchronize {
|
1005
1007
|
unless @initialized
|
1006
1008
|
@nameserver_port = []
|
1009
|
+
@use_ipv6 = nil
|
1007
1010
|
@search = nil
|
1008
1011
|
@ndots = 1
|
1009
1012
|
case @config_info
|
@@ -1028,8 +1031,12 @@ class Resolv
|
|
1028
1031
|
if config_hash.include? :nameserver_port
|
1029
1032
|
@nameserver_port = config_hash[:nameserver_port].map {|ns, port| [ns, (port || Port)] }
|
1030
1033
|
end
|
1034
|
+
if config_hash.include? :use_ipv6
|
1035
|
+
@use_ipv6 = config_hash[:use_ipv6]
|
1036
|
+
end
|
1031
1037
|
@search = config_hash[:search] if config_hash.include? :search
|
1032
1038
|
@ndots = config_hash[:ndots] if config_hash.include? :ndots
|
1039
|
+
@raise_timeout_errors = config_hash[:raise_timeout_errors]
|
1033
1040
|
|
1034
1041
|
if @nameserver_port.empty?
|
1035
1042
|
@nameserver_port << ['0.0.0.0', Port]
|
@@ -1083,6 +1090,10 @@ class Resolv
|
|
1083
1090
|
@nameserver_port
|
1084
1091
|
end
|
1085
1092
|
|
1093
|
+
def use_ipv6?
|
1094
|
+
@use_ipv6
|
1095
|
+
end
|
1096
|
+
|
1086
1097
|
def generate_candidates(name)
|
1087
1098
|
candidates = nil
|
1088
1099
|
name = Name.create(name)
|
@@ -1116,6 +1127,7 @@ class Resolv
|
|
1116
1127
|
def resolv(name)
|
1117
1128
|
candidates = generate_candidates(name)
|
1118
1129
|
timeouts = @timeouts || generate_timeouts
|
1130
|
+
timeout_error = false
|
1119
1131
|
begin
|
1120
1132
|
candidates.each {|candidate|
|
1121
1133
|
begin
|
@@ -1127,11 +1139,13 @@ class Resolv
|
|
1127
1139
|
end
|
1128
1140
|
}
|
1129
1141
|
}
|
1142
|
+
timeout_error = true
|
1130
1143
|
raise ResolvError.new("DNS resolv timeout: #{name}")
|
1131
1144
|
rescue NXDomain
|
1132
1145
|
end
|
1133
1146
|
}
|
1134
1147
|
rescue ResolvError
|
1148
|
+
raise if @raise_timeout_errors && timeout_error
|
1135
1149
|
end
|
1136
1150
|
end
|
1137
1151
|
|
@@ -1487,14 +1501,14 @@ class Resolv
|
|
1487
1501
|
}
|
1488
1502
|
end
|
1489
1503
|
|
1490
|
-
def put_name(d)
|
1491
|
-
put_labels(d.to_a)
|
1504
|
+
def put_name(d, compress: true)
|
1505
|
+
put_labels(d.to_a, compress: compress)
|
1492
1506
|
end
|
1493
1507
|
|
1494
|
-
def put_labels(d)
|
1508
|
+
def put_labels(d, compress: true)
|
1495
1509
|
d.each_index {|i|
|
1496
1510
|
domain = d[i..-1]
|
1497
|
-
if idx = @names[domain]
|
1511
|
+
if compress && idx = @names[domain]
|
1498
1512
|
self.put_pack("n", 0xc000 | idx)
|
1499
1513
|
return
|
1500
1514
|
else
|
@@ -1518,13 +1532,15 @@ class Resolv
|
|
1518
1532
|
id, flag, qdcount, ancount, nscount, arcount =
|
1519
1533
|
msg.get_unpack('nnnnnn')
|
1520
1534
|
o.id = id
|
1535
|
+
o.tc = (flag >> 9) & 1
|
1536
|
+
o.rcode = flag & 15
|
1537
|
+
return o unless o.tc.zero?
|
1538
|
+
|
1521
1539
|
o.qr = (flag >> 15) & 1
|
1522
1540
|
o.opcode = (flag >> 11) & 15
|
1523
1541
|
o.aa = (flag >> 10) & 1
|
1524
|
-
o.tc = (flag >> 9) & 1
|
1525
1542
|
o.rd = (flag >> 8) & 1
|
1526
1543
|
o.ra = (flag >> 7) & 1
|
1527
|
-
o.rcode = flag & 15
|
1528
1544
|
(1..qdcount).each {
|
1529
1545
|
name, typeclass = msg.get_question
|
1530
1546
|
o.add_question(name, typeclass)
|
@@ -1616,6 +1632,14 @@ class Resolv
|
|
1616
1632
|
strings
|
1617
1633
|
end
|
1618
1634
|
|
1635
|
+
def get_list
|
1636
|
+
[].tap do |values|
|
1637
|
+
while @index < @limit
|
1638
|
+
values << yield
|
1639
|
+
end
|
1640
|
+
end
|
1641
|
+
end
|
1642
|
+
|
1619
1643
|
def get_name
|
1620
1644
|
return Name.new(self.get_labels)
|
1621
1645
|
end
|
@@ -1676,6 +1700,378 @@ class Resolv
|
|
1676
1700
|
end
|
1677
1701
|
end
|
1678
1702
|
|
1703
|
+
##
|
1704
|
+
# SvcParams for service binding RRs. [RFC9460]
|
1705
|
+
|
1706
|
+
class SvcParams
|
1707
|
+
include Enumerable
|
1708
|
+
|
1709
|
+
##
|
1710
|
+
# Create a list of SvcParams with the given initial content.
|
1711
|
+
#
|
1712
|
+
# +params+ has to be an enumerable of +SvcParam+s.
|
1713
|
+
# If its content has +SvcParam+s with the duplicate key,
|
1714
|
+
# the one appears last takes precedence.
|
1715
|
+
|
1716
|
+
def initialize(params = [])
|
1717
|
+
@params = {}
|
1718
|
+
|
1719
|
+
params.each do |param|
|
1720
|
+
add param
|
1721
|
+
end
|
1722
|
+
end
|
1723
|
+
|
1724
|
+
##
|
1725
|
+
# Get SvcParam for the given +key+ in this list.
|
1726
|
+
|
1727
|
+
def [](key)
|
1728
|
+
@params[canonical_key(key)]
|
1729
|
+
end
|
1730
|
+
|
1731
|
+
##
|
1732
|
+
# Get the number of SvcParams in this list.
|
1733
|
+
|
1734
|
+
def count
|
1735
|
+
@params.count
|
1736
|
+
end
|
1737
|
+
|
1738
|
+
##
|
1739
|
+
# Get whether this list is empty.
|
1740
|
+
|
1741
|
+
def empty?
|
1742
|
+
@params.empty?
|
1743
|
+
end
|
1744
|
+
|
1745
|
+
##
|
1746
|
+
# Add the SvcParam +param+ to this list, overwriting the existing one with the same key.
|
1747
|
+
|
1748
|
+
def add(param)
|
1749
|
+
@params[param.class.key_number] = param
|
1750
|
+
end
|
1751
|
+
|
1752
|
+
##
|
1753
|
+
# Remove the +SvcParam+ with the given +key+ and return it.
|
1754
|
+
|
1755
|
+
def delete(key)
|
1756
|
+
@params.delete(canonical_key(key))
|
1757
|
+
end
|
1758
|
+
|
1759
|
+
##
|
1760
|
+
# Enumerate the +SvcParam+s in this list.
|
1761
|
+
|
1762
|
+
def each(&block)
|
1763
|
+
return enum_for(:each) unless block
|
1764
|
+
@params.each_value(&block)
|
1765
|
+
end
|
1766
|
+
|
1767
|
+
def encode(msg) # :nodoc:
|
1768
|
+
@params.keys.sort.each do |key|
|
1769
|
+
msg.put_pack('n', key)
|
1770
|
+
msg.put_length16 do
|
1771
|
+
@params.fetch(key).encode(msg)
|
1772
|
+
end
|
1773
|
+
end
|
1774
|
+
end
|
1775
|
+
|
1776
|
+
def self.decode(msg) # :nodoc:
|
1777
|
+
params = msg.get_list do
|
1778
|
+
key, = msg.get_unpack('n')
|
1779
|
+
msg.get_length16 do
|
1780
|
+
SvcParam::ClassHash[key].decode(msg)
|
1781
|
+
end
|
1782
|
+
end
|
1783
|
+
|
1784
|
+
return self.new(params)
|
1785
|
+
end
|
1786
|
+
|
1787
|
+
private
|
1788
|
+
|
1789
|
+
def canonical_key(key) # :nodoc:
|
1790
|
+
case key
|
1791
|
+
when Integer
|
1792
|
+
key
|
1793
|
+
when /\Akey(\d+)\z/
|
1794
|
+
Integer($1)
|
1795
|
+
when Symbol
|
1796
|
+
SvcParam::ClassHash[key].key_number
|
1797
|
+
else
|
1798
|
+
raise TypeError, 'key must be either String or Symbol'
|
1799
|
+
end
|
1800
|
+
end
|
1801
|
+
end
|
1802
|
+
|
1803
|
+
|
1804
|
+
##
|
1805
|
+
# Base class for SvcParam. [RFC9460]
|
1806
|
+
|
1807
|
+
class SvcParam
|
1808
|
+
|
1809
|
+
##
|
1810
|
+
# Get the presentation name of the SvcParamKey.
|
1811
|
+
|
1812
|
+
def self.key_name
|
1813
|
+
const_get(:KeyName)
|
1814
|
+
end
|
1815
|
+
|
1816
|
+
##
|
1817
|
+
# Get the registered number of the SvcParamKey.
|
1818
|
+
|
1819
|
+
def self.key_number
|
1820
|
+
const_get(:KeyNumber)
|
1821
|
+
end
|
1822
|
+
|
1823
|
+
ClassHash = Hash.new do |h, key| # :nodoc:
|
1824
|
+
case key
|
1825
|
+
when Integer
|
1826
|
+
Generic.create(key)
|
1827
|
+
when /\Akey(?<key>\d+)\z/
|
1828
|
+
Generic.create(key.to_int)
|
1829
|
+
when Symbol
|
1830
|
+
raise KeyError, "unknown key #{key}"
|
1831
|
+
else
|
1832
|
+
raise TypeError, 'key must be either String or Symbol'
|
1833
|
+
end
|
1834
|
+
end
|
1835
|
+
|
1836
|
+
##
|
1837
|
+
# Generic SvcParam abstract class.
|
1838
|
+
|
1839
|
+
class Generic < SvcParam
|
1840
|
+
|
1841
|
+
##
|
1842
|
+
# SvcParamValue in wire-format byte string.
|
1843
|
+
|
1844
|
+
attr_reader :value
|
1845
|
+
|
1846
|
+
##
|
1847
|
+
# Create generic SvcParam
|
1848
|
+
|
1849
|
+
def initialize(value)
|
1850
|
+
@value = value
|
1851
|
+
end
|
1852
|
+
|
1853
|
+
def encode(msg) # :nodoc:
|
1854
|
+
msg.put_bytes(@value)
|
1855
|
+
end
|
1856
|
+
|
1857
|
+
def self.decode(msg) # :nodoc:
|
1858
|
+
return self.new(msg.get_bytes)
|
1859
|
+
end
|
1860
|
+
|
1861
|
+
def self.create(key_number)
|
1862
|
+
c = Class.new(Generic)
|
1863
|
+
key_name = :"key#{key_number}"
|
1864
|
+
c.const_set(:KeyName, key_name)
|
1865
|
+
c.const_set(:KeyNumber, key_number)
|
1866
|
+
self.const_set(:"Key#{key_number}", c)
|
1867
|
+
ClassHash[key_name] = ClassHash[key_number] = c
|
1868
|
+
return c
|
1869
|
+
end
|
1870
|
+
end
|
1871
|
+
|
1872
|
+
##
|
1873
|
+
# "mandatory" SvcParam -- Mandatory keys in service binding RR
|
1874
|
+
|
1875
|
+
class Mandatory < SvcParam
|
1876
|
+
KeyName = :mandatory
|
1877
|
+
KeyNumber = 0
|
1878
|
+
ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
|
1879
|
+
|
1880
|
+
##
|
1881
|
+
# Mandatory keys.
|
1882
|
+
|
1883
|
+
attr_reader :keys
|
1884
|
+
|
1885
|
+
##
|
1886
|
+
# Initialize "mandatory" ScvParam.
|
1887
|
+
|
1888
|
+
def initialize(keys)
|
1889
|
+
@keys = keys.map(&:to_int)
|
1890
|
+
end
|
1891
|
+
|
1892
|
+
def encode(msg) # :nodoc:
|
1893
|
+
@keys.sort.each do |key|
|
1894
|
+
msg.put_pack('n', key)
|
1895
|
+
end
|
1896
|
+
end
|
1897
|
+
|
1898
|
+
def self.decode(msg) # :nodoc:
|
1899
|
+
keys = msg.get_list { msg.get_unpack('n')[0] }
|
1900
|
+
return self.new(keys)
|
1901
|
+
end
|
1902
|
+
end
|
1903
|
+
|
1904
|
+
##
|
1905
|
+
# "alpn" SvcParam -- Additional supported protocols
|
1906
|
+
|
1907
|
+
class ALPN < SvcParam
|
1908
|
+
KeyName = :alpn
|
1909
|
+
KeyNumber = 1
|
1910
|
+
ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
|
1911
|
+
|
1912
|
+
##
|
1913
|
+
# Supported protocol IDs.
|
1914
|
+
|
1915
|
+
attr_reader :protocol_ids
|
1916
|
+
|
1917
|
+
##
|
1918
|
+
# Initialize "alpn" ScvParam.
|
1919
|
+
|
1920
|
+
def initialize(protocol_ids)
|
1921
|
+
@protocol_ids = protocol_ids.map(&:to_str)
|
1922
|
+
end
|
1923
|
+
|
1924
|
+
def encode(msg) # :nodoc:
|
1925
|
+
msg.put_string_list(@protocol_ids)
|
1926
|
+
end
|
1927
|
+
|
1928
|
+
def self.decode(msg) # :nodoc:
|
1929
|
+
return self.new(msg.get_string_list)
|
1930
|
+
end
|
1931
|
+
end
|
1932
|
+
|
1933
|
+
##
|
1934
|
+
# "no-default-alpn" SvcParam -- No support for default protocol
|
1935
|
+
|
1936
|
+
class NoDefaultALPN < SvcParam
|
1937
|
+
KeyName = :'no-default-alpn'
|
1938
|
+
KeyNumber = 2
|
1939
|
+
ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
|
1940
|
+
|
1941
|
+
def encode(msg) # :nodoc:
|
1942
|
+
# no payload
|
1943
|
+
end
|
1944
|
+
|
1945
|
+
def self.decode(msg) # :nodoc:
|
1946
|
+
return self.new
|
1947
|
+
end
|
1948
|
+
end
|
1949
|
+
|
1950
|
+
##
|
1951
|
+
# "port" SvcParam -- Port for alternative endpoint
|
1952
|
+
|
1953
|
+
class Port < SvcParam
|
1954
|
+
KeyName = :port
|
1955
|
+
KeyNumber = 3
|
1956
|
+
ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
|
1957
|
+
|
1958
|
+
##
|
1959
|
+
# Port number.
|
1960
|
+
|
1961
|
+
attr_reader :port
|
1962
|
+
|
1963
|
+
##
|
1964
|
+
# Initialize "port" ScvParam.
|
1965
|
+
|
1966
|
+
def initialize(port)
|
1967
|
+
@port = port.to_int
|
1968
|
+
end
|
1969
|
+
|
1970
|
+
def encode(msg) # :nodoc:
|
1971
|
+
msg.put_pack('n', @port)
|
1972
|
+
end
|
1973
|
+
|
1974
|
+
def self.decode(msg) # :nodoc:
|
1975
|
+
port, = msg.get_unpack('n')
|
1976
|
+
return self.new(port)
|
1977
|
+
end
|
1978
|
+
end
|
1979
|
+
|
1980
|
+
##
|
1981
|
+
# "ipv4hint" SvcParam -- IPv4 address hints
|
1982
|
+
|
1983
|
+
class IPv4Hint < SvcParam
|
1984
|
+
KeyName = :ipv4hint
|
1985
|
+
KeyNumber = 4
|
1986
|
+
ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
|
1987
|
+
|
1988
|
+
##
|
1989
|
+
# Set of IPv4 addresses.
|
1990
|
+
|
1991
|
+
attr_reader :addresses
|
1992
|
+
|
1993
|
+
##
|
1994
|
+
# Initialize "ipv4hint" ScvParam.
|
1995
|
+
|
1996
|
+
def initialize(addresses)
|
1997
|
+
@addresses = addresses.map {|address| IPv4.create(address) }
|
1998
|
+
end
|
1999
|
+
|
2000
|
+
def encode(msg) # :nodoc:
|
2001
|
+
@addresses.each do |address|
|
2002
|
+
msg.put_bytes(address.address)
|
2003
|
+
end
|
2004
|
+
end
|
2005
|
+
|
2006
|
+
def self.decode(msg) # :nodoc:
|
2007
|
+
addresses = msg.get_list { IPv4.new(msg.get_bytes(4)) }
|
2008
|
+
return self.new(addresses)
|
2009
|
+
end
|
2010
|
+
end
|
2011
|
+
|
2012
|
+
##
|
2013
|
+
# "ipv6hint" SvcParam -- IPv6 address hints
|
2014
|
+
|
2015
|
+
class IPv6Hint < SvcParam
|
2016
|
+
KeyName = :ipv6hint
|
2017
|
+
KeyNumber = 6
|
2018
|
+
ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
|
2019
|
+
|
2020
|
+
##
|
2021
|
+
# Set of IPv6 addresses.
|
2022
|
+
|
2023
|
+
attr_reader :addresses
|
2024
|
+
|
2025
|
+
##
|
2026
|
+
# Initialize "ipv6hint" ScvParam.
|
2027
|
+
|
2028
|
+
def initialize(addresses)
|
2029
|
+
@addresses = addresses.map {|address| IPv6.create(address) }
|
2030
|
+
end
|
2031
|
+
|
2032
|
+
def encode(msg) # :nodoc:
|
2033
|
+
@addresses.each do |address|
|
2034
|
+
msg.put_bytes(address.address)
|
2035
|
+
end
|
2036
|
+
end
|
2037
|
+
|
2038
|
+
def self.decode(msg) # :nodoc:
|
2039
|
+
addresses = msg.get_list { IPv6.new(msg.get_bytes(16)) }
|
2040
|
+
return self.new(addresses)
|
2041
|
+
end
|
2042
|
+
end
|
2043
|
+
|
2044
|
+
##
|
2045
|
+
# "dohpath" SvcParam -- DNS over HTTPS path template [RFC9461]
|
2046
|
+
|
2047
|
+
class DoHPath < SvcParam
|
2048
|
+
KeyName = :dohpath
|
2049
|
+
KeyNumber = 7
|
2050
|
+
ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
|
2051
|
+
|
2052
|
+
##
|
2053
|
+
# URI template for DoH queries.
|
2054
|
+
|
2055
|
+
attr_reader :template
|
2056
|
+
|
2057
|
+
##
|
2058
|
+
# Initialize "dohpath" ScvParam.
|
2059
|
+
|
2060
|
+
def initialize(template)
|
2061
|
+
@template = template.encode('utf-8')
|
2062
|
+
end
|
2063
|
+
|
2064
|
+
def encode(msg) # :nodoc:
|
2065
|
+
msg.put_bytes(@template)
|
2066
|
+
end
|
2067
|
+
|
2068
|
+
def self.decode(msg) # :nodoc:
|
2069
|
+
template = msg.get_bytes.force_encoding('utf-8')
|
2070
|
+
return self.new(template)
|
2071
|
+
end
|
2072
|
+
end
|
2073
|
+
end
|
2074
|
+
|
1679
2075
|
##
|
1680
2076
|
# A DNS query abstract class.
|
1681
2077
|
|
@@ -2141,8 +2537,70 @@ class Resolv
|
|
2141
2537
|
TypeValue = 255 # :nodoc:
|
2142
2538
|
end
|
2143
2539
|
|
2540
|
+
##
|
2541
|
+
# CAA resource record defined in RFC 8659
|
2542
|
+
#
|
2543
|
+
# These records identify certificate authority allowed to issue
|
2544
|
+
# certificates for the given domain.
|
2545
|
+
|
2546
|
+
class CAA < Resource
|
2547
|
+
TypeValue = 257
|
2548
|
+
|
2549
|
+
##
|
2550
|
+
# Creates a new CAA for +flags+, +tag+ and +value+.
|
2551
|
+
|
2552
|
+
def initialize(flags, tag, value)
|
2553
|
+
unless (0..255) === flags
|
2554
|
+
raise ArgumentError.new('flags must be an Integer between 0 and 255')
|
2555
|
+
end
|
2556
|
+
unless (1..15) === tag.bytesize
|
2557
|
+
raise ArgumentError.new('length of tag must be between 1 and 15')
|
2558
|
+
end
|
2559
|
+
|
2560
|
+
@flags = flags
|
2561
|
+
@tag = tag
|
2562
|
+
@value = value
|
2563
|
+
end
|
2564
|
+
|
2565
|
+
##
|
2566
|
+
# Flags for this proprty:
|
2567
|
+
# - Bit 0 : 0 = not critical, 1 = critical
|
2568
|
+
|
2569
|
+
attr_reader :flags
|
2570
|
+
|
2571
|
+
##
|
2572
|
+
# Property tag ("issue", "issuewild", "iodef"...).
|
2573
|
+
|
2574
|
+
attr_reader :tag
|
2575
|
+
|
2576
|
+
##
|
2577
|
+
# Property value.
|
2578
|
+
|
2579
|
+
attr_reader :value
|
2580
|
+
|
2581
|
+
##
|
2582
|
+
# Whether the critical flag is set on this property.
|
2583
|
+
|
2584
|
+
def critical?
|
2585
|
+
flags & 0x80 != 0
|
2586
|
+
end
|
2587
|
+
|
2588
|
+
def encode_rdata(msg) # :nodoc:
|
2589
|
+
msg.put_pack('C', @flags)
|
2590
|
+
msg.put_string(@tag)
|
2591
|
+
msg.put_bytes(@value)
|
2592
|
+
end
|
2593
|
+
|
2594
|
+
def self.decode_rdata(msg) # :nodoc:
|
2595
|
+
flags, = msg.get_unpack('C')
|
2596
|
+
tag = msg.get_string
|
2597
|
+
value = msg.get_bytes
|
2598
|
+
self.new flags, tag, value
|
2599
|
+
end
|
2600
|
+
end
|
2601
|
+
|
2144
2602
|
ClassInsensitiveTypes = [ # :nodoc:
|
2145
|
-
NS, CNAME, SOA, PTR, HINFO, MINFO, MX, TXT, LOC, ANY
|
2603
|
+
NS, CNAME, SOA, PTR, HINFO, MINFO, MX, TXT, LOC, ANY, CAA
|
2146
2604
|
]
|
2147
2605
|
|
2148
2606
|
##
|
@@ -2328,7 +2786,7 @@ class Resolv
|
|
2328
2786
|
msg.put_pack("n", @priority)
|
2329
2787
|
msg.put_pack("n", @weight)
|
2330
2788
|
msg.put_pack("n", @port)
|
2331
|
-
msg.put_name(@target)
|
2789
|
+
msg.put_name(@target, compress: false)
|
2332
2790
|
end
|
2333
2791
|
|
2334
2792
|
def self.decode_rdata(msg) # :nodoc:
|
@@ -2339,6 +2797,84 @@ class Resolv
|
|
2339
2797
|
return self.new(priority, weight, port, target)
|
2340
2798
|
end
|
2341
2799
|
end
|
2800
|
+
|
2801
|
+
##
|
2802
|
+
# Common implementation for SVCB-compatible resource records.
|
2803
|
+
|
2804
|
+
class ServiceBinding
|
2805
|
+
|
2806
|
+
##
|
2807
|
+
# Create a service binding resource record.
|
2808
|
+
|
2809
|
+
def initialize(priority, target, params = [])
|
2810
|
+
@priority = priority.to_int
|
2811
|
+
@target = Name.create(target)
|
2812
|
+
@params = SvcParams.new(params)
|
2813
|
+
end
|
2814
|
+
|
2815
|
+
##
|
2816
|
+
# The priority of this target host.
|
2817
|
+
#
|
2818
|
+
# The range is 0-65535.
|
2819
|
+
# If set to 0, this RR is in AliasMode. Otherwise, it is in ServiceMode.
|
2820
|
+
|
2821
|
+
attr_reader :priority
|
2822
|
+
|
2823
|
+
##
|
2824
|
+
# The domain name of the target host.
|
2825
|
+
|
2826
|
+
attr_reader :target
|
2827
|
+
|
2828
|
+
##
|
2829
|
+
# The service parameters for the target host.
|
2830
|
+
|
2831
|
+
attr_reader :params
|
2832
|
+
|
2833
|
+
##
|
2834
|
+
# Whether this RR is in AliasMode.
|
2835
|
+
|
2836
|
+
def alias_mode?
|
2837
|
+
self.priority == 0
|
2838
|
+
end
|
2839
|
+
|
2840
|
+
##
|
2841
|
+
# Whether this RR is in ServiceMode.
|
2842
|
+
|
2843
|
+
def service_mode?
|
2844
|
+
!alias_mode?
|
2845
|
+
end
|
2846
|
+
|
2847
|
+
def encode_rdata(msg) # :nodoc:
|
2848
|
+
msg.put_pack("n", @priority)
|
2849
|
+
msg.put_name(@target, compress: false)
|
2850
|
+
@params.encode(msg)
|
2851
|
+
end
|
2852
|
+
|
2853
|
+
def self.decode_rdata(msg) # :nodoc:
|
2854
|
+
priority, = msg.get_unpack("n")
|
2855
|
+
target = msg.get_name
|
2856
|
+
params = SvcParams.decode(msg)
|
2857
|
+
return self.new(priority, target, params)
|
2858
|
+
end
|
2859
|
+
end
|
2860
|
+
|
2861
|
+
##
|
2862
|
+
# SVCB resource record [RFC9460]
|
2863
|
+
|
2864
|
+
class SVCB < ServiceBinding
|
2865
|
+
TypeValue = 64
|
2866
|
+
ClassValue = IN::ClassValue
|
2867
|
+
ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
|
2868
|
+
end
|
2869
|
+
|
2870
|
+
##
|
2871
|
+
# HTTPS resource record [RFC9460]
|
2872
|
+
|
2873
|
+
class HTTPS < ServiceBinding
|
2874
|
+
TypeValue = 65
|
2875
|
+
ClassValue = IN::ClassValue
|
2876
|
+
ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
|
2877
|
+
end
|
2342
2878
|
end
|
2343
2879
|
end
|
2344
2880
|
end
|
@@ -2558,11 +3094,7 @@ class Resolv
|
|
2558
3094
|
attr_reader :address
|
2559
3095
|
|
2560
3096
|
def to_s # :nodoc:
|
2561
|
-
|
2562
|
-
unless address.sub!(/(^|:)0(:0)+(:|$)/, '::')
|
2563
|
-
address.sub!(/(^|:)0(:|$)/, '::')
|
2564
|
-
end
|
2565
|
-
return address
|
3097
|
+
sprintf("%x:%x:%x:%x:%x:%x:%x:%x", *@address.unpack("nnnnnnnn")).sub(/(^|:)0(:0)+(:|$)/, '::')
|
2566
3098
|
end
|
2567
3099
|
|
2568
3100
|
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,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resolv
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tanaka Akira
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2024-03-19 00:00:00.000000000 Z
|
12
11
|
dependencies: []
|
13
12
|
description: Thread-aware DNS resolver library in Ruby.
|
14
13
|
email:
|
@@ -35,7 +34,6 @@ licenses:
|
|
35
34
|
metadata:
|
36
35
|
homepage_uri: https://github.com/ruby/resolv
|
37
36
|
source_code_uri: https://github.com/ruby/resolv
|
38
|
-
post_install_message:
|
39
37
|
rdoc_options: []
|
40
38
|
require_paths:
|
41
39
|
- lib
|
@@ -50,8 +48,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
50
48
|
- !ruby/object:Gem::Version
|
51
49
|
version: '0'
|
52
50
|
requirements: []
|
53
|
-
rubygems_version: 3.
|
54
|
-
signing_key:
|
51
|
+
rubygems_version: 3.6.0.dev
|
55
52
|
specification_version: 4
|
56
53
|
summary: Thread-aware DNS resolver library in Ruby.
|
57
54
|
test_files: []
|