Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement SVCB and HTTPS RRs #32

Merged
merged 3 commits into from
Nov 24, 2023
Merged

Implement SVCB and HTTPS RRs #32

merged 3 commits into from
Nov 24, 2023

Conversation

hanazuki
Copy link
Contributor

@hanazuki hanazuki commented Apr 9, 2023

This patch implements SVCB and HTTPS resource record types defined in draft-ietf-dnsop-svcb-https-12.

The RR types are now supported by many server implementations including BIND, unbound, PowerDNS, and Knot DNS. Major browsers such as Chrome, Edge, and Safari have started to query HTTPS records, with the records gradually adopted by websites. Also, SVCB is actually deployed in the public DNS resolvers such as Cloudflare DNS and Google Public DNS for DDR.

With such wide adoption, we have plenty of real-world use cases, and it is unlikely the wire format will change further in an incompatible way. It is time to implement them in the client libraries!

Rationale for proposed API

Resolv::DNS::Resource::IN::ServiceBinding

This is an abstract class for SVCB-compatible RR types. SVCB-compatible RR types, as defined in the Draft, shares the wire format and the semantics of their RDATA fields with SVCB to allow implementations to share the processing of these RR types. So we do so.

The interface of this class is straightforward: It has three attributes priority, target, and params, which correspond the RDATA fields SvcPriority, TargetName, and SvcParams, resp.

SVCB RR type is defined specifically within IN class. Thus, this class is placed in the Resolv::DNS::Resource::IN namespace.

Resolv::DNS::Resource::IN::SVCB, Resolv::DNS::Resource::IN::HTTPS

Just inherits ServiceBinding class.

Resolv::DNS::SvcParam

This class represents a pair of a SvcParamKey and a SvcParamValue. Aligned with the design of Resolv::DNS::Resource, each SvcParamKey has its own subclass of Resolv::DNS::SvcParam.

Resolv::DNS::SvcParam::Generic

This is an abstract class representing a SvcParamKey that is unknown to this library. Generic.create(key) dynamically defines its
subclass for specific key. E.g., Generic.create(667) will define Generic::Key667.

This class holds SvcParamValue in its wire format.

SvcParam with an unknown SvcParamKey will be decoded as a subclass of this class. Also, users of this library can generate a non-supported SvcParam if they know its wire format.

Resolv::DNS::SvcParams

This is conceptually a set of SvcParams, whose elements have the unique SvcParamKeys. It behaves like a set, and for convenience provides indexing by SvcParamKey.

  • #initialize(params) takes an Enumerable of SvcParams as the initial content. If it contains SvcParams with the duplicate key, the one that appears last takes precedence.
  • #[](key) fetches the SvcParam with the given key. The key can be specified by its name (e.g., :alpn) or number (e.g., 1).
  • #add(param) adds a SvcParam to the set. If the set already has a SvcParam with the same key, it will be replaced.
  • #delete(key) deletes a SvcParam by its key and returns it. The key can be specified by its name or number.

Actually working in real world:

irb(main):001:0> dns = Resolv::DNS.new(nameserver: ['1.1.1.1'])
=>
#<Resolv::DNS:0x00007f668a0a0170
 @config=
  #<Resolv::DNS::Config:0x00007f668a2ccc28
   @config_info={:nameserver=>["1.1.1.1"]},
   @initialized=nil,
   @mutex=#<Thread::Mutex:0x00007f668a0a0120>,
   @timeouts=nil>,
 @initialized=nil,
 @mutex=#<Thread::Mutex:0x00007f668a0a0148>>

