svcb_rr_patch 0.0.1
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 +7 -0
- data/.github/workflows/ci.yml +29 -0
- data/.gitignore +17 -0
- data/.rubocop.yml +27 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +10 -0
- data/lib/svcb_rr_patch.rb +8 -0
- data/lib/svcb_rr_patch/https.rb +28 -0
- data/lib/svcb_rr_patch/svc_params.rb +96 -0
- data/lib/svcb_rr_patch/svc_params/alpn.rb +38 -0
- data/lib/svcb_rr_patch/svc_params/ech.rb +66 -0
- data/lib/svcb_rr_patch/svc_params/ech/echconfig_contents.rb +74 -0
- data/lib/svcb_rr_patch/svc_params/ech/echconfig_contents/extension.rb +34 -0
- data/lib/svcb_rr_patch/svc_params/ech/echconfig_contents/hpke_key_config.rb +77 -0
- data/lib/svcb_rr_patch/svc_params/ech/echconfig_contents/hpke_key_config/hpke_kem_id.rb +22 -0
- data/lib/svcb_rr_patch/svc_params/ech/echconfig_contents/hpke_key_config/hpke_public_key.rb +20 -0
- data/lib/svcb_rr_patch/svc_params/ech/echconfig_contents/hpke_key_config/hpke_symmetric_cipher_suite.rb +41 -0
- data/lib/svcb_rr_patch/svc_params/ech/echconfig_contents/hpke_key_config/hpke_symmetric_cipher_suite/hpke_aead_id.rb +22 -0
- data/lib/svcb_rr_patch/svc_params/ech/echconfig_contents/hpke_key_config/hpke_symmetric_cipher_suite/hpke_kdf_id.rb +22 -0
- data/lib/svcb_rr_patch/svc_params/ipv4hint.rb +21 -0
- data/lib/svcb_rr_patch/svc_params/ipv6hint.rb +21 -0
- data/lib/svcb_rr_patch/svc_params/mandatory.rb +21 -0
- data/lib/svcb_rr_patch/svc_params/no_default_alpn.rb +10 -0
- data/lib/svcb_rr_patch/svc_params/port.rb +21 -0
- data/lib/svcb_rr_patch/svcb.rb +55 -0
- data/lib/svcb_rr_patch/version.rb +5 -0
- data/spec/alpn_spec.rb +30 -0
- data/spec/ipv4hint_spec.rb +38 -0
- data/spec/ipv6hint_spec.rb +33 -0
- data/spec/mandatory_spec.rb +39 -0
- data/spec/no_default_alpn_spec.rb +35 -0
- data/spec/port_spec.rb +30 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/svc_params_spec.rb +75 -0
- data/svcb_rr_patch.gemspec +24 -0
- metadata +101 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: fe01826cf1fe78d4b2d6db7d53bb474234121dceba9f7cd8dc26b25ca2d3e00a
|
|
4
|
+
data.tar.gz: 7acaffe658b58b44b2d22c66fafe904acacf15d1b68a5a1288db11dfacad886c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: eea644f426005af15be66fc9dbdcb9d60530e6ce77014a8345ebb52c561a71b2d352a2e29f373311724074a53c72d399007406348b8f421b1e9658a31c1b0c93
|
|
7
|
+
data.tar.gz: 8b60ace84e7fe2ab8c67ee7aea3cf412d1926740de83a60f5fd5820f06bad4e7202ea64349d74aa623c3475b0ccc74a4dd0de65eefe7266b851a24623923be06
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- master
|
|
7
|
+
pull_request:
|
|
8
|
+
branches:
|
|
9
|
+
- '*'
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
ci:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
ruby-version: ['2.7.x', '3.0.x']
|
|
17
|
+
steps:
|
|
18
|
+
- name: Set up Ruby
|
|
19
|
+
uses: actions/setup-ruby@v1
|
|
20
|
+
- uses: actions/checkout@v1
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: |
|
|
23
|
+
gem --version
|
|
24
|
+
gem install bundler
|
|
25
|
+
bundle --version
|
|
26
|
+
bundle install
|
|
27
|
+
- name: Run test
|
|
28
|
+
run: |
|
|
29
|
+
bundle exec rake
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 2.6
|
|
3
|
+
|
|
4
|
+
Style/ConditionalAssignment:
|
|
5
|
+
Enabled: false
|
|
6
|
+
|
|
7
|
+
Style/Documentation:
|
|
8
|
+
Enabled: false
|
|
9
|
+
|
|
10
|
+
Style/NumericLiterals:
|
|
11
|
+
Enabled: false
|
|
12
|
+
|
|
13
|
+
Style/ClassAndModuleChildren:
|
|
14
|
+
EnforcedStyle: compact
|
|
15
|
+
|
|
16
|
+
Metrics/AbcSize:
|
|
17
|
+
Max: 30
|
|
18
|
+
|
|
19
|
+
Metrics/MethodLength:
|
|
20
|
+
Max: 30
|
|
21
|
+
|
|
22
|
+
Naming/MethodParameterName:
|
|
23
|
+
MinNameLength: 1
|
|
24
|
+
|
|
25
|
+
Metrics/BlockLength:
|
|
26
|
+
Exclude:
|
|
27
|
+
- 'spec/*.rb'
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 Tomoya Kuwayama
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# svcb_rr_patch
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/rb/svcb_rr_patch)
|
|
4
|
+
[](https://github.com/thekuwayama/svcb_rr_patch/actions?workflow=CI)
|
|
5
|
+
[](https://codeclimate.com/github/thekuwayama/svcb_rr_patch/maintainability)
|
|
6
|
+
|
|
7
|
+
`svcb_rr_patch` is the patch that adds SVCB Resource Record and HTTPS Resource Record.
|
|
8
|
+
|
|
9
|
+
- https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-06
|
|
10
|
+
|
|
11
|
+
`svcb_rr_patch` supports "ech" SvcParamKey that ECHConfig.version is 0xfe0a.
|
|
12
|
+
|
|
13
|
+
- https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-10#section-4
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
he gem is available at [rubygems.org](https://rubygems.org/gems/svcb_rr_patch). You can install it the following.
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
$ gem install svcb_rr_patch
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
You can resolve HTTPS resources.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
$ irb
|
|
31
|
+
irb(main):001:0> require 'svcb_rr_patch'
|
|
32
|
+
=> true
|
|
33
|
+
irb(main):002:1* Resolv::DNS.new.getresources(
|
|
34
|
+
irb(main):003:1* "blog.cloudflare.com",
|
|
35
|
+
irb(main):004:1* Resolv::DNS::Resource::IN::HTTPS
|
|
36
|
+
irb(main):005:0> ) { |rr| pp rr }
|
|
37
|
+
=> [#<Resolv::DNS::Resource::IN::HTTPS:0x0000000000000001 @svc_priority=1, @svc_domain_name="", @svc_field_value={"alpn"=>#<SvcbRrPatch::SvcParams::Alpn:0x0000000000000002 @protocols=["h3-29", "h3-28", "h3-27", "h2"]>, "ipv4hint"=>#<SvcbRrPatch::SvcParams::Ipv4hint:0x0000000000000003 @addresses=[#<Resolv::IPv4 104.18.26.46>, #<Resolv::IPv4 104.18.27.46>]>, "ipv6hint"=>#<SvcbRrPatch::SvcParams::Ipv6hint:0x0000000000000004 @addresses=[#<Resolv::IPv6 2606:4700::6812:1a2e>, #<Resolv::IPv6 2606:4700::6812:1b2e>]>}, @ttl=300>]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## License
|
|
42
|
+
|
|
43
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# The HTTPS DNS Resource Record
|
|
5
|
+
|
|
6
|
+
class Resolv::DNS::Resource::IN::HTTPS < Resolv::DNS::Resource::IN::SVCB
|
|
7
|
+
TypeValue = 65 # rubocop:disable Naming/ConstantName
|
|
8
|
+
ClassValue = IN::ClassValue
|
|
9
|
+
ClassHash[[TypeValue, ClassValue]] = self
|
|
10
|
+
|
|
11
|
+
def initialize(svc_priority, target_name, svc_params)
|
|
12
|
+
# https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-06
|
|
13
|
+
super(svc_priority, target_name, svc_params)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
# :nodoc:
|
|
18
|
+
def decode_rdata(msg)
|
|
19
|
+
svc_priority = msg.get_bytes(2).unpack1('n')
|
|
20
|
+
target_name = msg.get_string
|
|
21
|
+
return new(svc_priority, target_name, {}) if svc_priority.zero?
|
|
22
|
+
|
|
23
|
+
# the SvcParams, consuming the remainder of the record
|
|
24
|
+
svc_params = ::SvcbRrPatch::SvcParams.decode(msg.get_bytes)
|
|
25
|
+
new(svc_priority, target_name, svc_params)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SvcbRrPatch::SvcParams
|
|
4
|
+
PARAMETER_REGISTRY = lambda {
|
|
5
|
+
registry = %w[
|
|
6
|
+
mandatory
|
|
7
|
+
alpn
|
|
8
|
+
no-default-alpn
|
|
9
|
+
port
|
|
10
|
+
ipv4hint
|
|
11
|
+
ech
|
|
12
|
+
ipv6hint
|
|
13
|
+
]
|
|
14
|
+
# rubocop:disable Security/Eval
|
|
15
|
+
(65280..65535).each do |nnnn|
|
|
16
|
+
eval "registry[nnnn] = \"key#{nnnn}\"", binding, __FILE__, __LINE__
|
|
17
|
+
end
|
|
18
|
+
# rubocop:enable Security/Eval
|
|
19
|
+
registry
|
|
20
|
+
}.call.freeze
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Dir[File.dirname(__FILE__) + '/svc_params/*.rb'].sort.each { |f| require f }
|
|
24
|
+
|
|
25
|
+
module SvcbRrPatch::SvcParams
|
|
26
|
+
# @return [String]
|
|
27
|
+
def self.encode(svc_params)
|
|
28
|
+
h = Hash[(0..PARAMETER_REGISTRY.size - 1).zip(PARAMETER_REGISTRY)].invert
|
|
29
|
+
|
|
30
|
+
svc_params
|
|
31
|
+
.map { |k, v| [h[k], v] }
|
|
32
|
+
.sort { |lh, rh| lh.first <=> rh.first }
|
|
33
|
+
.map do |k, v|
|
|
34
|
+
[k].pack('n') + v.encode.then { |s| [s.length].pack('n') + s }
|
|
35
|
+
end
|
|
36
|
+
.join
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @param octet [String]
|
|
40
|
+
#
|
|
41
|
+
# @return [Hash] Integer => SvcbRrPatch::$Object
|
|
42
|
+
# rubocop:disable Metrics/AbcSize
|
|
43
|
+
def self.decode(octet)
|
|
44
|
+
svc_params = {}
|
|
45
|
+
i = 0
|
|
46
|
+
h = Hash[(0..PARAMETER_REGISTRY.size - 1).zip(PARAMETER_REGISTRY)].invert
|
|
47
|
+
while i < octet.length
|
|
48
|
+
raise ::Resolv::DNS::DecodeError if i + 4 > octet.length
|
|
49
|
+
|
|
50
|
+
k = octet.slice(i, 2).unpack1('n')
|
|
51
|
+
# SvcParamKeys SHALL appear in increasing numeric order.
|
|
52
|
+
raise ::Resolv::DNS::DecodeError \
|
|
53
|
+
unless svc_params.keys.find { |already| h[already] >= k }.nil?
|
|
54
|
+
|
|
55
|
+
i += 2
|
|
56
|
+
vlen = octet.slice(i, 2).unpack1('n')
|
|
57
|
+
i += 2
|
|
58
|
+
raise ::Resolv::DNS::DecodeError if i + vlen > octet.length
|
|
59
|
+
|
|
60
|
+
v = octet.slice(i, vlen)
|
|
61
|
+
i += vlen
|
|
62
|
+
# Values are in a format specific to the SvcParamKey.
|
|
63
|
+
svc_param_key = PARAMETER_REGISTRY[k]
|
|
64
|
+
svc_param_values = decode_svc_params(svc_param_key, v)
|
|
65
|
+
svc_params.store(svc_param_key, svc_param_values)
|
|
66
|
+
end
|
|
67
|
+
raise ::Resolv::DNS::DecodeError if i != octet.length
|
|
68
|
+
|
|
69
|
+
svc_params
|
|
70
|
+
end
|
|
71
|
+
# rubocop:enable Metrics/AbcSize
|
|
72
|
+
|
|
73
|
+
# :nodoc:
|
|
74
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
75
|
+
def self.decode_svc_params(key, octet)
|
|
76
|
+
case key
|
|
77
|
+
when 'no name'
|
|
78
|
+
NoName.decode(octet)
|
|
79
|
+
when 'alpn'
|
|
80
|
+
Alpn.decode(octet)
|
|
81
|
+
when 'no-default-alpn'
|
|
82
|
+
NoDefaultAlpn.decode(octet)
|
|
83
|
+
when 'port'
|
|
84
|
+
Port.decode(octet)
|
|
85
|
+
when 'ipv4hint'
|
|
86
|
+
Ipv4hint.decode(octet)
|
|
87
|
+
when 'ech'
|
|
88
|
+
Ech.decode(octet)
|
|
89
|
+
when 'ipv6hint'
|
|
90
|
+
Ipv6hint.decode(octet)
|
|
91
|
+
else
|
|
92
|
+
octet
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
96
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SvcbRrPatch::SvcParams::Alpn
|
|
4
|
+
attr_reader :protocols
|
|
5
|
+
|
|
6
|
+
# @param protocols [Array of String]
|
|
7
|
+
def initialize(protocols)
|
|
8
|
+
@protocols = protocols
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @return [String]
|
|
12
|
+
def encode
|
|
13
|
+
@protocols.map { |p| [p.length].pack('C') + p }.join
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# :nodoc:
|
|
17
|
+
def self.decode_protocols(octet)
|
|
18
|
+
protocols = []
|
|
19
|
+
i = 0
|
|
20
|
+
while i < octet.length
|
|
21
|
+
raise ::Resolv::DNS::DecodeError if i + 1 > octet.length
|
|
22
|
+
|
|
23
|
+
id_len = octet.slice(i, 1).unpack1('C1')
|
|
24
|
+
i += 1
|
|
25
|
+
protocols << octet.slice(i, id_len)
|
|
26
|
+
i += id_len
|
|
27
|
+
end
|
|
28
|
+
raise ::Resolv::DNS::DecodeError if i != octet.length
|
|
29
|
+
|
|
30
|
+
protocols
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# :nodec:
|
|
34
|
+
def self.decode(octet)
|
|
35
|
+
protocols = decode_protocols(octet)
|
|
36
|
+
new(protocols)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SvcbRrPatch::SvcParams::Ech
|
|
4
|
+
attr_reader :echconfigs
|
|
5
|
+
|
|
6
|
+
# @param echconfiglist [Array of ECHConfig]
|
|
7
|
+
def initialize(echconfiglist)
|
|
8
|
+
@echconfiglist = echconfiglist
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @return [String]
|
|
12
|
+
def encode
|
|
13
|
+
@echconfiglist.map(&:encode).join.then { |s| [s.length].pack('n') + s }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# :nodoc:
|
|
17
|
+
def self.decode(octet)
|
|
18
|
+
raise ::Resolv::DNS::DecodeError \
|
|
19
|
+
unless octet.length == octet.slice(0, 2).unpack1('n') + 2
|
|
20
|
+
|
|
21
|
+
echconfiglist = ECHConfig.decode_vectors(octet.slice(2..))
|
|
22
|
+
new(echconfiglist)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require_relative 'ech/echconfig_contents'
|
|
27
|
+
|
|
28
|
+
class SvcbRrPatch::SvcParams::Ech::ECHConfig
|
|
29
|
+
attr_reader :version
|
|
30
|
+
attr_reader :echconfigcontents
|
|
31
|
+
|
|
32
|
+
ECHConfigContents = ::SvcbRrPatch::SvcParams::Ech::ECHConfigContents
|
|
33
|
+
|
|
34
|
+
# @param version [String]
|
|
35
|
+
# @param echconfig_contents [ECHConfigContents]
|
|
36
|
+
def initialize(version, echconfig_contents)
|
|
37
|
+
@version = version
|
|
38
|
+
@echconfig_contents = echconfig_contents
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @return [String]
|
|
42
|
+
def encode
|
|
43
|
+
@version + @echconfig_contents.encode.then { |s| [s.length].pack('n') + s }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @return [Array of ECHConfig]
|
|
47
|
+
def self.decode_vectors(octet)
|
|
48
|
+
i = 0
|
|
49
|
+
echconfigs = []
|
|
50
|
+
while i < octet.length
|
|
51
|
+
raise ::Resolv::DNS::DecodeError if i + 4 > octet.length
|
|
52
|
+
|
|
53
|
+
version = octet.slice(i, 2)
|
|
54
|
+
length = octet.slice(i + 2, 2).unpack1('n')
|
|
55
|
+
i += 4
|
|
56
|
+
raise ::Resolv::DNS::DecodeError if i + length > octet.length
|
|
57
|
+
|
|
58
|
+
echconfig_contents = ECHConfigContents.decode(octet.slice(i, length))
|
|
59
|
+
i += length
|
|
60
|
+
echconfigs << new(version, echconfig_contents)
|
|
61
|
+
end
|
|
62
|
+
raise ::Resolv::DNS::DecodeError if i != octet.length
|
|
63
|
+
|
|
64
|
+
echconfigs
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SvcbRrPatch::SvcParams::Ech::ECHConfigContents
|
|
4
|
+
# define class
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
Dir[File.dirname(__FILE__) + '/echconfig_contents/*.rb']
|
|
8
|
+
.sort.each { |f| require f }
|
|
9
|
+
|
|
10
|
+
class SvcbRrPatch::SvcParams::Ech::ECHConfigContents
|
|
11
|
+
attr_reader :key_config
|
|
12
|
+
attr_reader :maximum_name_length
|
|
13
|
+
attr_reader :public_name
|
|
14
|
+
attr_reader :extensions
|
|
15
|
+
|
|
16
|
+
# @param key_config [HpkeKeyConfig]
|
|
17
|
+
# @param maximum_name_length [Integer]
|
|
18
|
+
# @param public_name [String]
|
|
19
|
+
# @param extensions [Array of Extension]
|
|
20
|
+
def initialize(key_config,
|
|
21
|
+
maximum_name_length,
|
|
22
|
+
public_name,
|
|
23
|
+
extensions)
|
|
24
|
+
@key_config = key_config
|
|
25
|
+
@maximum_name_length = maximum_name_length
|
|
26
|
+
@public_name = public_name
|
|
27
|
+
@extensions = extensions
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @return [String]
|
|
31
|
+
def encode
|
|
32
|
+
@key_config.encode \
|
|
33
|
+
+ [@maximum_name_length].pack('n') \
|
|
34
|
+
+ @public_name.then { |s| [s.length].pack('n') + s } \
|
|
35
|
+
+ @extensions.map(&:encode).join.then { |s| [s.length].pack('n') + s }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# :nodoc
|
|
39
|
+
# rubocop:disable Metrics/AbcSize
|
|
40
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
41
|
+
def self.decode(octet)
|
|
42
|
+
key_config, octet = HpkeKeyConfig.decode(octet)
|
|
43
|
+
raise ::Resolv::DNS::DecodeError if octet.length < 2
|
|
44
|
+
|
|
45
|
+
maximum_name_length = octet.slice(0, 2).unpack1('n')
|
|
46
|
+
i = 2
|
|
47
|
+
raise ::Resolv::DNS::DecodeError if i + 2 > octet.length
|
|
48
|
+
|
|
49
|
+
pn_len = octet.slice(i, 2).unpack1('n')
|
|
50
|
+
i += 2
|
|
51
|
+
raise ::Resolv::DNS::DecodeError if i + pn_len > octet.length
|
|
52
|
+
|
|
53
|
+
public_name = octet.slice(i, pn_len)
|
|
54
|
+
i += pn_len
|
|
55
|
+
raise ::Resolv::DNS::DecodeError if i + 2 > octet.length
|
|
56
|
+
|
|
57
|
+
ex_len = octet.slice(i, 2).unpack1('n')
|
|
58
|
+
i += 2
|
|
59
|
+
raise ::Resolv::DNS::DecodeError if i + ex_len > octet.length
|
|
60
|
+
|
|
61
|
+
extensions = Extension.decode_vectors(octet.slice(i, ex_len))
|
|
62
|
+
i += ex_len
|
|
63
|
+
raise ::Resolv::DNS::DecodeError if i != octet.length
|
|
64
|
+
|
|
65
|
+
new(
|
|
66
|
+
key_config,
|
|
67
|
+
maximum_name_length,
|
|
68
|
+
public_name,
|
|
69
|
+
extensions
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
# rubocop:enable Metrics/AbcSize
|
|
73
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
74
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SvcbRrPatch::SvcParams::Ech::ECHConfigContents::Extension
|
|
4
|
+
attr_reader :octet
|
|
5
|
+
|
|
6
|
+
# @param octet [String]
|
|
7
|
+
def initialize(octet)
|
|
8
|
+
@octet = octet # TODO
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @return [String]
|
|
12
|
+
def encode
|
|
13
|
+
@octet # TODO
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @return [Array of Extension]
|
|
17
|
+
def self.decode_vectors(octet)
|
|
18
|
+
i = 0
|
|
19
|
+
extensions = []
|
|
20
|
+
while i < octet.length
|
|
21
|
+
raise ::Resolv::DNS::DecodeError if i + 4 > octet.length
|
|
22
|
+
|
|
23
|
+
ex_len = octet.slice(i + 2, 2)
|
|
24
|
+
i += 4
|
|
25
|
+
raise ::Resolv::DNS::DecodeError if i + ex_len > octet.length
|
|
26
|
+
|
|
27
|
+
extensions << new(octet.slice(i, ex_len)) # TODO
|
|
28
|
+
i += ex_len
|
|
29
|
+
end
|
|
30
|
+
raise ::Resolv::DNS::DecodeError if i != octet.length
|
|
31
|
+
|
|
32
|
+
extensions
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SvcbRrPatch::SvcParams::Ech::ECHConfigContents::HpkeKeyConfig
|
|
4
|
+
# define class
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
Dir[File.dirname(__FILE__) + '/hpke_key_config/*.rb']
|
|
8
|
+
.sort.each { |f| require f }
|
|
9
|
+
|
|
10
|
+
class SvcbRrPatch::SvcParams::Ech::ECHConfigContents::HpkeKeyConfig
|
|
11
|
+
attr_reader :config_id
|
|
12
|
+
attr_reader :kem_id
|
|
13
|
+
attr_reader :public_key
|
|
14
|
+
attr_reader :cipher_suites
|
|
15
|
+
|
|
16
|
+
# @param config_id [Integer]
|
|
17
|
+
# @param kem_id [HpkeKemId]
|
|
18
|
+
# @param public_key [HpkePublicKey]
|
|
19
|
+
# @param cipher_suites [Array of HpkeSymmetricCipherSuite]
|
|
20
|
+
def initialize(config_id,
|
|
21
|
+
kem_id,
|
|
22
|
+
public_key,
|
|
23
|
+
cipher_suites)
|
|
24
|
+
@config_id = config_id
|
|
25
|
+
@kem_id = kem_id
|
|
26
|
+
@public_key = public_key
|
|
27
|
+
@cipher_suites = cipher_suites
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @return [String]
|
|
31
|
+
def encode
|
|
32
|
+
[@config_id].pack('C') \
|
|
33
|
+
+ @kem_id.encode \
|
|
34
|
+
+ @public_key.encode \
|
|
35
|
+
+ @cipher_suites.map(&:encode).join.then { |s| [s.length].pack('n') + s }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# :nodoc
|
|
39
|
+
# rubocop:disable Metrics/AbcSize
|
|
40
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
41
|
+
def self.decode(octet)
|
|
42
|
+
raise ::Resolv::DNS::DecodeError if octet.empty?
|
|
43
|
+
|
|
44
|
+
config_id = octet.slice(0, 1).unpack1('C')
|
|
45
|
+
i = 1
|
|
46
|
+
raise ::Resolv::DNS::DecodeError if i + 2 > octet.length
|
|
47
|
+
|
|
48
|
+
kem_id = HpkeKemId.decode(octet.slice(i, 2))
|
|
49
|
+
i += 2
|
|
50
|
+
raise ::Resolv::DNS::DecodeError if i + 2 > octet.length
|
|
51
|
+
|
|
52
|
+
pk_len = octet.slice(i, 2).unpack1('n')
|
|
53
|
+
i += 2
|
|
54
|
+
raise ::Resolv::DNS::DecodeError if i + pk_len > octet.length
|
|
55
|
+
|
|
56
|
+
public_key = HpkePublicKey.decode(octet.slice(i, pk_len))
|
|
57
|
+
i += pk_len
|
|
58
|
+
raise ::Resolv::DNS::DecodeError if i + 2 > octet.length
|
|
59
|
+
|
|
60
|
+
cs_len = octet.slice(i, 2).unpack1('n')
|
|
61
|
+
i += 2
|
|
62
|
+
raise ::Resolv::DNS::DecodeError if i + 2 > octet.length
|
|
63
|
+
|
|
64
|
+
cs_bin = octet.slice(i, cs_len)
|
|
65
|
+
i += cs_len
|
|
66
|
+
cipher_suites = HpkeSymmetricCipherSuite.decode_vectors(cs_bin)
|
|
67
|
+
hpke_key_config = new(
|
|
68
|
+
config_id,
|
|
69
|
+
kem_id,
|
|
70
|
+
public_key,
|
|
71
|
+
cipher_suites
|
|
72
|
+
)
|
|
73
|
+
[hpke_key_config, octet[i..]]
|
|
74
|
+
end
|
|
75
|
+
# rubocop:enable Metrics/AbcSize
|
|
76
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
77
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SvcbRrPatch::SvcParams::Ech::ECHConfigContents::HpkeKeyConfig::HpkeKemId
|
|
4
|
+
attr_reader :uint16
|
|
5
|
+
|
|
6
|
+
# @param uint16 [Integer]
|
|
7
|
+
def initialize(uint16)
|
|
8
|
+
@uint16 = uint16
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @return [String]
|
|
12
|
+
def encode
|
|
13
|
+
[@uint16].pack('n')
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# :nodoc
|
|
17
|
+
def self.decode(octet)
|
|
18
|
+
raise ::Resolv::DNS::DecodeError if octet.length != 2
|
|
19
|
+
|
|
20
|
+
new(octet.unpack1('n'))
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SvcbRrPatch::SvcParams::Ech::ECHConfigContents::HpkeKeyConfig::HpkePublicKey # rubocop:disable Layout/LineLength
|
|
4
|
+
attr_reader :opaque
|
|
5
|
+
|
|
6
|
+
# @param opaque [String]
|
|
7
|
+
def initialize(opaque)
|
|
8
|
+
@opaque = opaque
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @return [String]
|
|
12
|
+
def encode
|
|
13
|
+
@opaque.then { |s| [s.length].pack('n') + s }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# :nodoc
|
|
17
|
+
def self.decode(octet)
|
|
18
|
+
new(octet)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SvcbRrPatch::SvcParams::Ech::ECHConfigContents::HpkeKeyConfig::HpkeSymmetricCipherSuite # rubocop:disable Layout/LineLength
|
|
4
|
+
# define class
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
Dir[File.dirname(__FILE__) + '/hpke_symmetric_cipher_suite/*.rb']
|
|
8
|
+
.sort.each { |f| require f }
|
|
9
|
+
|
|
10
|
+
class SvcbRrPatch::SvcParams::Ech::ECHConfigContents::HpkeKeyConfig::HpkeSymmetricCipherSuite # rubocop:disable Layout/LineLength
|
|
11
|
+
attr_reader :kdf_id
|
|
12
|
+
attr_reader :aead_id
|
|
13
|
+
|
|
14
|
+
# @param kdf_id [HpkeKdfId]
|
|
15
|
+
# @param aead_id [HpkeAeadId]
|
|
16
|
+
def initialize(kdf_id, aead_id)
|
|
17
|
+
@kdf_id = kdf_id
|
|
18
|
+
@aead_id = aead_id
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @return [String]
|
|
22
|
+
def encode
|
|
23
|
+
@kdf_id.encode + @aead_id.encode
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @return [Array of HpkeSymmetricCipherSuite]
|
|
27
|
+
def self.decode_vectors(octet)
|
|
28
|
+
i = 0
|
|
29
|
+
cipher_suites = []
|
|
30
|
+
while i < octet.length
|
|
31
|
+
raise ::Resolv::DNS::DecodeError if i + 4 > octet.length
|
|
32
|
+
|
|
33
|
+
kdf_id = HpkeKdfId.decode(octet.slice(i, 2))
|
|
34
|
+
aead_id = HpkeAeadId.decode(octet.slice(i + 2, 2))
|
|
35
|
+
i += 4
|
|
36
|
+
cipher_suites << new(kdf_id, aead_id)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
cipher_suites
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SvcbRrPatch::SvcParams::Ech::ECHConfigContents::HpkeKeyConfig::HpkeSymmetricCipherSuite::HpkeAeadId # rubocop:disable Layout/LineLength
|
|
4
|
+
attr_reader :uint16
|
|
5
|
+
|
|
6
|
+
# @param uint16 [Integer]
|
|
7
|
+
def initialize(uint16)
|
|
8
|
+
@uint16 = uint16
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @return [String]
|
|
12
|
+
def encode
|
|
13
|
+
[@uint16].pack('n')
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# :nodoc
|
|
17
|
+
def self.decode(octet)
|
|
18
|
+
raise ::Resolv::DNS::DecodeError if octet.length != 2
|
|
19
|
+
|
|
20
|
+
new(octet.unpack1('n'))
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SvcbRrPatch::SvcParams::Ech::ECHConfigContents::HpkeKeyConfig::HpkeSymmetricCipherSuite::HpkeKdfId # rubocop:disable Layout/LineLength
|
|
4
|
+
attr_reader :uint16
|
|
5
|
+
|
|
6
|
+
# @param uint16 [Integer]
|
|
7
|
+
def initialize(uint16)
|
|
8
|
+
@uint16 = uint16
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @return [String]
|
|
12
|
+
def encode
|
|
13
|
+
[@uint16].pack('n')
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# :nodoc
|
|
17
|
+
def self.decode(octet)
|
|
18
|
+
raise ::Resolv::DNS::DecodeError if octet.length != 2
|
|
19
|
+
|
|
20
|
+
new(octet.unpack1('n'))
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SvcbRrPatch::SvcParams::Ipv4hint
|
|
4
|
+
attr_reader :addresses
|
|
5
|
+
|
|
6
|
+
# @param addresses [Array of Resolv::IPv4]
|
|
7
|
+
def initialize(addresses)
|
|
8
|
+
@addresses = addresses
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @return [String]
|
|
12
|
+
def encode
|
|
13
|
+
@addresses.map(&:address).join
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# :nodoc:
|
|
17
|
+
def self.decode(octet)
|
|
18
|
+
addresses = octet.scan(/.{1,4}/).map { |s| Resolv::IPv4.new(s) }
|
|
19
|
+
new(addresses)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SvcbRrPatch::SvcParams::Ipv6hint
|
|
4
|
+
attr_reader :addresses
|
|
5
|
+
|
|
6
|
+
# @param addresses [Array of Resolv::IPv6]
|
|
7
|
+
def initialize(addresses)
|
|
8
|
+
@addresses = addresses
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @return [String]
|
|
12
|
+
def encode
|
|
13
|
+
@addresses.map(&:address).join
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# :nodoc:
|
|
17
|
+
def self.decode(octet)
|
|
18
|
+
addresses = octet.scan(/.{1,16}/).map { |s| Resolv::IPv6.new(s) }
|
|
19
|
+
new(addresses)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SvcbRrPatch::SvcParams::Mandatory
|
|
4
|
+
attr_reader :keys
|
|
5
|
+
|
|
6
|
+
# @param keys [Array of Integer]]
|
|
7
|
+
def initialize(keys)
|
|
8
|
+
@keys = keys
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @return [String]
|
|
12
|
+
def encode
|
|
13
|
+
@keys.sort.map { |k| [k].pack('n') }.join
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# :nodoc:
|
|
17
|
+
def self.decode(octet)
|
|
18
|
+
keys = octet.scan(/.{1,2}/).map { |s| s.unpack1('n') }
|
|
19
|
+
new(keys)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SvcbRrPatch::SvcParams::Port
|
|
4
|
+
attr_reader :port
|
|
5
|
+
|
|
6
|
+
# @param port [Integer]
|
|
7
|
+
def initialize(port)
|
|
8
|
+
@port = port
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @return [String]
|
|
12
|
+
def encode
|
|
13
|
+
[@port].pack('n')
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# :nodoc:
|
|
17
|
+
def self.decode(octet)
|
|
18
|
+
port = octet.unpack1('n')
|
|
19
|
+
new(port)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# The SVCB(contraction of "service binding") DNS Resource Record
|
|
5
|
+
|
|
6
|
+
class Resolv::DNS::Resource::IN::SVCB < Resolv::DNS::Resource
|
|
7
|
+
TypeValue = 64 # rubocop: disable Naming/ConstantName
|
|
8
|
+
ClassValue = IN::ClassValue
|
|
9
|
+
ClassHash[[TypeValue, ClassValue]] = self
|
|
10
|
+
|
|
11
|
+
# @param svc_priority [Integer]
|
|
12
|
+
# @param target_name [String]
|
|
13
|
+
# @param svc_params [Map]
|
|
14
|
+
def initialize(svc_priority, target_name, svc_params)
|
|
15
|
+
# https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-06
|
|
16
|
+
@svc_priority = svc_priority
|
|
17
|
+
@target_name = target_name
|
|
18
|
+
@svc_params = svc_params
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
##
|
|
22
|
+
# SvcPriority
|
|
23
|
+
|
|
24
|
+
attr_reader :svc_priority
|
|
25
|
+
|
|
26
|
+
##
|
|
27
|
+
# TargetName
|
|
28
|
+
|
|
29
|
+
attr_reader :target_name
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# SvcParams
|
|
33
|
+
|
|
34
|
+
attr_reader :svc_params
|
|
35
|
+
|
|
36
|
+
# :nodoc:
|
|
37
|
+
def encode_rdata(msg)
|
|
38
|
+
msg.put_bytes([@svc_priority].pack('n'))
|
|
39
|
+
msg.put_string(@target_name)
|
|
40
|
+
msg.put_string(::SvcbRrPatch::SvcParams.encode(@svc_params))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class << self
|
|
44
|
+
# :nodoc:
|
|
45
|
+
def decode_rdata(msg)
|
|
46
|
+
svc_priority = msg.get_bytes(2).unpack1('n')
|
|
47
|
+
target_name = msg.get_string
|
|
48
|
+
return new(svc_priority, target_name, {}) if svc_priority.zero?
|
|
49
|
+
|
|
50
|
+
# the SvcParams, consuming the remainder of the record
|
|
51
|
+
svc_params = ::SvcbRrPatch::SvcParams.decode(msg.get_bytes)
|
|
52
|
+
new(svc_priority, target_name, svc_params)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
data/spec/alpn_spec.rb
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative 'spec_helper'
|
|
5
|
+
|
|
6
|
+
RSpec.describe SvcbRrPatch::SvcParams::Alpn do
|
|
7
|
+
let(:octet) do
|
|
8
|
+
"\x05h3-29\x05h3-28\x05h3-27\x02h2"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
context '#decode' do
|
|
12
|
+
let(:alpn) do
|
|
13
|
+
SvcbRrPatch::SvcParams::Alpn.decode(octet)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'could decode' do
|
|
17
|
+
expect(alpn.protocols).to eq %w[h3-29 h3-28 h3-27 h2]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
context '#encode' do
|
|
22
|
+
let(:alpn) do
|
|
23
|
+
SvcbRrPatch::SvcParams::Alpn.new(%w[h3-29 h3-28 h3-27 h2])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'could encode' do
|
|
27
|
+
expect(alpn.encode).to eq octet
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative 'spec_helper'
|
|
5
|
+
|
|
6
|
+
RSpec.describe SvcbRrPatch::SvcParams::Ipv4hint do
|
|
7
|
+
let(:octet) do
|
|
8
|
+
"\xc0\xa8\x00\x01\xc0\xa8\x00\x02"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
context '#decode' do
|
|
12
|
+
let(:ipv4hint) do
|
|
13
|
+
SvcbRrPatch::SvcParams::Ipv4hint.decode(octet)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'could decode' do
|
|
17
|
+
expect(ipv4hint.addresses).to eq [
|
|
18
|
+
Resolv::IPv4.create('192.168.0.1'),
|
|
19
|
+
Resolv::IPv4.create('192.168.0.2')
|
|
20
|
+
]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
context '#encode' do
|
|
25
|
+
let(:ipv4hint) do
|
|
26
|
+
SvcbRrPatch::SvcParams::Ipv4hint.new(
|
|
27
|
+
[
|
|
28
|
+
Resolv::IPv4.create('192.168.0.1'),
|
|
29
|
+
Resolv::IPv4.create('192.168.0.2')
|
|
30
|
+
]
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'could encode' do
|
|
35
|
+
expect(ipv4hint.encode).to eq octet
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative 'spec_helper'
|
|
5
|
+
|
|
6
|
+
RSpec.describe SvcbRrPatch::SvcParams::Ipv6hint do
|
|
7
|
+
let(:octet) do
|
|
8
|
+
"\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
context '#decode' do
|
|
12
|
+
let(:ipv6hint) do
|
|
13
|
+
SvcbRrPatch::SvcParams::Ipv6hint.decode(octet)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'could decode' do
|
|
17
|
+
expect(ipv6hint.addresses)
|
|
18
|
+
.to eq [Resolv::IPv6.create('101:101:101:101:101:101:101:101')]
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
context '#encode' do
|
|
23
|
+
let(:ipv6hint) do
|
|
24
|
+
SvcbRrPatch::SvcParams::Ipv6hint.new(
|
|
25
|
+
[Resolv::IPv6.create('101:101:101:101:101:101:101:101')]
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'could encode' do
|
|
30
|
+
expect(ipv6hint.encode).to eq octet
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative 'spec_helper'
|
|
5
|
+
|
|
6
|
+
RSpec.describe SvcbRrPatch::SvcParams::Mandatory do
|
|
7
|
+
let(:octet) do
|
|
8
|
+
"\x00\x05\xff\xa4"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
let(:keys) do
|
|
12
|
+
h = Hash[
|
|
13
|
+
(0..SvcbRrPatch::SvcParams::PARAMETER_REGISTRY.size - 1)
|
|
14
|
+
.zip(SvcbRrPatch::SvcParams::PARAMETER_REGISTRY)
|
|
15
|
+
].invert
|
|
16
|
+
|
|
17
|
+
[h['ech'], h['key65444']]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context '#decode' do
|
|
21
|
+
let(:mandatory) do
|
|
22
|
+
SvcbRrPatch::SvcParams::Mandatory.decode(octet)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'could decode' do
|
|
26
|
+
expect(mandatory.keys).to eq keys
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
context '#encode' do
|
|
31
|
+
let(:mandatory) do
|
|
32
|
+
SvcbRrPatch::SvcParams::Mandatory.new(keys)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'could encode' do
|
|
36
|
+
expect(mandatory.encode).to eq octet
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative 'spec_helper'
|
|
5
|
+
|
|
6
|
+
RSpec.describe SvcbRrPatch::SvcParams::NoDefaultAlpn do
|
|
7
|
+
let(:octet) do
|
|
8
|
+
"\x05h3-29\x05h3-28\x05h3-27\x02h2"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
context '#decode' do
|
|
12
|
+
let(:no_default_alpn) do
|
|
13
|
+
SvcbRrPatch::SvcParams::NoDefaultAlpn
|
|
14
|
+
.decode(octet)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'could decode' do
|
|
18
|
+
expect(no_default_alpn.protocols).to eq %w[h3-29 h3-28 h3-27 h2]
|
|
19
|
+
expect(no_default_alpn)
|
|
20
|
+
.to be_a(SvcbRrPatch::SvcParams::NoDefaultAlpn)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
context '#encode' do
|
|
25
|
+
let(:no_default_alpn) do
|
|
26
|
+
SvcbRrPatch::SvcParams::NoDefaultAlpn.new(%w[h3-29 h3-28 h3-27 h2])
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'could encode' do
|
|
30
|
+
expect(no_default_alpn)
|
|
31
|
+
.to be_a(SvcbRrPatch::SvcParams::NoDefaultAlpn)
|
|
32
|
+
expect(no_default_alpn.encode).to eq octet
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/spec/port_spec.rb
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative 'spec_helper'
|
|
5
|
+
|
|
6
|
+
RSpec.describe SvcbRrPatch::SvcParams::Port do
|
|
7
|
+
let(:octet) do
|
|
8
|
+
"\x01\xbb"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
context '#decode' do
|
|
12
|
+
let(:port) do
|
|
13
|
+
SvcbRrPatch::SvcParams::Port.decode(octet)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'could decode' do
|
|
17
|
+
expect(port.port).to eq 443
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
context '#encode' do
|
|
22
|
+
let(:port) do
|
|
23
|
+
SvcbRrPatch::SvcParams::Port.new(443)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'could encode' do
|
|
27
|
+
expect(port.encode).to eq octet
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative 'spec_helper'
|
|
5
|
+
|
|
6
|
+
RSpec.describe SvcbRrPatch::SvcParams do
|
|
7
|
+
let(:octet) do
|
|
8
|
+
<<-BIN.split.map(&:hex).map(&:chr).join
|
|
9
|
+
00 01 00 15 05 68 33 2d 32 39 05 68 33 2d 32 38
|
|
10
|
+
05 68 33 2d 32 37 02 68 32 00 04 00 08 c0 a8 00
|
|
11
|
+
01 c0 a8 00 02 00 06 00 10 01 01 01 01 01 01 01
|
|
12
|
+
01 01 01 01 01 01 01 01 01 ff 35 00 03 01 02 03
|
|
13
|
+
BIN
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
let(:alpn) do
|
|
17
|
+
SvcbRrPatch::SvcParams::Alpn.new(%w[h3-29 h3-28 h3-27 h2])
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
let(:ipv4hint) do
|
|
21
|
+
SvcbRrPatch::SvcParams::Ipv4hint.new(
|
|
22
|
+
[
|
|
23
|
+
Resolv::IPv4.create('192.168.0.1'),
|
|
24
|
+
Resolv::IPv4.create('192.168.0.2')
|
|
25
|
+
]
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
let(:ipv6hint) do
|
|
30
|
+
SvcbRrPatch::SvcParams::Ipv6hint.new(
|
|
31
|
+
[Resolv::IPv6.create('101:101:101:101:101:101:101:101')]
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
let(:key65333) do
|
|
36
|
+
"\x01\x02\x03"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context '#keyNNNNN' do
|
|
40
|
+
it 'could be defined' do
|
|
41
|
+
expect(SvcbRrPatch::SvcParams::PARAMETER_REGISTRY[65333])
|
|
42
|
+
.to eq 'key65333'
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
context '#decode' do
|
|
47
|
+
let(:svc_params) do
|
|
48
|
+
SvcbRrPatch::SvcParams.decode(octet)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'could decode' do
|
|
52
|
+
expect(svc_params.keys).to eq %w[alpn ipv4hint ipv6hint key65333]
|
|
53
|
+
|
|
54
|
+
expect(svc_params['alpn'].protocols).to eq alpn.protocols
|
|
55
|
+
expect(svc_params['ipv4hint'].addresses).to eq ipv4hint.addresses
|
|
56
|
+
expect(svc_params['ipv6hint'].addresses).to eq ipv6hint.addresses
|
|
57
|
+
expect(svc_params['key65333']).to eq key65333
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
context '#encode' do
|
|
62
|
+
let(:svc_params) do
|
|
63
|
+
{
|
|
64
|
+
'alpn' => alpn,
|
|
65
|
+
'ipv4hint' => ipv4hint,
|
|
66
|
+
'ipv6hint' => ipv6hint,
|
|
67
|
+
'key65333' => key65333
|
|
68
|
+
}
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'could encode' do
|
|
72
|
+
expect(SvcbRrPatch::SvcParams.encode(svc_params)).to eq octet
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
+
require 'svcb_rr_patch/version'
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |spec|
|
|
8
|
+
spec.name = 'svcb_rr_patch'
|
|
9
|
+
spec.version = SvcbRrPatch::VERSION
|
|
10
|
+
spec.authors = ['thekuwayama']
|
|
11
|
+
spec.email = ['thekuwayama@gmail.com']
|
|
12
|
+
spec.summary = 'the patch that adds SVCB Resource Record' \
|
|
13
|
+
' and HTTPS Resource Record'
|
|
14
|
+
spec.description = spec.summary
|
|
15
|
+
spec.homepage = 'https://github.com/thekuwayama/svcb_rr_patch'
|
|
16
|
+
spec.license = 'MIT'
|
|
17
|
+
spec.required_ruby_version = '>=2.6.0'
|
|
18
|
+
|
|
19
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
|
20
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
21
|
+
spec.require_paths = ['lib']
|
|
22
|
+
|
|
23
|
+
spec.add_development_dependency 'bundler'
|
|
24
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: svcb_rr_patch
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- thekuwayama
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2021-07-21 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
description: the patch that adds SVCB Resource Record and HTTPS Resource Record
|
|
28
|
+
email:
|
|
29
|
+
- thekuwayama@gmail.com
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- ".github/workflows/ci.yml"
|
|
35
|
+
- ".gitignore"
|
|
36
|
+
- ".rubocop.yml"
|
|
37
|
+
- Gemfile
|
|
38
|
+
- LICENSE.txt
|
|
39
|
+
- README.md
|
|
40
|
+
- Rakefile
|
|
41
|
+
- lib/svcb_rr_patch.rb
|
|
42
|
+
- lib/svcb_rr_patch/https.rb
|
|
43
|
+
- lib/svcb_rr_patch/svc_params.rb
|
|
44
|
+
- lib/svcb_rr_patch/svc_params/alpn.rb
|
|
45
|
+
- lib/svcb_rr_patch/svc_params/ech.rb
|
|
46
|
+
- lib/svcb_rr_patch/svc_params/ech/echconfig_contents.rb
|
|
47
|
+
- lib/svcb_rr_patch/svc_params/ech/echconfig_contents/extension.rb
|
|
48
|
+
- lib/svcb_rr_patch/svc_params/ech/echconfig_contents/hpke_key_config.rb
|
|
49
|
+
- lib/svcb_rr_patch/svc_params/ech/echconfig_contents/hpke_key_config/hpke_kem_id.rb
|
|
50
|
+
- lib/svcb_rr_patch/svc_params/ech/echconfig_contents/hpke_key_config/hpke_public_key.rb
|
|
51
|
+
- lib/svcb_rr_patch/svc_params/ech/echconfig_contents/hpke_key_config/hpke_symmetric_cipher_suite.rb
|
|
52
|
+
- lib/svcb_rr_patch/svc_params/ech/echconfig_contents/hpke_key_config/hpke_symmetric_cipher_suite/hpke_aead_id.rb
|
|
53
|
+
- lib/svcb_rr_patch/svc_params/ech/echconfig_contents/hpke_key_config/hpke_symmetric_cipher_suite/hpke_kdf_id.rb
|
|
54
|
+
- lib/svcb_rr_patch/svc_params/ipv4hint.rb
|
|
55
|
+
- lib/svcb_rr_patch/svc_params/ipv6hint.rb
|
|
56
|
+
- lib/svcb_rr_patch/svc_params/mandatory.rb
|
|
57
|
+
- lib/svcb_rr_patch/svc_params/no_default_alpn.rb
|
|
58
|
+
- lib/svcb_rr_patch/svc_params/port.rb
|
|
59
|
+
- lib/svcb_rr_patch/svcb.rb
|
|
60
|
+
- lib/svcb_rr_patch/version.rb
|
|
61
|
+
- spec/alpn_spec.rb
|
|
62
|
+
- spec/ipv4hint_spec.rb
|
|
63
|
+
- spec/ipv6hint_spec.rb
|
|
64
|
+
- spec/mandatory_spec.rb
|
|
65
|
+
- spec/no_default_alpn_spec.rb
|
|
66
|
+
- spec/port_spec.rb
|
|
67
|
+
- spec/spec_helper.rb
|
|
68
|
+
- spec/svc_params_spec.rb
|
|
69
|
+
- svcb_rr_patch.gemspec
|
|
70
|
+
homepage: https://github.com/thekuwayama/svcb_rr_patch
|
|
71
|
+
licenses:
|
|
72
|
+
- MIT
|
|
73
|
+
metadata: {}
|
|
74
|
+
post_install_message:
|
|
75
|
+
rdoc_options: []
|
|
76
|
+
require_paths:
|
|
77
|
+
- lib
|
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: 2.6.0
|
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - ">="
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '0'
|
|
88
|
+
requirements: []
|
|
89
|
+
rubygems_version: 3.2.3
|
|
90
|
+
signing_key:
|
|
91
|
+
specification_version: 4
|
|
92
|
+
summary: the patch that adds SVCB Resource Record and HTTPS Resource Record
|
|
93
|
+
test_files:
|
|
94
|
+
- spec/alpn_spec.rb
|
|
95
|
+
- spec/ipv4hint_spec.rb
|
|
96
|
+
- spec/ipv6hint_spec.rb
|
|
97
|
+
- spec/mandatory_spec.rb
|
|
98
|
+
- spec/no_default_alpn_spec.rb
|
|
99
|
+
- spec/port_spec.rb
|
|
100
|
+
- spec/spec_helper.rb
|
|
101
|
+
- spec/svc_params_spec.rb
|