resolv 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 69d284d2efb6b6833a61ba3737c584fc67b18550e2e0f50b18f9125c259e6464
4
- data.tar.gz: 8081d3d7396a29a6d359df59b6c52d25063074e1d5af285b333bc22dc59432ef
3
+ metadata.gz: 82cf1416dbd548bac4a243078cb8b93a0990f906169eff49752840d3145d0a0a
4
+ data.tar.gz: baf98405285dd3a553873a2f10e20d188e1e73cf066bf5f430b8d206c553978f
5
5
  SHA512:
6
- metadata.gz: 3b0fe1a7fa99005ce6771c792219b567e968b3ba3a40c63ae70eeabbc5766479e0577edfd6b4f8316f06c3d73a910423eebb29c32062addff564bcc1b905b501
7
- data.tar.gz: 87b7cadc26a2f2bb95e78af53b3556de81ebd0b8890827b1c4b31de1780389a8b3f8d8780fdb991f79700977edd728e08d4ef65b32f7d130b903194dee209577
6
+ metadata.gz: 646bccbb40a992224d079ddd8a586a231a69f67f628d424f79e1079dce4fcc346254ec0288d874ec356e199cfffb5c694b0cea8b51ed2435299a72ec29d88759
7
+ data.tar.gz: 28f34daddd2b5e3262e1b6b4381933c40683c8938129efad0914d495ae522f0c0b53bcbd6108073c1b3b1b4beb7f67d5e8af4b2af17387812c7275bed11c696d
data/lib/resolv.rb CHANGED
@@ -37,7 +37,7 @@ end
37
37
 
38
38
  class Resolv
39
39
 
40
- VERSION = "0.3.1"
40
+ VERSION = "0.3.2"
41
41
 
42
42
  ##
43
43
  # Looks up the first IP address for +name+.
@@ -1219,6 +1219,13 @@ class Resolv
1219
1219
 
1220
1220
  class Str # :nodoc:
1221
1221
  def initialize(string)
1222
+ # A label is limited to 63 octets. [RFC 1035 2.3.4] Checking it here
1223
+ # makes it an invariant of the object: every label, however it was
1224
+ # built, fits in its length octet and cannot wrap it. Callers turn
1225
+ # this into the error their own contract promises.
1226
+ if string.bytesize > 63
1227
+ raise ArgumentError, "DNS label is too long (#{string.bytesize} bytes, max 63): #{string.inspect}"
1228
+ end
1222
1229
  @string = string
1223
1230
  # case insensivity of DNS labels doesn't apply non-ASCII characters. [RFC 4343]
1224
1231
  # This assumes @string is given in ASCII compatible encoding.
@@ -1264,7 +1271,26 @@ class Resolv
1264
1271
  when Name
1265
1272
  return arg
1266
1273
  when String
1267
- return Name.new(Label.split(arg), /\.\z/ =~ arg ? true : false)
1274
+ # A hostname is runtime data rather than a programming mistake, so
1275
+ # both size limits surface as ResolvError to stay rescuable alongside
1276
+ # the rest of name resolution. The type check below is a caller
1277
+ # mistake and keeps raising ArgumentError.
1278
+ begin
1279
+ labels = Label.split(arg)
1280
+ rescue ArgumentError => e
1281
+ raise ResolvError.new(e.message)
1282
+ end
1283
+ # Label::Str enforces the per-label limit. Only the total is knowable
1284
+ # here, and it counts the encoded form, so size starts at 1 for the
1285
+ # root label's terminating zero octet. [RFC 1035 2.3.4, 3.1]
1286
+ size = 1
1287
+ labels.each do |label|
1288
+ size += 1 + label.string.bytesize
1289
+ if size > 255
1290
+ raise ResolvError.new("DNS name is too long (#{size} octets, max 255): #{arg.inspect}")
1291
+ end
1292
+ end
1293
+ return Name.new(labels, /\.\z/ =~ arg ? true : false)
1268
1294
  else
1269
1295
  raise ArgumentError.new("cannot interpret as DNS name: #{arg.inspect}")
1270
1296
  end
@@ -1386,12 +1412,24 @@ class Resolv
1386
1412
  @rd == other.rd &&
1387
1413
  @ra == other.ra &&
1388
1414
  @rcode == other.rcode &&
1389
- @question == other.question &&
1415
+ question_equal?(other.question) &&
1390
1416
  @answer == other.answer &&
1391
1417
  @authority == other.authority &&
1392
1418
  @additional == other.additional
1393
1419
  end
1394
1420
 
1421
+ # A question holds the resource class itself, and decoding creates a fresh
1422
+ # class for each unknown type, so the classes cannot be compared by
1423
+ # identity alone.
1424
+ private def question_equal?(other_question) # :nodoc:
1425
+ return false unless @question.length == other_question.length
1426
+ @question.zip(other_question) {|(name, typeclass), (o_name, o_typeclass)|
1427
+ return false unless name == o_name &&
1428
+ Resource::Generic.type_class_equal?(typeclass, o_typeclass)
1429
+ }
1430
+ return true
1431
+ end
1432
+
1395
1433
  def add_question(name, typeclass)
1396
1434
  @question << [Name.create(name), typeclass]
1397
1435
  end
@@ -1498,8 +1536,15 @@ class Resolv
1498
1536
  end
1499
1537
 
1500
1538
  def put_string(d)
1501
- self.put_pack("C", d.length)
1502
- @data << d
1539
+ s = d.to_s
1540
+ # A character-string is prefixed by a single length octet, so it can
1541
+ # hold at most 255 octets. [RFC 1035 3.3] Reject anything longer to
1542
+ # avoid silently truncating the length to its low 8 bits (mod 256).
1543
+ if s.bytesize > 255
1544
+ raise ArgumentError, "character-string is too long (#{s.bytesize} bytes, max 255): #{s.inspect}"
1545
+ end
1546
+ self.put_pack("C", s.bytesize)
1547
+ @data << s
1503
1548
  end
1504
1549
 
1505
1550
  def put_string_list(ds)
@@ -1529,7 +1574,17 @@ class Resolv
1529
1574
  end
1530
1575
 
1531
1576
  def put_label(d)
1532
- self.put_string(d.to_s)
1577
+ s = d.to_s
1578
+ # Label::Str applies this limit when a label is built, so what is left
1579
+ # for here is a raw string handed straight to put_labels. The two ways
1580
+ # an over-long label goes wrong differ: 64 to 255 octets write a length
1581
+ # octet in the reserved or compression pointer range, and 256 or more
1582
+ # wrap it mod 256. Either way the encoded name stops being the name the
1583
+ # caller asked for. [RFC 1035 2.3.4, 4.1.4]
1584
+ if s.bytesize > 63
1585
+ raise ArgumentError, "DNS label is too long (#{s.bytesize} bytes, max 63): #{s.inspect}"
1586
+ end
1587
+ self.put_string(s)
1533
1588
  end
1534
1589
  end
1535
1590
 
@@ -1655,7 +1710,9 @@ class Resolv
1655
1710
  prev_index = @index
1656
1711
  save_index = nil
1657
1712
  d = []
1658
- size = -1
1713
+ # size counts the encoded form, so it starts at 1 for the root
1714
+ # label's terminating zero octet. [RFC 1035 3.1]
1715
+ size = 1
1659
1716
  while true
1660
1717
  raise DecodeError.new("limit exceeded") if @limit <= @index
1661
1718
  case @data.getbyte(@index)
@@ -1686,6 +1743,11 @@ class Resolv
1686
1743
 
1687
1744
  def get_label
1688
1745
  return Label::Str.new(self.get_string)
1746
+ rescue ArgumentError => e
1747
+ # A length octet of 64..191 is reserved rather than a label length,
1748
+ # but this decoder used to read it as one. [RFC 1035 4.1.4] Report it
1749
+ # the way the rest of a malformed message is reported.
1750
+ raise DecodeError.new(e.message)
1689
1751
  end
1690
1752
 
1691
1753
  def get_question
@@ -1874,8 +1936,9 @@ class Resolv
1874
1936
  key_name = :"key#{key_number}"
1875
1937
  c.const_set(:KeyName, key_name)
1876
1938
  c.const_set(:KeyNumber, key_number)
1877
- self.const_set(:"Key#{key_number}", c)
1878
- ClassHash[key_name] = ClassHash[key_number] = c
1939
+ # Not registered in a constant or in ClassHash. ClassHash creates a
1940
+ # class for every unknown SvcParamKey, so registering them
1941
+ # permanently would let a malicious response exhaust memory.
1879
1942
  return c
1880
1943
  end
1881
1944
  end
@@ -2173,12 +2236,28 @@ class Resolv
2173
2236
  return self.new(msg.get_bytes)
2174
2237
  end
2175
2238
 
2239
+ # create makes a fresh class for each decoded resource, so the type and
2240
+ # class values have to be compared instead of the class itself.
2241
+ def self.type_class_equal?(klass, other) # :nodoc:
2242
+ return true if klass.equal?(other)
2243
+ Generic > klass && Generic > other &&
2244
+ klass::TypeValue == other::TypeValue &&
2245
+ klass::ClassValue == other::ClassValue
2246
+ end
2247
+
2248
+ def ==(other) # :nodoc:
2249
+ return other.is_a?(Generic) &&
2250
+ Generic.type_class_equal?(self.class, other.class) &&
2251
+ @data == other.data
2252
+ end
2253
+
2176
2254
  def self.create(type_value, class_value) # :nodoc:
2177
2255
  c = Class.new(Generic)
2178
2256
  c.const_set(:TypeValue, type_value)
2179
2257
  c.const_set(:ClassValue, class_value)
2180
- Generic.const_set("Type#{type_value}_Class#{class_value}", c)
2181
- ClassHash[[type_value, class_value]] = c
2258
+ # Not registered in a constant or in ClassHash. get_class creates a
2259
+ # class for every unknown (type, class) pair, so registering them
2260
+ # permanently would let a malicious response exhaust memory.
2182
2261
  return c
2183
2262
  end
2184
2263
  end
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.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tanaka Akira
@@ -16,17 +16,9 @@ executables: []
16
16
  extensions: []
17
17
  extra_rdoc_files: []
18
18
  files:
19
- - ".github/dependabot.yml"
20
- - ".github/workflows/test.yml"
21
- - ".gitignore"
22
- - Gemfile
23
19
  - LICENSE.txt
24
20
  - README.md
25
- - Rakefile
26
- - bin/console
27
- - bin/setup
28
21
  - lib/resolv.rb
29
- - resolv.gemspec
30
22
  homepage: https://github.com/ruby/resolv
31
23
  licenses:
32
24
  - Ruby
@@ -48,7 +40,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
48
40
  - !ruby/object:Gem::Version
49
41
  version: '0'
50
42
  requirements: []
51
- rubygems_version: 3.7.0.dev
43
+ rubygems_version: 3.6.9
52
44
  specification_version: 4
53
45
  summary: Thread-aware DNS resolver library in Ruby.
54
46
  test_files: []
@@ -1,6 +0,0 @@
1
- version: 2
2
- updates:
3
- - package-ecosystem: 'github-actions'
4
- directory: '/'
5
- schedule:
6
- interval: 'weekly'
@@ -1,28 +0,0 @@
1
- name: test
2
-
3
- on: [push, pull_request]
4
-
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
-
12
- build:
13
- needs: ruby-versions
14
- name: build (${{ matrix.ruby }} / ${{ matrix.os }})
15
- strategy:
16
- matrix:
17
- ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }}
18
- os: [ ubuntu-latest, macos-latest ]
19
- runs-on: ${{ matrix.os }}
20
- steps:
21
- - uses: actions/checkout@v4
22
- - name: Set up Ruby
23
- uses: ruby/setup-ruby@v1
24
- with:
25
- ruby-version: ${{ matrix.ruby }}
26
- bundler-cache: true
27
- - name: Run test
28
- run: bundle exec rake test
data/.gitignore DELETED
@@ -1,10 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
- /tmp/
9
-
10
- Gemfile.lock
data/Gemfile DELETED
@@ -1,7 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gemspec
4
-
5
- gem "rake"
6
- gem "test-unit"
7
- gem "test-unit-ruby-core"
data/Rakefile DELETED
@@ -1,10 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
3
-
4
- Rake::TestTask.new(:test) do |t|
5
- t.libs << "test/lib"
6
- t.ruby_opts << "-rhelper"
7
- t.test_files = FileList["test/**/test_*.rb"]
8
- end
9
-
10
- task :default => :test
data/bin/console DELETED
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "resolv"
5
-
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require "irb"
14
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
data/resolv.gemspec DELETED
@@ -1,29 +0,0 @@
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
-
8
- Gem::Specification.new do |spec|
9
- spec.name = name
10
- spec.version = version
11
- spec.authors = ["Tanaka Akira"]
12
- spec.email = ["akr@fsij.org"]
13
-
14
- spec.summary = %q{Thread-aware DNS resolver library in Ruby.}
15
- spec.description = %q{Thread-aware DNS resolver library in Ruby.}
16
- spec.homepage = "https://github.com/ruby/resolv"
17
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
18
- spec.licenses = ["Ruby", "BSD-2-Clause"]
19
-
20
- spec.metadata["homepage_uri"] = spec.homepage
21
- spec.metadata["source_code_uri"] = spec.homepage
22
-
23
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
24
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
- end
26
- spec.bindir = "exe"
27
- spec.executables = []
28
- spec.require_paths = ["lib"]
29
- end