refactor: DNSTransport I/Os DNS messages (#760)
This diff refactors the DNSTransport model to receive in input a DNSQuery and return in output a DNSResponse. The design of DNSQuery and DNSResponse takes into account the use case of a transport using getaddrinfo, meaning that we don't need to serialize and deserialize messages when using getaddrinfo. The current codebase does not use a getaddrinfo transport, but I wrote one such a transport in the Websteps Winter 2021 prototype (https://github.com/bassosimone/websteps-illustrated/). The design conversation that lead to producing this diff is https://github.com/ooni/probe/issues/2099
This commit is contained in:
parent
7a0a156aec
commit
01a513a496
35 changed files with 1694 additions and 1039 deletions
|
|
@ -15,14 +15,16 @@ import (
|
|||
// DNSDecoderMiekg uses github.com/miekg/dns to implement the Decoder.
|
||||
type DNSDecoderMiekg struct{}
|
||||
|
||||
// ErrDNSReplyWithWrongQueryID indicates we have got a DNS reply with the wrong queryID.
|
||||
var ErrDNSReplyWithWrongQueryID = errors.New(FailureDNSReplyWithWrongQueryID)
|
||||
var (
|
||||
// ErrDNSReplyWithWrongQueryID indicates we have got a DNS reply with the wrong queryID.
|
||||
ErrDNSReplyWithWrongQueryID = errors.New(FailureDNSReplyWithWrongQueryID)
|
||||
|
||||
// ErrDNSIsQuery indicates that we were passed a DNS query.
|
||||
var ErrDNSIsQuery = errors.New("ooresolver: expected response but received query")
|
||||
// ErrDNSIsQuery indicates that we were passed a DNS query.
|
||||
ErrDNSIsQuery = errors.New("ooresolver: expected response but received query")
|
||||
)
|
||||
|
||||
// DecodeReply implements model.DNSDecoder.DecodeReply
|
||||
func (d *DNSDecoderMiekg) DecodeReply(data []byte) (*dns.Msg, error) {
|
||||
// DecodeResponse implements model.DNSDecoder.DecodeResponse.
|
||||
func (d *DNSDecoderMiekg) DecodeResponse(data []byte, query model.DNSQuery) (model.DNSResponse, error) {
|
||||
reply := &dns.Msg{}
|
||||
if err := reply.Unpack(data); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -30,46 +32,64 @@ func (d *DNSDecoderMiekg) DecodeReply(data []byte) (*dns.Msg, error) {
|
|||
if !reply.Response {
|
||||
return nil, ErrDNSIsQuery
|
||||
}
|
||||
return reply, nil
|
||||
}
|
||||
|
||||
// decodeSuccessfulReply decodes the bytes in data as a successful reply for the
|
||||
// given queryID. This function returns an error if:
|
||||
//
|
||||
// 1. we cannot decode data
|
||||
//
|
||||
// 2. the decoded message is not a reply
|
||||
//
|
||||
// 3. the query ID does not match
|
||||
//
|
||||
// 4. the Rcode is not zero.
|
||||
func (d *DNSDecoderMiekg) decodeSuccessfulReply(data []byte, queryID uint16) (*dns.Msg, error) {
|
||||
reply, err := d.DecodeReply(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if reply.Id != queryID {
|
||||
if reply.Id != query.ID() {
|
||||
return nil, ErrDNSReplyWithWrongQueryID
|
||||
}
|
||||
resp := &dnsResponse{
|
||||
bytes: data,
|
||||
msg: reply,
|
||||
query: query,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// dnsResponse implements model.DNSResponse.
|
||||
type dnsResponse struct {
|
||||
// bytes contains the response bytes.
|
||||
bytes []byte
|
||||
|
||||
// msg contains the message.
|
||||
msg *dns.Msg
|
||||
|
||||
// query is the original query.
|
||||
query model.DNSQuery
|
||||
}
|
||||
|
||||
// Query implements model.DNSResponse.Query.
|
||||
func (r *dnsResponse) Query() model.DNSQuery {
|
||||
return r.query
|
||||
}
|
||||
|
||||
// Bytes implements model.DNSResponse.Bytes.
|
||||
func (r *dnsResponse) Bytes() []byte {
|
||||
return r.bytes
|
||||
}
|
||||
|
||||
// Rcode implements model.DNSResponse.Rcode.
|
||||
func (r *dnsResponse) Rcode() int {
|
||||
return r.msg.Rcode
|
||||
}
|
||||
|
||||
func (r *dnsResponse) rcodeToError() error {
|
||||
// TODO(bassosimone): map more errors to net.DNSError names
|
||||
// TODO(bassosimone): add support for lame referral.
|
||||
switch reply.Rcode {
|
||||
switch r.msg.Rcode {
|
||||
case dns.RcodeSuccess:
|
||||
return reply, nil
|
||||
return nil
|
||||
case dns.RcodeNameError:
|
||||
return nil, ErrOODNSNoSuchHost
|
||||
return ErrOODNSNoSuchHost
|
||||
case dns.RcodeRefused:
|
||||
return nil, ErrOODNSRefused
|
||||
return ErrOODNSRefused
|
||||
case dns.RcodeServerFailure:
|
||||
return nil, ErrOODNSServfail
|
||||
return ErrOODNSServfail
|
||||
default:
|
||||
return nil, ErrOODNSMisbehaving
|
||||
return ErrOODNSMisbehaving
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSDecoderMiekg) DecodeHTTPS(data []byte, queryID uint16) (*model.HTTPSSvc, error) {
|
||||
reply, err := d.decodeSuccessfulReply(data, queryID)
|
||||
if err != nil {
|
||||
// DecodeHTTPS implements model.DNSResponse.DecodeHTTPS.
|
||||
func (r *dnsResponse) DecodeHTTPS() (*model.HTTPSSvc, error) {
|
||||
if err := r.rcodeToError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := &model.HTTPSSvc{
|
||||
|
|
@ -77,7 +97,7 @@ func (d *DNSDecoderMiekg) DecodeHTTPS(data []byte, queryID uint16) (*model.HTTPS
|
|||
IPv4: []string{}, // ensure it's not nil
|
||||
IPv6: []string{}, // ensure it's not nil
|
||||
}
|
||||
for _, answer := range reply.Answer {
|
||||
for _, answer := range r.msg.Answer {
|
||||
switch avalue := answer.(type) {
|
||||
case *dns.HTTPS:
|
||||
for _, v := range avalue.Value {
|
||||
|
|
@ -102,14 +122,14 @@ func (d *DNSDecoderMiekg) DecodeHTTPS(data []byte, queryID uint16) (*model.HTTPS
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (d *DNSDecoderMiekg) DecodeLookupHost(qtype uint16, data []byte, queryID uint16) ([]string, error) {
|
||||
reply, err := d.decodeSuccessfulReply(data, queryID)
|
||||
if err != nil {
|
||||
// DecodeLookupHost implements model.DNSResponse.DecodeLookupHost.
|
||||
func (r *dnsResponse) DecodeLookupHost() ([]string, error) {
|
||||
if err := r.rcodeToError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var addrs []string
|
||||
for _, answer := range reply.Answer {
|
||||
switch qtype {
|
||||
for _, answer := range r.msg.Answer {
|
||||
switch r.Query().Type() {
|
||||
case dns.TypeA:
|
||||
if rra, ok := answer.(*dns.A); ok {
|
||||
ip := rra.A
|
||||
|
|
@ -128,13 +148,13 @@ func (d *DNSDecoderMiekg) DecodeLookupHost(qtype uint16, data []byte, queryID ui
|
|||
return addrs, nil
|
||||
}
|
||||
|
||||
func (d *DNSDecoderMiekg) DecodeNS(data []byte, queryID uint16) ([]*net.NS, error) {
|
||||
reply, err := d.decodeSuccessfulReply(data, queryID)
|
||||
if err != nil {
|
||||
// DecodeNS implements model.DNSResponse.DecodeNS.
|
||||
func (r *dnsResponse) DecodeNS() ([]*net.NS, error) {
|
||||
if err := r.rcodeToError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := []*net.NS{}
|
||||
for _, answer := range reply.Answer {
|
||||
for _, answer := range r.msg.Answer {
|
||||
switch avalue := answer.(type) {
|
||||
case *dns.NS:
|
||||
out = append(out, &net.NS{Host: avalue.Ns})
|
||||
|
|
@ -147,3 +167,4 @@ func (d *DNSDecoderMiekg) DecodeNS(data []byte, queryID uint16) ([]*net.NS, erro
|
|||
}
|
||||
|
||||
var _ model.DNSDecoder = &DNSDecoderMiekg{}
|
||||
var _ model.DNSResponse = &dnsResponse{}
|
||||
|
|
|
|||
Loading…
Reference in a new issue