irb(main):003:0> dns.getresources('cloudflare.com', Resolv::DNS::Resource::IN::HTTPS)
=>
[#<Resolv::DNS::Resource::IN::HTTPS:0x00007f668a0468a0
  @params=
   #<Resolv::DNS::SvcParams:0x00007f668a046788
    @params=
     {1=>#<Resolv::DNS::SvcParam::ALPN:0x00007f668a0472a0 @protocol_ids=["h3", "h3-29", "h2"]>,
      4=>
       #<Resolv::DNS::SvcParam::IPv4Hint:0x00007f668a046fa8
        @addresses=[#<Resolv::IPv4 104.16.132.229>, #<Resolv::IPv4 104.16.133.229>]>,
      6=>
       #<Resolv::DNS::SvcParam::IPv6Hint:0x00007f668a046c60
        @addresses=[#<Resolv::IPv6 2606:4700::6810:84e5>, #<Resolv::IPv6 2606:4700::6810:85e5>]>}>,
  @priority=1,
  @target=#<Resolv::DNS::Name: .>,
  @ttl=16>] (size=1)

irb(main):004:0> dns.getresources('_dns.resolver.arpa', Resolv::DNS::Resource::IN::SVCB)
=>
[#<Resolv::DNS::Resource::IN::SVCB:0x00007f668a30d8e0
  @params=
   #<Resolv::DNS::SvcParams:0x00007f668a30d868
    @params=
     {1=>#<Resolv::DNS::SvcParam::ALPN:0x00007f668a30e790 @protocol_ids=["h2"]>,
      3=>#<Resolv::DNS::SvcParam::Port:0x00007f668a30e538 @port=443>,
      4=>
       #<Resolv::DNS::SvcParam::IPv4Hint:0x00007f668a30e2b8
        @addresses=[#<Resolv::IPv4 1.1.1.1>, #<Resolv::IPv4 1.0.0.1>]>,
      6=>
       #<Resolv::DNS::SvcParam::IPv6Hint:0x00007f668a30e0b0
        @addresses=[#<Resolv::IPv6 2606:4700:4700::1111>, #<Resolv::IPv6 2606:4700:4700::1001>]>,
      7=>#<Resolv::DNS::SvcParam::Generic::Key7:0x00007f668a30dae8 @value="/dns-query{?dns}">}>,
  @priority=1,
  @target=#<Resolv::DNS::Name: one.one.one.one.>,
  @ttl=300>,
 #<Resolv::DNS::Resource::IN::SVCB:0x00007f668a10f2f0
  @params=
   #<Resolv::DNS::SvcParams:0x00007f668a30c440
    @params=
     {1=>#<Resolv::DNS::SvcParam::ALPN:0x00007f668a30cb70 @protocol_ids=["dot"]>,
      3=>#<Resolv::DNS::SvcParam::Port:0x00007f668a30c968 @port=853>,
      4=>
       #<Resolv::DNS::SvcParam::IPv4Hint:0x00007f668a30c710
        @addresses=[#<Resolv::IPv4 1.1.1.1>, #<Resolv::IPv4 1.0.0.1>]>,
      6=>
       #<Resolv::DNS::SvcParam::IPv6Hint:0x00007f668a30c508
        @addresses=[#<Resolv::IPv6 2606:4700:4700::1111>, #<Resolv::IPv6 2606:4700:4700::1001>]>}>,
  @priority=2,
  @target=#<Resolv::DNS::Name: one.one.one.one.>,
  @ttl=300>] (size=2)

hanazuki added 2 commits April 9, 2023 02:41
This method repeats yielding until all the data upto the current limit
is consumed, and then returns an Array containig the block results.
This patch implements SVCB and HTTPS resource record types defined in
[draft-ietf-dnsop-svcb-https-12].

The RR types are now supported by many server implementations including
BIND, unbound, PowerDNS, and Knot DNS. Major browsers such as Chrome,
Edge, and Safari have started to query HTTPS records, with the records
gradually adopted by websites. Also, SVCB is actually deployed in the
public DNS resolvers such as Cloudflare DNS and Google Public DNS for
[DDR].

With such wide adoption, we have plenty of real-world use cases, and
it is unlikely the wire format will change further in an incompatible
way. It is time to implement them in the client libraries!

# Rationale for proposed API

## `Resolv::DNS::Resource::IN::ServiceBinding`

This is an abstract class for SVCB-compatible RR types.
SVCB-compatible RR types, as defined in the Draft, shares the wire
format and the semantics of their RDATA fields with SVCB to allow
implementations to share the processing of these RR types. So we do
so.

The interface of this class is straightforward: It has three
attributes `priority`, `target`, and `params`, which correspond the
RDATA fields SvcPriority, TargetName, and SvcParams, resp.

SVCB RR type is defined specifically within IN class. Thus, this
class is placed in the `Resolv::DNS::Resource::IN` namespace.

## `Resolv::DNS::Resource::IN::SVCB`, `Resolv::DNS::Resource::IN::HTTPS`

Just inherits ServiceBinding class.

## `Resolv::DNS::SvcParam`

This class represents a pair of a SvcParamKey and a SvcParamValue.
Aligned with the design of `Resolv::DNS::Resource`, each SvcParamKey
has its own subclass of `Resolv::DNS::SvcParam`.

## `Resolv::DNS::SvcParam::Generic`

This is an abstract class representing a SvcParamKey that is unknown
to this library. `Generic.create(key)` dynamically defines its
subclass for specific `key`. E.g., `Generic.create(667)` will define
`Generic::Key667`.

This class holds SvcParamValue in its wire format.

SvcParam with an unknown SvcParamKey will be decoded as a subclass of
this class. Also, users of this library can generate a non-supported
SvcParam if they know its wire format.

## `Resolv::DNS::SvcParams`

This is conceptually a set of `SvcParam`s, whose elements have the
unique SvcParamKeys. It behaves like a set, and for convenience
provides indexing by SvcParamKey.

- `#initialize(params)` takes an Enumerable of `SvcParam`s as the
  initial content. If it contains `SvcParam`s with the duplicate key,
  the one that appears last takes precedence.
- `#[](key)` fetches the `SvcParam` with the given key. The key can be
  specified by its name (e.g., `:alpn`) or number (e.g., `1`).
- `#add(param)` adds a `SvcParam` to the set. If the set already has a
  `SvcParam` with the same key, it will be replaced.
- `#delete(key)` deletes a `SvcParam` by its key and returns it. The key
  can be specified by its name or number.

[draft-ietf-dnsop-svcb-https-12]: https://datatracker.ietf.org/doc/draft-ietf-dnsop-svcb-https/12/
[DDR]: https://datatracker.ietf.org/doc/draft-ietf-add-ddr/
@sorah sorah requested review from akr and nobu November 22, 2023 10:22
@sorah
Copy link
Member

sorah commented Nov 22, 2023

The draft now published as: https://datatracker.ietf.org/doc/rfc9460/

@hanazuki
Copy link
Contributor Author

hanazuki commented Nov 22, 2023

Just pushed a patch that updates the references to the RFC.
There are no changes in the wire format and semantics that affect this patch.

@sorah sorah merged commit b3ced7f into ruby:master Nov 24, 2023
matzbot pushed a commit to ruby/ruby that referenced this pull request Nov 24, 2023
(ruby/resolv#32)

* Add MessageDecoder#get_list

This method repeats yielding until all the data upto the current limit
is consumed, and then returns an Array containig the block results.

* Implement SVCB and HTTPS RRs [RFC 9460]

> This patch implements SVCB and HTTPS resource record types defined in
> [RFC 9460].
>
> The RR types are now supported by many server implementations including
> BIND, unbound, PowerDNS, and Knot DNS. Major browsers such as Chrome,
> Edge, and Safari have started to query HTTPS records, with the records
> gradually adopted by websites. Also, SVCB is actually deployed in the
> public DNS resolvers such as Cloudflare DNS and Google Public DNS for
> [DDR].
>
> With such wide adoption, we have plenty of real-world use cases, and
> it is unlikely the wire format will change further in an incompatible
> way. It is time to implement them in the client libraries!
>
> # Rationale for proposed API
>
> ## `Resolv::DNS::Resource::IN::ServiceBinding`
>
> This is an abstract class for SVCB-compatible RR types.
> SVCB-compatible RR types, as defined in the Draft, shares the wire
> format and the semantics of their RDATA fields with SVCB to allow
> implementations to share the processing of these RR types. So we do
> so.
>
> The interface of this class is straightforward: It has three
> attributes `priority`, `target`, and `params`, which correspond the
> RDATA fields SvcPriority, TargetName, and SvcParams, resp.
>
> SVCB RR type is defined specifically within IN class. Thus, this
> class is placed in the `Resolv::DNS::Resource::IN` namespace.
>
> ## `Resolv::DNS::Resource::IN::SVCB`, `Resolv::DNS::Resource::IN::HTTPS`
>
> Just inherits ServiceBinding class.
>
> ## `Resolv::DNS::SvcParam`
>
> This class represents a pair of a SvcParamKey and a SvcParamValue.
> Aligned with the design of `Resolv::DNS::Resource`, each SvcParamKey
> has its own subclass of `Resolv::DNS::SvcParam`.
>
> ## `Resolv::DNS::SvcParam::Generic`
>
> This is an abstract class representing a SvcParamKey that is unknown
> to this library. `Generic.create(key)` dynamically defines its
> subclass for specific `key`. E.g., `Generic.create(667)` will define
> `Generic::Key667`.
>
> This class holds SvcParamValue in its wire format.
>
> SvcParam with an unknown SvcParamKey will be decoded as a subclass of
> this class. Also, users of this library can generate a non-supported
> SvcParam if they know its wire format.
>
> ## `Resolv::DNS::SvcParams`
>
> This is conceptually a set of `SvcParam`s, whose elements have the
> unique SvcParamKeys. It behaves like a set, and for convenience
> provides indexing by SvcParamKey.
>
> - `#initialize(params)` takes an Enumerable of `SvcParam`s as the
>   initial content. If it contains `SvcParam`s with the duplicate key,
>   the one that appears last takes precedence.
> - `#[](key)` fetches the `SvcParam` with the given key. The key can be
>   specified by its name (e.g., `:alpn`) or number (e.g., `1`).
> - `#add(param)` adds a `SvcParam` to the set. If the set already has a
>   `SvcParam` with the same key, it will be replaced.
> - `#delete(key)` deletes a `SvcParam` by its key and returns it. The key
>   can be specified by its name or number.

* Update comments referring to draft-ietf-dnsop-svcb-https-12

Published as RFC 9460. https://datatracker.ietf.org/doc/rfc9460/

[draft-ietf-dnsop-svcb-https-12]: https://datatracker.ietf.org/doc/draft-ietf-dnsop-svcb-https/12/
[RFC 9460]: https://datatracker.ietf.org/doc/rfc9460/
[DDR]: https://datatracker.ietf.org/doc/draft-ietf-add-ddr/

ruby/resolv@b3ced7f039
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants