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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: be8cc01e584a5861d458d566337d00aeafab62860c2170612c1df385df352f0d
4
- data.tar.gz: 4810a106bcf7e982c68c9e8a6a1ff19a5614deacbdabd6859b0d2078fba48c0d
3
+ metadata.gz: 56680c64e918a60193f43e3ca4a7093db20bbe66f516da79a159d9a3c05860d8
4
+ data.tar.gz: f017a2c89fbcdf946b69ce57d41a67537b73f54ec6e99d18cc62865fd6ec1633
5
5
  SHA512:
6
- metadata.gz: 5a8d2b5467bc3af582454742074a91f2d43dbeac233cf33c299541e8c208ff0ef31abe694e7d8b70d6aaf14793df90c7e6fbf976ddc3a989d627f12d59d7c1a7
7
- data.tar.gz: 2898499c18aae2c0f5700084e82cff594c60920e6d5a3782470e18a3001d101d602810b5aa423067be46099d05f144bf508c0d18ce5c7ef566c3f038906e3823
6
+ metadata.gz: d57f866a30bc2b8da6dbcdb8c65d0a5db4630af4ec0244aa490e37370ba7b27c3031e153e76055e7f4f63114bd477944326b05dbb7319f97bf59478f8518d76a
7
+ data.tar.gz: f07e85287b3bb9973c90900c1fbdc272e193d91493e63b407e696cc9f52de29fc0716f2ec11aba56d37d89a12b6d3fec55d21bd8ce756f02d56577940c454b06
@@ -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: [ '3.0', 2.7, 2.6, 2.5, head ]
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@v3
24
+ - uses: actions/checkout@v4
15
25
  - name: Set up Ruby
16
26
  uses: ruby/setup-ruby@v1
17
27
  with:
data/.gitignore CHANGED
@@ -6,3 +6,5 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+
10
+ Gemfile.lock
data/Gemfile CHANGED
@@ -1,4 +1,7 @@
1
1
  source "https://rubygems.org"
2
2
 
3
+ gemspec
4
+
3
5
  gem "rake"
4
6
  gem "test-unit"
7
+ gem "test-unit-ruby-core"
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=[Hosts.new, DNS.new])
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, hostname, *aliases = line.split(/\s+/)
197
+ addr, *hostnames = line.split(/\s+/)
196
198
  next unless addr
197
- @addr2name[addr] = [] unless @addr2name.include? addr
198
- @addr2name[addr] << hostname
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 += args
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
- address = sprintf("%x:%x:%x:%x:%x:%x:%x:%x", *@address.unpack("nnnnnnnn"))
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 = "resolv"
3
- spec.version = "0.2.2"
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.2.2
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: 2022-12-05 00:00:00.000000000 Z
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.4.0.dev
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: []