ruby / resolv

A thread-aware DNS resolver library written in Ruby
Other
38 stars 29 forks source link

Implement SVCB and HTTPS RRs #32

Closed hanazuki closed 10 months ago

hanazuki commented 1 year ago

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.


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)
sorah commented 10 months ago

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

hanazuki commented 10 months ago

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.