fix(netxlite): reject replies with wrong queryID (#732)
This diff has been extracted from c2f7ccab0e
See https://github.com/ooni/probe/issues/2096
While there, export DecodeReply to decode a raw reply without
interpreting the Rcode or parsing the results, which seems a
nice extra feature to have to more flexibly parse DNS replies
in other parts of the codebase.
			
			
This commit is contained in:
		
							parent
							
								
									f5b801ae95
								
							
						
					
					
						commit
						9d2301cae2
					
				@ -1,20 +1,30 @@
 | 
				
			|||||||
package mocks
 | 
					package mocks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "github.com/ooni/probe-cli/v3/internal/model"
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/miekg/dns"
 | 
				
			||||||
 | 
						"github.com/ooni/probe-cli/v3/internal/model"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DNSDecoder allows mocking dnsx.DNSDecoder.
 | 
					// DNSDecoder allows mocking dnsx.DNSDecoder.
 | 
				
			||||||
type DNSDecoder struct {
 | 
					type DNSDecoder struct {
 | 
				
			||||||
	MockDecodeLookupHost func(qtype uint16, reply []byte) ([]string, error)
 | 
						MockDecodeLookupHost func(qtype uint16, reply []byte, queryID uint16) ([]string, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	MockDecodeHTTPS func(reply []byte) (*model.HTTPSSvc, error)
 | 
						MockDecodeHTTPS func(reply []byte, queryID uint16) (*model.HTTPSSvc, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						MockDecodeReply func(reply []byte) (*dns.Msg, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DecodeLookupHost calls MockDecodeLookupHost.
 | 
					// DecodeLookupHost calls MockDecodeLookupHost.
 | 
				
			||||||
func (e *DNSDecoder) DecodeLookupHost(qtype uint16, reply []byte) ([]string, error) {
 | 
					func (e *DNSDecoder) DecodeLookupHost(qtype uint16, reply []byte, queryID uint16) ([]string, error) {
 | 
				
			||||||
	return e.MockDecodeLookupHost(qtype, reply)
 | 
						return e.MockDecodeLookupHost(qtype, reply, queryID)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DecodeHTTPS calls MockDecodeHTTPS.
 | 
					// DecodeHTTPS calls MockDecodeHTTPS.
 | 
				
			||||||
func (e *DNSDecoder) DecodeHTTPS(reply []byte) (*model.HTTPSSvc, error) {
 | 
					func (e *DNSDecoder) DecodeHTTPS(reply []byte, queryID uint16) (*model.HTTPSSvc, error) {
 | 
				
			||||||
	return e.MockDecodeHTTPS(reply)
 | 
						return e.MockDecodeHTTPS(reply, queryID)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DecodeReply calls MockDecodeReply.
 | 
				
			||||||
 | 
					func (e *DNSDecoder) DecodeReply(reply []byte) (*dns.Msg, error) {
 | 
				
			||||||
 | 
						return e.MockDecodeReply(reply)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -12,11 +12,11 @@ func TestDNSDecoder(t *testing.T) {
 | 
				
			|||||||
	t.Run("DecodeLookupHost", func(t *testing.T) {
 | 
						t.Run("DecodeLookupHost", func(t *testing.T) {
 | 
				
			||||||
		expected := errors.New("mocked error")
 | 
							expected := errors.New("mocked error")
 | 
				
			||||||
		e := &DNSDecoder{
 | 
							e := &DNSDecoder{
 | 
				
			||||||
			MockDecodeLookupHost: func(qtype uint16, reply []byte) ([]string, error) {
 | 
								MockDecodeLookupHost: func(qtype uint16, reply []byte, queryID uint16) ([]string, error) {
 | 
				
			||||||
				return nil, expected
 | 
									return nil, expected
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		out, err := e.DecodeLookupHost(dns.TypeA, make([]byte, 17))
 | 
							out, err := e.DecodeLookupHost(dns.TypeA, make([]byte, 17), dns.Id())
 | 
				
			||||||
		if !errors.Is(err, expected) {
 | 
							if !errors.Is(err, expected) {
 | 
				
			||||||
			t.Fatal("unexpected err", err)
 | 
								t.Fatal("unexpected err", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -28,11 +28,27 @@ func TestDNSDecoder(t *testing.T) {
 | 
				
			|||||||
	t.Run("DecodeHTTPS", func(t *testing.T) {
 | 
						t.Run("DecodeHTTPS", func(t *testing.T) {
 | 
				
			||||||
		expected := errors.New("mocked error")
 | 
							expected := errors.New("mocked error")
 | 
				
			||||||
		e := &DNSDecoder{
 | 
							e := &DNSDecoder{
 | 
				
			||||||
			MockDecodeHTTPS: func(reply []byte) (*model.HTTPSSvc, error) {
 | 
								MockDecodeHTTPS: func(reply []byte, queryID uint16) (*model.HTTPSSvc, error) {
 | 
				
			||||||
				return nil, expected
 | 
									return nil, expected
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		out, err := e.DecodeHTTPS(make([]byte, 17))
 | 
							out, err := e.DecodeHTTPS(make([]byte, 17), dns.Id())
 | 
				
			||||||
 | 
							if !errors.Is(err, expected) {
 | 
				
			||||||
 | 
								t.Fatal("unexpected err", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if out != nil {
 | 
				
			||||||
 | 
								t.Fatal("unexpected out")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("DecodeReply", func(t *testing.T) {
 | 
				
			||||||
 | 
							expected := errors.New("mocked error")
 | 
				
			||||||
 | 
							e := &DNSDecoder{
 | 
				
			||||||
 | 
								MockDecodeReply: func(reply []byte) (*dns.Msg, error) {
 | 
				
			||||||
 | 
									return nil, expected
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							out, err := e.DecodeReply(make([]byte, 17))
 | 
				
			||||||
		if !errors.Is(err, expected) {
 | 
							if !errors.Is(err, expected) {
 | 
				
			||||||
			t.Fatal("unexpected err", err)
 | 
								t.Fatal("unexpected err", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
				
			|||||||
@ -2,10 +2,10 @@ package mocks
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// DNSEncoder allows mocking dnsx.DNSEncoder.
 | 
					// DNSEncoder allows mocking dnsx.DNSEncoder.
 | 
				
			||||||
type DNSEncoder struct {
 | 
					type DNSEncoder struct {
 | 
				
			||||||
	MockEncode func(domain string, qtype uint16, padding bool) ([]byte, error)
 | 
						MockEncode func(domain string, qtype uint16, padding bool) ([]byte, uint16, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Encode calls MockEncode.
 | 
					// Encode calls MockEncode.
 | 
				
			||||||
func (e *DNSEncoder) Encode(domain string, qtype uint16, padding bool) ([]byte, error) {
 | 
					func (e *DNSEncoder) Encode(domain string, qtype uint16, padding bool) ([]byte, uint16, error) {
 | 
				
			||||||
	return e.MockEncode(domain, qtype, padding)
 | 
						return e.MockEncode(domain, qtype, padding)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -11,16 +11,19 @@ func TestDNSEncoder(t *testing.T) {
 | 
				
			|||||||
	t.Run("Encode", func(t *testing.T) {
 | 
						t.Run("Encode", func(t *testing.T) {
 | 
				
			||||||
		expected := errors.New("mocked error")
 | 
							expected := errors.New("mocked error")
 | 
				
			||||||
		e := &DNSEncoder{
 | 
							e := &DNSEncoder{
 | 
				
			||||||
			MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, error) {
 | 
								MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, uint16, error) {
 | 
				
			||||||
				return nil, expected
 | 
									return nil, 0, expected
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		out, err := e.Encode("dns.google", dns.TypeA, true)
 | 
							out, queryID, err := e.Encode("dns.google", dns.TypeA, true)
 | 
				
			||||||
		if !errors.Is(err, expected) {
 | 
							if !errors.Is(err, expected) {
 | 
				
			||||||
			t.Fatal("unexpected err", err)
 | 
								t.Fatal("unexpected err", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if out != nil {
 | 
							if out != nil {
 | 
				
			||||||
			t.Fatal("unexpected out")
 | 
								t.Fatal("unexpected out")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if queryID != 0 {
 | 
				
			||||||
 | 
								t.Fatal("unexpected queryID")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,18 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestHTTPTransport(t *testing.T) {
 | 
					func TestHTTPTransport(t *testing.T) {
 | 
				
			||||||
 | 
						t.Run("Network", func(t *testing.T) {
 | 
				
			||||||
 | 
							expected := "quic"
 | 
				
			||||||
 | 
							txp := &HTTPTransport{
 | 
				
			||||||
 | 
								MockNetwork: func() string {
 | 
				
			||||||
 | 
									return expected
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if txp.Network() != expected {
 | 
				
			||||||
 | 
								t.Fatal("unexpected network value")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	t.Run("RoundTrip", func(t *testing.T) {
 | 
						t.Run("RoundTrip", func(t *testing.T) {
 | 
				
			||||||
		expected := errors.New("mocked error")
 | 
							expected := errors.New("mocked error")
 | 
				
			||||||
		txp := &HTTPTransport{
 | 
							txp := &HTTPTransport{
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,7 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/lucas-clemente/quic-go"
 | 
						"github.com/lucas-clemente/quic-go"
 | 
				
			||||||
 | 
						"github.com/miekg/dns"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
@ -25,6 +26,8 @@ type DNSDecoder interface {
 | 
				
			|||||||
	//
 | 
						//
 | 
				
			||||||
	// - data contains the reply bytes read from a DNSTransport
 | 
						// - data contains the reply bytes read from a DNSTransport
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
 | 
						// - queryID is the original query ID
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
	// Returns:
 | 
						// Returns:
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// - on success, a list of IP addrs inside the reply and a nil error
 | 
						// - on success, a list of IP addrs inside the reply and a nil error
 | 
				
			||||||
@ -33,9 +36,9 @@ type DNSDecoder interface {
 | 
				
			|||||||
	//
 | 
						//
 | 
				
			||||||
	// Note that this function will return an error if there is no
 | 
						// Note that this function will return an error if there is no
 | 
				
			||||||
	// IP address inside of the reply.
 | 
						// IP address inside of the reply.
 | 
				
			||||||
	DecodeLookupHost(qtype uint16, data []byte) ([]string, error)
 | 
						DecodeLookupHost(qtype uint16, data []byte, queryID uint16) ([]string, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// DecodeHTTPS decodes an HTTPS reply.
 | 
						// DecodeHTTPS is like DecodeLookupHost but decodes an HTTPS reply.
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// The argument is the reply as read by the DNSTransport.
 | 
						// The argument is the reply as read by the DNSTransport.
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
@ -46,7 +49,22 @@ type DNSDecoder interface {
 | 
				
			|||||||
	// This function will return an error if the HTTPS reply does not
 | 
						// This function will return an error if the HTTPS reply does not
 | 
				
			||||||
	// contain at least a valid ALPN entry. It will not return
 | 
						// contain at least a valid ALPN entry. It will not return
 | 
				
			||||||
	// an error, though, when there are no IPv4/IPv6 hints in the reply.
 | 
						// an error, though, when there are no IPv4/IPv6 hints in the reply.
 | 
				
			||||||
	DecodeHTTPS(data []byte) (*HTTPSSvc, error)
 | 
						DecodeHTTPS(data []byte, queryID uint16) (*HTTPSSvc, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// DecodeReply decodes a DNS reply message.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Arguments:
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// - data is the raw reply
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// If you use this function, remember that:
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// 1. the Rcode MAY be nonzero;
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// 2. the replyID MAY NOT match the original query ID.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// That is, this is a very basic parsing method.
 | 
				
			||||||
 | 
						DecodeReply(data []byte) (*dns.Msg, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// The DNSEncoder encodes DNS queries to bytes
 | 
					// The DNSEncoder encodes DNS queries to bytes
 | 
				
			||||||
@ -61,9 +79,10 @@ type DNSEncoder interface {
 | 
				
			|||||||
	//
 | 
						//
 | 
				
			||||||
	// - padding is whether to add padding to the query.
 | 
						// - padding is whether to add padding to the query.
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// On success, this function returns a valid byte array and
 | 
						// On success, this function returns a valid byte array, the queryID, and
 | 
				
			||||||
	// a nil error. On failure, we have an error and the byte array is nil.
 | 
						// a nil error. On failure, we have a non-nil error, a nil arrary and a zero
 | 
				
			||||||
	Encode(domain string, qtype uint16, padding bool) ([]byte, error)
 | 
						// query ID.
 | 
				
			||||||
 | 
						Encode(domain string, qtype uint16, padding bool) ([]byte, uint16, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DNSTransport represents an abstract DNS transport.
 | 
					// DNSTransport represents an abstract DNS transport.
 | 
				
			||||||
 | 
				
			|||||||
@ -287,6 +287,9 @@ func classifyResolverError(err error) string {
 | 
				
			|||||||
	if errors.Is(err, ErrOODNSServfail) {
 | 
						if errors.Is(err, ErrOODNSServfail) {
 | 
				
			||||||
		return FailureDNSServfailError
 | 
							return FailureDNSServfailError
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if errors.Is(err, ErrDNSReplyWithWrongQueryID) {
 | 
				
			||||||
 | 
							return FailureDNSReplyWithWrongQueryID
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return classifyGenericError(err)
 | 
						return classifyGenericError(err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -275,6 +275,12 @@ func TestClassifyResolverError(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("for dns reply with wrong queryID", func(t *testing.T) {
 | 
				
			||||||
 | 
							if classifyResolverError(ErrDNSReplyWithWrongQueryID) != FailureDNSReplyWithWrongQueryID {
 | 
				
			||||||
 | 
								t.Fatal("unexpected result")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	t.Run("for another kind of error", func(t *testing.T) {
 | 
						t.Run("for another kind of error", func(t *testing.T) {
 | 
				
			||||||
		if classifyResolverError(io.EOF) != FailureEOFError {
 | 
							if classifyResolverError(io.EOF) != FailureEOFError {
 | 
				
			||||||
			t.Fatal("unexpected result")
 | 
								t.Fatal("unexpected result")
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,8 @@
 | 
				
			|||||||
package netxlite
 | 
					package netxlite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/miekg/dns"
 | 
						"github.com/miekg/dns"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/model"
 | 
						"github.com/ooni/probe-cli/v3/internal/model"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -8,11 +10,26 @@ import (
 | 
				
			|||||||
// DNSDecoderMiekg uses github.com/miekg/dns to implement the Decoder.
 | 
					// DNSDecoderMiekg uses github.com/miekg/dns to implement the Decoder.
 | 
				
			||||||
type DNSDecoderMiekg struct{}
 | 
					type DNSDecoderMiekg struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *DNSDecoderMiekg) parseReply(data []byte) (*dns.Msg, error) {
 | 
					// ErrDNSReplyWithWrongQueryID indicates we have got a DNS reply with the wrong queryID.
 | 
				
			||||||
 | 
					var ErrDNSReplyWithWrongQueryID = errors.New(FailureDNSReplyWithWrongQueryID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DecodeReply implements model.DNSDecoder.DecodeReply
 | 
				
			||||||
 | 
					func (d *DNSDecoderMiekg) DecodeReply(data []byte) (*dns.Msg, error) {
 | 
				
			||||||
	reply := new(dns.Msg)
 | 
						reply := new(dns.Msg)
 | 
				
			||||||
	if err := reply.Unpack(data); err != nil {
 | 
						if err := reply.Unpack(data); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return reply, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *DNSDecoderMiekg) parseReply(data []byte, queryID uint16) (*dns.Msg, error) {
 | 
				
			||||||
 | 
						reply, err := d.DecodeReply(data)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if reply.Id != queryID {
 | 
				
			||||||
 | 
							return nil, ErrDNSReplyWithWrongQueryID
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	// TODO(bassosimone): map more errors to net.DNSError names
 | 
						// TODO(bassosimone): map more errors to net.DNSError names
 | 
				
			||||||
	// TODO(bassosimone): add support for lame referral.
 | 
						// TODO(bassosimone): add support for lame referral.
 | 
				
			||||||
	switch reply.Rcode {
 | 
						switch reply.Rcode {
 | 
				
			||||||
@ -29,8 +46,8 @@ func (d *DNSDecoderMiekg) parseReply(data []byte) (*dns.Msg, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *DNSDecoderMiekg) DecodeHTTPS(data []byte) (*model.HTTPSSvc, error) {
 | 
					func (d *DNSDecoderMiekg) DecodeHTTPS(data []byte, queryID uint16) (*model.HTTPSSvc, error) {
 | 
				
			||||||
	reply, err := d.parseReply(data)
 | 
						reply, err := d.parseReply(data, queryID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -64,8 +81,8 @@ func (d *DNSDecoderMiekg) DecodeHTTPS(data []byte) (*model.HTTPSSvc, error) {
 | 
				
			|||||||
	return out, nil
 | 
						return out, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *DNSDecoderMiekg) DecodeLookupHost(qtype uint16, data []byte) ([]string, error) {
 | 
					func (d *DNSDecoderMiekg) DecodeLookupHost(qtype uint16, data []byte, queryID uint16) ([]string, error) {
 | 
				
			||||||
	reply, err := d.parseReply(data)
 | 
						reply, err := d.parseReply(data, queryID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -8,15 +8,32 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"github.com/google/go-cmp/cmp"
 | 
						"github.com/google/go-cmp/cmp"
 | 
				
			||||||
	"github.com/miekg/dns"
 | 
						"github.com/miekg/dns"
 | 
				
			||||||
 | 
						"github.com/ooni/probe-cli/v3/internal/runtimex"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestDNSDecoder(t *testing.T) {
 | 
					func TestDNSDecoder(t *testing.T) {
 | 
				
			||||||
	t.Run("LookupHost", func(t *testing.T) {
 | 
						t.Run("LookupHost", func(t *testing.T) {
 | 
				
			||||||
		t.Run("UnpackError", func(t *testing.T) {
 | 
							t.Run("UnpackError", func(t *testing.T) {
 | 
				
			||||||
			d := &DNSDecoderMiekg{}
 | 
								d := &DNSDecoderMiekg{}
 | 
				
			||||||
			data, err := d.DecodeLookupHost(dns.TypeA, nil)
 | 
								data, err := d.DecodeLookupHost(dns.TypeA, nil, 0)
 | 
				
			||||||
			if err == nil {
 | 
								if err == nil || err.Error() != "dns: overflow unpacking uint16" {
 | 
				
			||||||
				t.Fatal("expected an error here")
 | 
									t.Fatal("unexpected error", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if data != nil {
 | 
				
			||||||
 | 
									t.Fatal("expected nil data here")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							t.Run("wrong query ID", func(t *testing.T) {
 | 
				
			||||||
 | 
								d := &DNSDecoderMiekg{}
 | 
				
			||||||
 | 
								const (
 | 
				
			||||||
 | 
									queryID     = 17
 | 
				
			||||||
 | 
									unrelatedID = 14
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
								reply := dnsGenLookupHostReplySuccess(dnsGenQuery(dns.TypeA, queryID))
 | 
				
			||||||
 | 
								data, err := d.DecodeLookupHost(dns.TypeA, reply, unrelatedID)
 | 
				
			||||||
 | 
								if !errors.Is(err, ErrDNSReplyWithWrongQueryID) {
 | 
				
			||||||
 | 
									t.Fatal("unexpected error", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if data != nil {
 | 
								if data != nil {
 | 
				
			||||||
				t.Fatal("expected nil data here")
 | 
									t.Fatal("expected nil data here")
 | 
				
			||||||
@ -25,8 +42,9 @@ func TestDNSDecoder(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		t.Run("NXDOMAIN", func(t *testing.T) {
 | 
							t.Run("NXDOMAIN", func(t *testing.T) {
 | 
				
			||||||
			d := &DNSDecoderMiekg{}
 | 
								d := &DNSDecoderMiekg{}
 | 
				
			||||||
			data, err := d.DecodeLookupHost(
 | 
								queryID := dns.Id()
 | 
				
			||||||
				dns.TypeA, dnsGenReplyWithError(t, dns.TypeA, dns.RcodeNameError))
 | 
								data, err := d.DecodeLookupHost(dns.TypeA, dnsGenReplyWithError(
 | 
				
			||||||
 | 
									dnsGenQuery(dns.TypeA, queryID), dns.RcodeNameError), queryID)
 | 
				
			||||||
			if err == nil || !strings.HasSuffix(err.Error(), "no such host") {
 | 
								if err == nil || !strings.HasSuffix(err.Error(), "no such host") {
 | 
				
			||||||
				t.Fatal("not the error we expected", err)
 | 
									t.Fatal("not the error we expected", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -37,8 +55,9 @@ func TestDNSDecoder(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		t.Run("Refused", func(t *testing.T) {
 | 
							t.Run("Refused", func(t *testing.T) {
 | 
				
			||||||
			d := &DNSDecoderMiekg{}
 | 
								d := &DNSDecoderMiekg{}
 | 
				
			||||||
			data, err := d.DecodeLookupHost(
 | 
								queryID := dns.Id()
 | 
				
			||||||
				dns.TypeA, dnsGenReplyWithError(t, dns.TypeA, dns.RcodeRefused))
 | 
								data, err := d.DecodeLookupHost(dns.TypeA, dnsGenReplyWithError(
 | 
				
			||||||
 | 
									dnsGenQuery(dns.TypeA, queryID), dns.RcodeRefused), queryID)
 | 
				
			||||||
			if !errors.Is(err, ErrOODNSRefused) {
 | 
								if !errors.Is(err, ErrOODNSRefused) {
 | 
				
			||||||
				t.Fatal("not the error we expected", err)
 | 
									t.Fatal("not the error we expected", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -49,8 +68,9 @@ func TestDNSDecoder(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		t.Run("Servfail", func(t *testing.T) {
 | 
							t.Run("Servfail", func(t *testing.T) {
 | 
				
			||||||
			d := &DNSDecoderMiekg{}
 | 
								d := &DNSDecoderMiekg{}
 | 
				
			||||||
			data, err := d.DecodeLookupHost(
 | 
								queryID := dns.Id()
 | 
				
			||||||
				dns.TypeA, dnsGenReplyWithError(t, dns.TypeA, dns.RcodeServerFailure))
 | 
								data, err := d.DecodeLookupHost(dns.TypeA, dnsGenReplyWithError(
 | 
				
			||||||
 | 
									dnsGenQuery(dns.TypeA, queryID), dns.RcodeServerFailure), queryID)
 | 
				
			||||||
			if !errors.Is(err, ErrOODNSServfail) {
 | 
								if !errors.Is(err, ErrOODNSServfail) {
 | 
				
			||||||
				t.Fatal("not the error we expected", err)
 | 
									t.Fatal("not the error we expected", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -61,7 +81,9 @@ func TestDNSDecoder(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		t.Run("no address", func(t *testing.T) {
 | 
							t.Run("no address", func(t *testing.T) {
 | 
				
			||||||
			d := &DNSDecoderMiekg{}
 | 
								d := &DNSDecoderMiekg{}
 | 
				
			||||||
			data, err := d.DecodeLookupHost(dns.TypeA, dnsGenLookupHostReplySuccess(t, dns.TypeA))
 | 
								queryID := dns.Id()
 | 
				
			||||||
 | 
								data, err := d.DecodeLookupHost(dns.TypeA, dnsGenLookupHostReplySuccess(
 | 
				
			||||||
 | 
									dnsGenQuery(dns.TypeA, queryID)), queryID)
 | 
				
			||||||
			if !errors.Is(err, ErrOODNSNoAnswer) {
 | 
								if !errors.Is(err, ErrOODNSNoAnswer) {
 | 
				
			||||||
				t.Fatal("not the error we expected", err)
 | 
									t.Fatal("not the error we expected", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -72,8 +94,9 @@ func TestDNSDecoder(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		t.Run("decode A", func(t *testing.T) {
 | 
							t.Run("decode A", func(t *testing.T) {
 | 
				
			||||||
			d := &DNSDecoderMiekg{}
 | 
								d := &DNSDecoderMiekg{}
 | 
				
			||||||
			data, err := d.DecodeLookupHost(
 | 
								queryID := dns.Id()
 | 
				
			||||||
				dns.TypeA, dnsGenLookupHostReplySuccess(t, dns.TypeA, "1.1.1.1", "8.8.8.8"))
 | 
								data, err := d.DecodeLookupHost(dns.TypeA, dnsGenLookupHostReplySuccess(
 | 
				
			||||||
 | 
									dnsGenQuery(dns.TypeA, queryID), "1.1.1.1", "8.8.8.8"), queryID)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				t.Fatal(err)
 | 
									t.Fatal(err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -90,8 +113,9 @@ func TestDNSDecoder(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		t.Run("decode AAAA", func(t *testing.T) {
 | 
							t.Run("decode AAAA", func(t *testing.T) {
 | 
				
			||||||
			d := &DNSDecoderMiekg{}
 | 
								d := &DNSDecoderMiekg{}
 | 
				
			||||||
			data, err := d.DecodeLookupHost(
 | 
								queryID := dns.Id()
 | 
				
			||||||
				dns.TypeAAAA, dnsGenLookupHostReplySuccess(t, dns.TypeAAAA, "::1", "fe80::1"))
 | 
								data, err := d.DecodeLookupHost(dns.TypeAAAA, dnsGenLookupHostReplySuccess(
 | 
				
			||||||
 | 
									dnsGenQuery(dns.TypeAAAA, queryID), "::1", "fe80::1"), queryID)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				t.Fatal(err)
 | 
									t.Fatal(err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -108,8 +132,9 @@ func TestDNSDecoder(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		t.Run("unexpected A reply", func(t *testing.T) {
 | 
							t.Run("unexpected A reply", func(t *testing.T) {
 | 
				
			||||||
			d := &DNSDecoderMiekg{}
 | 
								d := &DNSDecoderMiekg{}
 | 
				
			||||||
			data, err := d.DecodeLookupHost(
 | 
								queryID := dns.Id()
 | 
				
			||||||
				dns.TypeA, dnsGenLookupHostReplySuccess(t, dns.TypeAAAA, "::1", "fe80::1"))
 | 
								data, err := d.DecodeLookupHost(dns.TypeA, dnsGenLookupHostReplySuccess(
 | 
				
			||||||
 | 
									dnsGenQuery(dns.TypeAAAA, queryID), "::1", "fe80::1"), queryID)
 | 
				
			||||||
			if !errors.Is(err, ErrOODNSNoAnswer) {
 | 
								if !errors.Is(err, ErrOODNSNoAnswer) {
 | 
				
			||||||
				t.Fatal("not the error we expected", err)
 | 
									t.Fatal("not the error we expected", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -120,8 +145,9 @@ func TestDNSDecoder(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		t.Run("unexpected AAAA reply", func(t *testing.T) {
 | 
							t.Run("unexpected AAAA reply", func(t *testing.T) {
 | 
				
			||||||
			d := &DNSDecoderMiekg{}
 | 
								d := &DNSDecoderMiekg{}
 | 
				
			||||||
			data, err := d.DecodeLookupHost(
 | 
								queryID := dns.Id()
 | 
				
			||||||
				dns.TypeAAAA, dnsGenLookupHostReplySuccess(t, dns.TypeA, "1.1.1.1", "8.8.4.4."))
 | 
								data, err := d.DecodeLookupHost(dns.TypeAAAA, dnsGenLookupHostReplySuccess(
 | 
				
			||||||
 | 
									dnsGenQuery(dns.TypeA, queryID), "1.1.1.1", "8.8.4.4"), queryID)
 | 
				
			||||||
			if !errors.Is(err, ErrOODNSNoAnswer) {
 | 
								if !errors.Is(err, ErrOODNSNoAnswer) {
 | 
				
			||||||
				t.Fatal("not the error we expected", err)
 | 
									t.Fatal("not the error we expected", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -139,7 +165,7 @@ func TestDNSDecoder(t *testing.T) {
 | 
				
			|||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			t.Fatal(err)
 | 
								t.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		reply, err := d.parseReply(data)
 | 
							reply, err := d.parseReply(data, 0)
 | 
				
			||||||
		if !errors.Is(err, ErrOODNSMisbehaving) { // catch all error
 | 
							if !errors.Is(err, ErrOODNSMisbehaving) { // catch all error
 | 
				
			||||||
			t.Fatal("not the error we expected", err)
 | 
								t.Fatal("not the error we expected", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -151,7 +177,7 @@ func TestDNSDecoder(t *testing.T) {
 | 
				
			|||||||
	t.Run("DecodeHTTPS", func(t *testing.T) {
 | 
						t.Run("DecodeHTTPS", func(t *testing.T) {
 | 
				
			||||||
		t.Run("with nil data", func(t *testing.T) {
 | 
							t.Run("with nil data", func(t *testing.T) {
 | 
				
			||||||
			d := &DNSDecoderMiekg{}
 | 
								d := &DNSDecoderMiekg{}
 | 
				
			||||||
			reply, err := d.DecodeHTTPS(nil)
 | 
								reply, err := d.DecodeHTTPS(nil, 0)
 | 
				
			||||||
			if err == nil || err.Error() != "dns: overflow unpacking uint16" {
 | 
								if err == nil || err.Error() != "dns: overflow unpacking uint16" {
 | 
				
			||||||
				t.Fatal("not the error we expected", err)
 | 
									t.Fatal("not the error we expected", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -160,10 +186,28 @@ func TestDNSDecoder(t *testing.T) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		t.Run("with empty answer", func(t *testing.T) {
 | 
							t.Run("wrong query ID", func(t *testing.T) {
 | 
				
			||||||
			data := dnsGenHTTPSReplySuccess(t, nil, nil, nil)
 | 
					 | 
				
			||||||
			d := &DNSDecoderMiekg{}
 | 
								d := &DNSDecoderMiekg{}
 | 
				
			||||||
			reply, err := d.DecodeHTTPS(data)
 | 
								const (
 | 
				
			||||||
 | 
									queryID     = 17
 | 
				
			||||||
 | 
									unrelatedID = 14
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
								reply := dnsGenHTTPSReplySuccess(dnsGenQuery(dns.TypeA, queryID), nil, nil, nil)
 | 
				
			||||||
 | 
								data, err := d.DecodeLookupHost(dns.TypeA, reply, unrelatedID)
 | 
				
			||||||
 | 
								if !errors.Is(err, ErrDNSReplyWithWrongQueryID) {
 | 
				
			||||||
 | 
									t.Fatal("unexpected error", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if data != nil {
 | 
				
			||||||
 | 
									t.Fatal("expected nil data here")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							t.Run("with empty answer", func(t *testing.T) {
 | 
				
			||||||
 | 
								queryID := dns.Id()
 | 
				
			||||||
 | 
								data := dnsGenHTTPSReplySuccess(
 | 
				
			||||||
 | 
									dnsGenQuery(dns.TypeHTTPS, queryID), nil, nil, nil)
 | 
				
			||||||
 | 
								d := &DNSDecoderMiekg{}
 | 
				
			||||||
 | 
								reply, err := d.DecodeHTTPS(data, queryID)
 | 
				
			||||||
			if !errors.Is(err, ErrOODNSNoAnswer) {
 | 
								if !errors.Is(err, ErrOODNSNoAnswer) {
 | 
				
			||||||
				t.Fatal("unexpected err", err)
 | 
									t.Fatal("unexpected err", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -173,12 +217,14 @@ func TestDNSDecoder(t *testing.T) {
 | 
				
			|||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		t.Run("with full answer", func(t *testing.T) {
 | 
							t.Run("with full answer", func(t *testing.T) {
 | 
				
			||||||
 | 
								queryID := dns.Id()
 | 
				
			||||||
			alpn := []string{"h3"}
 | 
								alpn := []string{"h3"}
 | 
				
			||||||
			v4 := []string{"1.1.1.1"}
 | 
								v4 := []string{"1.1.1.1"}
 | 
				
			||||||
			v6 := []string{"::1"}
 | 
								v6 := []string{"::1"}
 | 
				
			||||||
			data := dnsGenHTTPSReplySuccess(t, alpn, v4, v6)
 | 
								data := dnsGenHTTPSReplySuccess(
 | 
				
			||||||
 | 
									dnsGenQuery(dns.TypeHTTPS, queryID), alpn, v4, v6)
 | 
				
			||||||
			d := &DNSDecoderMiekg{}
 | 
								d := &DNSDecoderMiekg{}
 | 
				
			||||||
			reply, err := d.DecodeHTTPS(data)
 | 
								reply, err := d.DecodeHTTPS(data, queryID)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				t.Fatal(err)
 | 
									t.Fatal(err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -195,64 +241,73 @@ func TestDNSDecoder(t *testing.T) {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// dnsGenReplyWithError generates a DNS reply for the given
 | 
					// dnsGenQuery generates a query suitable to be used with testing.
 | 
				
			||||||
// query type (e.g., dns.TypeA) using code as the Rcode.
 | 
					func dnsGenQuery(qtype uint16, queryID uint16) []byte {
 | 
				
			||||||
func dnsGenReplyWithError(t *testing.T, qtype uint16, code int) []byte {
 | 
					 | 
				
			||||||
	question := dns.Question{
 | 
						question := dns.Question{
 | 
				
			||||||
		Name:   dns.Fqdn("x.org"),
 | 
							Name:   dns.Fqdn("x.org"),
 | 
				
			||||||
		Qtype:  qtype,
 | 
							Qtype:  qtype,
 | 
				
			||||||
		Qclass: dns.ClassINET,
 | 
							Qclass: dns.ClassINET,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	query := new(dns.Msg)
 | 
						query := new(dns.Msg)
 | 
				
			||||||
	query.Id = dns.Id()
 | 
						query.Id = queryID
 | 
				
			||||||
	query.RecursionDesired = true
 | 
						query.RecursionDesired = true
 | 
				
			||||||
	query.Question = make([]dns.Question, 1)
 | 
						query.Question = make([]dns.Question, 1)
 | 
				
			||||||
	query.Question[0] = question
 | 
						query.Question[0] = question
 | 
				
			||||||
 | 
						data, err := query.Pack()
 | 
				
			||||||
 | 
						runtimex.PanicOnError(err, "query.Pack failed")
 | 
				
			||||||
 | 
						return data
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// dnsGenReplyWithError generates a DNS reply for the given
 | 
				
			||||||
 | 
					// query type (e.g., dns.TypeA) using code as the Rcode.
 | 
				
			||||||
 | 
					func dnsGenReplyWithError(rawQuery []byte, code int) []byte {
 | 
				
			||||||
 | 
						query := new(dns.Msg)
 | 
				
			||||||
 | 
						err := query.Unpack(rawQuery)
 | 
				
			||||||
 | 
						runtimex.PanicOnError(err, "query.Unpack failed")
 | 
				
			||||||
	reply := new(dns.Msg)
 | 
						reply := new(dns.Msg)
 | 
				
			||||||
	reply.Compress = true
 | 
						reply.Compress = true
 | 
				
			||||||
	reply.MsgHdr.RecursionAvailable = true
 | 
						reply.MsgHdr.RecursionAvailable = true
 | 
				
			||||||
	reply.SetRcode(query, code)
 | 
						reply.SetRcode(query, code)
 | 
				
			||||||
	data, err := reply.Pack()
 | 
						data, err := reply.Pack()
 | 
				
			||||||
	if err != nil {
 | 
						runtimex.PanicOnError(err, "reply.Pack failed")
 | 
				
			||||||
		t.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return data
 | 
						return data
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// dnsGenLookupHostReplySuccess generates a successful DNS reply for the given
 | 
					// dnsGenLookupHostReplySuccess generates a successful DNS reply for the given
 | 
				
			||||||
// qtype (e.g., dns.TypeA) containing the given ips... in the answer.
 | 
					// qtype (e.g., dns.TypeA) containing the given ips... in the answer.
 | 
				
			||||||
func dnsGenLookupHostReplySuccess(t *testing.T, qtype uint16, ips ...string) []byte {
 | 
					func dnsGenLookupHostReplySuccess(rawQuery []byte, ips ...string) []byte {
 | 
				
			||||||
	question := dns.Question{
 | 
					 | 
				
			||||||
		Name:   dns.Fqdn("x.org"),
 | 
					 | 
				
			||||||
		Qtype:  qtype,
 | 
					 | 
				
			||||||
		Qclass: dns.ClassINET,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	query := new(dns.Msg)
 | 
						query := new(dns.Msg)
 | 
				
			||||||
	query.Id = dns.Id()
 | 
						err := query.Unpack(rawQuery)
 | 
				
			||||||
	query.RecursionDesired = true
 | 
						runtimex.PanicOnError(err, "query.Unpack failed")
 | 
				
			||||||
	query.Question = make([]dns.Question, 1)
 | 
						runtimex.PanicIfFalse(len(query.Question) == 1, "more than one question")
 | 
				
			||||||
	query.Question[0] = question
 | 
						question := query.Question[0]
 | 
				
			||||||
	reply := new(dns.Msg)
 | 
						reply := new(dns.Msg)
 | 
				
			||||||
	reply.Compress = true
 | 
						reply.Compress = true
 | 
				
			||||||
	reply.MsgHdr.RecursionAvailable = true
 | 
						reply.MsgHdr.RecursionAvailable = true
 | 
				
			||||||
	reply.SetReply(query)
 | 
						reply.SetReply(query)
 | 
				
			||||||
	for _, ip := range ips {
 | 
						for _, ip := range ips {
 | 
				
			||||||
		switch qtype {
 | 
							switch question.Qtype {
 | 
				
			||||||
		case dns.TypeA:
 | 
							case dns.TypeA:
 | 
				
			||||||
 | 
								if isIPv6(ip) {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			reply.Answer = append(reply.Answer, &dns.A{
 | 
								reply.Answer = append(reply.Answer, &dns.A{
 | 
				
			||||||
				Hdr: dns.RR_Header{
 | 
									Hdr: dns.RR_Header{
 | 
				
			||||||
					Name:   dns.Fqdn("x.org"),
 | 
										Name:   dns.Fqdn("x.org"),
 | 
				
			||||||
					Rrtype: qtype,
 | 
										Rrtype: question.Qtype,
 | 
				
			||||||
					Class:  dns.ClassINET,
 | 
										Class:  dns.ClassINET,
 | 
				
			||||||
					Ttl:    0,
 | 
										Ttl:    0,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				A: net.ParseIP(ip),
 | 
									A: net.ParseIP(ip),
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		case dns.TypeAAAA:
 | 
							case dns.TypeAAAA:
 | 
				
			||||||
 | 
								if !isIPv6(ip) {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			reply.Answer = append(reply.Answer, &dns.AAAA{
 | 
								reply.Answer = append(reply.Answer, &dns.AAAA{
 | 
				
			||||||
				Hdr: dns.RR_Header{
 | 
									Hdr: dns.RR_Header{
 | 
				
			||||||
					Name:   dns.Fqdn("x.org"),
 | 
										Name:   dns.Fqdn("x.org"),
 | 
				
			||||||
					Rrtype: qtype,
 | 
										Rrtype: question.Qtype,
 | 
				
			||||||
					Class:  dns.ClassINET,
 | 
										Class:  dns.ClassINET,
 | 
				
			||||||
					Ttl:    0,
 | 
										Ttl:    0,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
@ -261,25 +316,16 @@ func dnsGenLookupHostReplySuccess(t *testing.T, qtype uint16, ips ...string) []b
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	data, err := reply.Pack()
 | 
						data, err := reply.Pack()
 | 
				
			||||||
	if err != nil {
 | 
						runtimex.PanicOnError(err, "reply.Pack failed")
 | 
				
			||||||
		t.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return data
 | 
						return data
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// dnsGenHTTPSReplySuccess generates a successful HTTPS response containing
 | 
					// dnsGenHTTPSReplySuccess generates a successful HTTPS response containing
 | 
				
			||||||
// the given (possibly nil) alpns, ipv4s, and ipv6s.
 | 
					// the given (possibly nil) alpns, ipv4s, and ipv6s.
 | 
				
			||||||
func dnsGenHTTPSReplySuccess(t *testing.T, alpns, ipv4s, ipv6s []string) []byte {
 | 
					func dnsGenHTTPSReplySuccess(rawQuery []byte, alpns, ipv4s, ipv6s []string) []byte {
 | 
				
			||||||
	question := dns.Question{
 | 
					 | 
				
			||||||
		Name:   dns.Fqdn("x.org"),
 | 
					 | 
				
			||||||
		Qtype:  dns.TypeHTTPS,
 | 
					 | 
				
			||||||
		Qclass: dns.ClassINET,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	query := new(dns.Msg)
 | 
						query := new(dns.Msg)
 | 
				
			||||||
	query.Id = dns.Id()
 | 
						err := query.Unpack(rawQuery)
 | 
				
			||||||
	query.RecursionDesired = true
 | 
						runtimex.PanicOnError(err, "query.Unpack failed")
 | 
				
			||||||
	query.Question = make([]dns.Question, 1)
 | 
					 | 
				
			||||||
	query.Question[0] = question
 | 
					 | 
				
			||||||
	reply := new(dns.Msg)
 | 
						reply := new(dns.Msg)
 | 
				
			||||||
	reply.Compress = true
 | 
						reply.Compress = true
 | 
				
			||||||
	reply.MsgHdr.RecursionAvailable = true
 | 
						reply.MsgHdr.RecursionAvailable = true
 | 
				
			||||||
@ -315,8 +361,6 @@ func dnsGenHTTPSReplySuccess(t *testing.T, alpns, ipv4s, ipv6s []string) []byte
 | 
				
			|||||||
		answer.Value = append(answer.Value, &dns.SVCBIPv6Hint{Hint: addrs})
 | 
							answer.Value = append(answer.Value, &dns.SVCBIPv6Hint{Hint: addrs})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	data, err := reply.Pack()
 | 
						data, err := reply.Pack()
 | 
				
			||||||
	if err != nil {
 | 
						runtimex.PanicOnError(err, "reply.Pack failed")
 | 
				
			||||||
		t.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return data
 | 
						return data
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,7 @@ const (
 | 
				
			|||||||
	dnsDNSSECEnabled = true
 | 
						dnsDNSSECEnabled = true
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (e *DNSEncoderMiekg) Encode(domain string, qtype uint16, padding bool) ([]byte, error) {
 | 
					func (e *DNSEncoderMiekg) Encode(domain string, qtype uint16, padding bool) ([]byte, uint16, error) {
 | 
				
			||||||
	question := dns.Question{
 | 
						question := dns.Question{
 | 
				
			||||||
		Name:   dns.Fqdn(domain),
 | 
							Name:   dns.Fqdn(domain),
 | 
				
			||||||
		Qtype:  qtype,
 | 
							Qtype:  qtype,
 | 
				
			||||||
@ -43,7 +43,8 @@ func (e *DNSEncoderMiekg) Encode(domain string, qtype uint16, padding bool) ([]b
 | 
				
			|||||||
		opt.Padding = make([]byte, remainder)
 | 
							opt.Padding = make([]byte, remainder)
 | 
				
			||||||
		query.IsEdns0().Option = append(query.IsEdns0().Option, opt)
 | 
							query.IsEdns0().Option = append(query.IsEdns0().Option, opt)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return query.Pack()
 | 
						data, err := query.Pack()
 | 
				
			||||||
 | 
						return data, query.Id, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var _ model.DNSEncoder = &DNSEncoderMiekg{}
 | 
					var _ model.DNSEncoder = &DNSEncoderMiekg{}
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,7 @@ import (
 | 
				
			|||||||
func TestDNSEncoder(t *testing.T) {
 | 
					func TestDNSEncoder(t *testing.T) {
 | 
				
			||||||
	t.Run("encode A", func(t *testing.T) {
 | 
						t.Run("encode A", func(t *testing.T) {
 | 
				
			||||||
		e := &DNSEncoderMiekg{}
 | 
							e := &DNSEncoderMiekg{}
 | 
				
			||||||
		data, err := e.Encode("x.org", dns.TypeA, false)
 | 
							data, _, err := e.Encode("x.org", dns.TypeA, false)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			t.Fatal(err)
 | 
								t.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -19,7 +19,7 @@ func TestDNSEncoder(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	t.Run("encode AAAA", func(t *testing.T) {
 | 
						t.Run("encode AAAA", func(t *testing.T) {
 | 
				
			||||||
		e := &DNSEncoderMiekg{}
 | 
							e := &DNSEncoderMiekg{}
 | 
				
			||||||
		data, err := e.Encode("x.org", dns.TypeAAAA, false)
 | 
							data, _, err := e.Encode("x.org", dns.TypeAAAA, false)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			t.Fatal(err)
 | 
								t.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -31,7 +31,7 @@ func TestDNSEncoder(t *testing.T) {
 | 
				
			|||||||
		// array of values we obtain the right query size.
 | 
							// array of values we obtain the right query size.
 | 
				
			||||||
		getquerylen := func(domainlen int, padding bool) int {
 | 
							getquerylen := func(domainlen int, padding bool) int {
 | 
				
			||||||
			e := &DNSEncoderMiekg{}
 | 
								e := &DNSEncoderMiekg{}
 | 
				
			||||||
			data, err := e.Encode(
 | 
								data, _, err := e.Encode(
 | 
				
			||||||
				// This is not a valid name because it ends up being way
 | 
									// This is not a valid name because it ends up being way
 | 
				
			||||||
				// longer than 255 octets. However, the library is allowing
 | 
									// longer than 255 octets. However, the library is allowing
 | 
				
			||||||
				// us to generate such name and we are not going to send
 | 
									// us to generate such name and we are not going to send
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
// Code generated by go generate; DO NOT EDIT.
 | 
					// Code generated by go generate; DO NOT EDIT.
 | 
				
			||||||
// Generated: 2022-05-13 19:09:07.343096 +0200 CEST m=+0.512294417
 | 
					// Generated: 2022-05-14 18:04:43.744122 +0200 CEST m=+0.315992417
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package netxlite
 | 
					package netxlite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -25,6 +25,7 @@ const (
 | 
				
			|||||||
	FailureDNSNoAnswer                 = "dns_no_answer"
 | 
						FailureDNSNoAnswer                 = "dns_no_answer"
 | 
				
			||||||
	FailureDNSNonRecoverableFailure    = "dns_non_recoverable_failure"
 | 
						FailureDNSNonRecoverableFailure    = "dns_non_recoverable_failure"
 | 
				
			||||||
	FailureDNSRefusedError             = "dns_refused_error"
 | 
						FailureDNSRefusedError             = "dns_refused_error"
 | 
				
			||||||
 | 
						FailureDNSReplyWithWrongQueryID    = "dns_reply_with_wrong_query_id"
 | 
				
			||||||
	FailureDNSServerMisbehaving        = "dns_server_misbehaving"
 | 
						FailureDNSServerMisbehaving        = "dns_server_misbehaving"
 | 
				
			||||||
	FailureDNSServfailError            = "dns_servfail_error"
 | 
						FailureDNSServfailError            = "dns_servfail_error"
 | 
				
			||||||
	FailureDNSTemporaryFailure         = "dns_temporary_failure"
 | 
						FailureDNSTemporaryFailure         = "dns_temporary_failure"
 | 
				
			||||||
@ -75,6 +76,7 @@ var failuresMap = map[string]string{
 | 
				
			|||||||
	"dns_non_recoverable_failure":    "dns_non_recoverable_failure",
 | 
						"dns_non_recoverable_failure":    "dns_non_recoverable_failure",
 | 
				
			||||||
	"dns_nxdomain_error":             "dns_nxdomain_error",
 | 
						"dns_nxdomain_error":             "dns_nxdomain_error",
 | 
				
			||||||
	"dns_refused_error":              "dns_refused_error",
 | 
						"dns_refused_error":              "dns_refused_error",
 | 
				
			||||||
 | 
						"dns_reply_with_wrong_query_id":  "dns_reply_with_wrong_query_id",
 | 
				
			||||||
	"dns_server_misbehaving":         "dns_server_misbehaving",
 | 
						"dns_server_misbehaving":         "dns_server_misbehaving",
 | 
				
			||||||
	"dns_servfail_error":             "dns_servfail_error",
 | 
						"dns_servfail_error":             "dns_servfail_error",
 | 
				
			||||||
	"dns_temporary_failure":          "dns_temporary_failure",
 | 
						"dns_temporary_failure":          "dns_temporary_failure",
 | 
				
			||||||
 | 
				
			|||||||
@ -159,6 +159,7 @@ var Specs = []*ErrorSpec{
 | 
				
			|||||||
	NewLibraryError("DNS_server_misbehaving"),
 | 
						NewLibraryError("DNS_server_misbehaving"),
 | 
				
			||||||
	NewLibraryError("DNS_no_answer"),
 | 
						NewLibraryError("DNS_no_answer"),
 | 
				
			||||||
	NewLibraryError("DNS_servfail_error"),
 | 
						NewLibraryError("DNS_servfail_error"),
 | 
				
			||||||
 | 
						NewLibraryError("DNS_reply_with_wrong_query_ID"),
 | 
				
			||||||
	NewLibraryError("EOF_error"),
 | 
						NewLibraryError("EOF_error"),
 | 
				
			||||||
	NewLibraryError("generic_timeout_error"),
 | 
						NewLibraryError("generic_timeout_error"),
 | 
				
			||||||
	NewLibraryError("QUIC_incompatible_version"),
 | 
						NewLibraryError("QUIC_incompatible_version"),
 | 
				
			||||||
 | 
				
			|||||||
@ -85,7 +85,7 @@ func (r *ParallelResolver) LookupHost(ctx context.Context, hostname string) ([]s
 | 
				
			|||||||
// LookupHTTPS implements Resolver.LookupHTTPS.
 | 
					// LookupHTTPS implements Resolver.LookupHTTPS.
 | 
				
			||||||
func (r *ParallelResolver) LookupHTTPS(
 | 
					func (r *ParallelResolver) LookupHTTPS(
 | 
				
			||||||
	ctx context.Context, hostname string) (*model.HTTPSSvc, error) {
 | 
						ctx context.Context, hostname string) (*model.HTTPSSvc, error) {
 | 
				
			||||||
	querydata, err := r.Encoder.Encode(
 | 
						querydata, queryID, err := r.Encoder.Encode(
 | 
				
			||||||
		hostname, dns.TypeHTTPS, r.Txp.RequiresPadding())
 | 
							hostname, dns.TypeHTTPS, r.Txp.RequiresPadding())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@ -94,7 +94,7 @@ func (r *ParallelResolver) LookupHTTPS(
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return r.Decoder.DecodeHTTPS(replydata)
 | 
						return r.Decoder.DecodeHTTPS(replydata, queryID)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// parallelResolverResult is the internal representation of a
 | 
					// parallelResolverResult is the internal representation of a
 | 
				
			||||||
@ -107,7 +107,7 @@ type parallelResolverResult struct {
 | 
				
			|||||||
// lookupHost issues a lookup host query for the specified qtype (e.g., dns.A).
 | 
					// lookupHost issues a lookup host query for the specified qtype (e.g., dns.A).
 | 
				
			||||||
func (r *ParallelResolver) lookupHost(ctx context.Context, hostname string,
 | 
					func (r *ParallelResolver) lookupHost(ctx context.Context, hostname string,
 | 
				
			||||||
	qtype uint16, out chan<- *parallelResolverResult) {
 | 
						qtype uint16, out chan<- *parallelResolverResult) {
 | 
				
			||||||
	querydata, err := r.Encoder.Encode(hostname, qtype, r.Txp.RequiresPadding())
 | 
						querydata, queryID, err := r.Encoder.Encode(hostname, qtype, r.Txp.RequiresPadding())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		out <- ¶llelResolverResult{
 | 
							out <- ¶llelResolverResult{
 | 
				
			||||||
			addrs: []string{},
 | 
								addrs: []string{},
 | 
				
			||||||
@ -123,7 +123,7 @@ func (r *ParallelResolver) lookupHost(ctx context.Context, hostname string,
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	addrs, err := r.Decoder.DecodeLookupHost(qtype, replydata)
 | 
						addrs, err := r.Decoder.DecodeLookupHost(qtype, replydata, queryID)
 | 
				
			||||||
	out <- ¶llelResolverResult{
 | 
						out <- ¶llelResolverResult{
 | 
				
			||||||
		addrs: addrs,
 | 
							addrs: addrs,
 | 
				
			||||||
		err:   err,
 | 
							err:   err,
 | 
				
			||||||
 | 
				
			|||||||
@ -34,8 +34,8 @@ func TestParallelResolver(t *testing.T) {
 | 
				
			|||||||
			txp := NewDNSOverTLS((&tls.Dialer{}).DialContext, "8.8.8.8:853")
 | 
								txp := NewDNSOverTLS((&tls.Dialer{}).DialContext, "8.8.8.8:853")
 | 
				
			||||||
			r := ParallelResolver{
 | 
								r := ParallelResolver{
 | 
				
			||||||
				Encoder: &mocks.DNSEncoder{
 | 
									Encoder: &mocks.DNSEncoder{
 | 
				
			||||||
					MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, error) {
 | 
										MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, uint16, error) {
 | 
				
			||||||
						return nil, mocked
 | 
											return nil, 0, mocked
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				Txp: txp,
 | 
									Txp: txp,
 | 
				
			||||||
@ -72,7 +72,7 @@ func TestParallelResolver(t *testing.T) {
 | 
				
			|||||||
		t.Run("empty reply", func(t *testing.T) {
 | 
							t.Run("empty reply", func(t *testing.T) {
 | 
				
			||||||
			txp := &mocks.DNSTransport{
 | 
								txp := &mocks.DNSTransport{
 | 
				
			||||||
				MockRoundTrip: func(ctx context.Context, query []byte) (reply []byte, err error) {
 | 
									MockRoundTrip: func(ctx context.Context, query []byte) (reply []byte, err error) {
 | 
				
			||||||
					return dnsGenLookupHostReplySuccess(t, dns.TypeA), nil
 | 
										return dnsGenLookupHostReplySuccess(query), nil
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				MockRequiresPadding: func() bool {
 | 
									MockRequiresPadding: func() bool {
 | 
				
			||||||
					return true
 | 
										return true
 | 
				
			||||||
@ -91,7 +91,7 @@ func TestParallelResolver(t *testing.T) {
 | 
				
			|||||||
		t.Run("with A reply", func(t *testing.T) {
 | 
							t.Run("with A reply", func(t *testing.T) {
 | 
				
			||||||
			txp := &mocks.DNSTransport{
 | 
								txp := &mocks.DNSTransport{
 | 
				
			||||||
				MockRoundTrip: func(ctx context.Context, query []byte) (reply []byte, err error) {
 | 
									MockRoundTrip: func(ctx context.Context, query []byte) (reply []byte, err error) {
 | 
				
			||||||
					return dnsGenLookupHostReplySuccess(t, dns.TypeA, "8.8.8.8"), nil
 | 
										return dnsGenLookupHostReplySuccess(query, "8.8.8.8"), nil
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				MockRequiresPadding: func() bool {
 | 
									MockRequiresPadding: func() bool {
 | 
				
			||||||
					return true
 | 
										return true
 | 
				
			||||||
@ -103,14 +103,14 @@ func TestParallelResolver(t *testing.T) {
 | 
				
			|||||||
				t.Fatal(err)
 | 
									t.Fatal(err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if len(addrs) != 1 || addrs[0] != "8.8.8.8" {
 | 
								if len(addrs) != 1 || addrs[0] != "8.8.8.8" {
 | 
				
			||||||
				t.Fatal("not the result we expected")
 | 
									t.Fatal("not the result we expected", addrs)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		t.Run("with AAAA reply", func(t *testing.T) {
 | 
							t.Run("with AAAA reply", func(t *testing.T) {
 | 
				
			||||||
			txp := &mocks.DNSTransport{
 | 
								txp := &mocks.DNSTransport{
 | 
				
			||||||
				MockRoundTrip: func(ctx context.Context, query []byte) (reply []byte, err error) {
 | 
									MockRoundTrip: func(ctx context.Context, query []byte) (reply []byte, err error) {
 | 
				
			||||||
					return dnsGenLookupHostReplySuccess(t, dns.TypeAAAA, "::1"), nil
 | 
										return dnsGenLookupHostReplySuccess(query, "::1"), nil
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				MockRequiresPadding: func() bool {
 | 
									MockRequiresPadding: func() bool {
 | 
				
			||||||
					return true
 | 
										return true
 | 
				
			||||||
@ -122,7 +122,7 @@ func TestParallelResolver(t *testing.T) {
 | 
				
			|||||||
				t.Fatal(err)
 | 
									t.Fatal(err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if len(addrs) != 1 || addrs[0] != "::1" {
 | 
								if len(addrs) != 1 || addrs[0] != "::1" {
 | 
				
			||||||
				t.Fatal("not the result we expected")
 | 
									t.Fatal("not the result we expected", addrs)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -182,8 +182,8 @@ func TestParallelResolver(t *testing.T) {
 | 
				
			|||||||
			expected := errors.New("mocked error")
 | 
								expected := errors.New("mocked error")
 | 
				
			||||||
			r := &ParallelResolver{
 | 
								r := &ParallelResolver{
 | 
				
			||||||
				Encoder: &mocks.DNSEncoder{
 | 
									Encoder: &mocks.DNSEncoder{
 | 
				
			||||||
					MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, error) {
 | 
										MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, uint16, error) {
 | 
				
			||||||
						return nil, expected
 | 
											return nil, 0, expected
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				Decoder:     nil,
 | 
									Decoder:     nil,
 | 
				
			||||||
@ -208,8 +208,8 @@ func TestParallelResolver(t *testing.T) {
 | 
				
			|||||||
			expected := errors.New("mocked error")
 | 
								expected := errors.New("mocked error")
 | 
				
			||||||
			r := &ParallelResolver{
 | 
								r := &ParallelResolver{
 | 
				
			||||||
				Encoder: &mocks.DNSEncoder{
 | 
									Encoder: &mocks.DNSEncoder{
 | 
				
			||||||
					MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, error) {
 | 
										MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, uint16, error) {
 | 
				
			||||||
						return make([]byte, 64), nil
 | 
											return make([]byte, 64), 0, nil
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				Decoder:     nil,
 | 
									Decoder:     nil,
 | 
				
			||||||
@ -237,12 +237,12 @@ func TestParallelResolver(t *testing.T) {
 | 
				
			|||||||
			expected := errors.New("mocked error")
 | 
								expected := errors.New("mocked error")
 | 
				
			||||||
			r := &ParallelResolver{
 | 
								r := &ParallelResolver{
 | 
				
			||||||
				Encoder: &mocks.DNSEncoder{
 | 
									Encoder: &mocks.DNSEncoder{
 | 
				
			||||||
					MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, error) {
 | 
										MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, uint16, error) {
 | 
				
			||||||
						return make([]byte, 64), nil
 | 
											return make([]byte, 64), 0, nil
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				Decoder: &mocks.DNSDecoder{
 | 
									Decoder: &mocks.DNSDecoder{
 | 
				
			||||||
					MockDecodeHTTPS: func(reply []byte) (*model.HTTPSSvc, error) {
 | 
										MockDecodeHTTPS: func(reply []byte, queryID uint16) (*model.HTTPSSvc, error) {
 | 
				
			||||||
						return nil, expected
 | 
											return nil, expected
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
 | 
				
			|||||||
@ -84,7 +84,7 @@ func (r *SerialResolver) LookupHost(ctx context.Context, hostname string) ([]str
 | 
				
			|||||||
// LookupHTTPS implements Resolver.LookupHTTPS.
 | 
					// LookupHTTPS implements Resolver.LookupHTTPS.
 | 
				
			||||||
func (r *SerialResolver) LookupHTTPS(
 | 
					func (r *SerialResolver) LookupHTTPS(
 | 
				
			||||||
	ctx context.Context, hostname string) (*model.HTTPSSvc, error) {
 | 
						ctx context.Context, hostname string) (*model.HTTPSSvc, error) {
 | 
				
			||||||
	querydata, err := r.Encoder.Encode(
 | 
						querydata, queryID, err := r.Encoder.Encode(
 | 
				
			||||||
		hostname, dns.TypeHTTPS, r.Txp.RequiresPadding())
 | 
							hostname, dns.TypeHTTPS, r.Txp.RequiresPadding())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@ -93,7 +93,7 @@ func (r *SerialResolver) LookupHTTPS(
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return r.Decoder.DecodeHTTPS(replydata)
 | 
						return r.Decoder.DecodeHTTPS(replydata, queryID)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *SerialResolver) lookupHostWithRetry(
 | 
					func (r *SerialResolver) lookupHostWithRetry(
 | 
				
			||||||
@ -126,7 +126,7 @@ func (r *SerialResolver) lookupHostWithRetry(
 | 
				
			|||||||
// qtype (dns.A or dns.AAAA) without retrying on failure.
 | 
					// qtype (dns.A or dns.AAAA) without retrying on failure.
 | 
				
			||||||
func (r *SerialResolver) lookupHostWithoutRetry(
 | 
					func (r *SerialResolver) lookupHostWithoutRetry(
 | 
				
			||||||
	ctx context.Context, hostname string, qtype uint16) ([]string, error) {
 | 
						ctx context.Context, hostname string, qtype uint16) ([]string, error) {
 | 
				
			||||||
	querydata, err := r.Encoder.Encode(hostname, qtype, r.Txp.RequiresPadding())
 | 
						querydata, queryID, err := r.Encoder.Encode(hostname, qtype, r.Txp.RequiresPadding())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -134,5 +134,5 @@ func (r *SerialResolver) lookupHostWithoutRetry(
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return r.Decoder.DecodeLookupHost(qtype, replydata)
 | 
						return r.Decoder.DecodeLookupHost(qtype, replydata, queryID)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,6 @@ import (
 | 
				
			|||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/miekg/dns"
 | 
					 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/atomicx"
 | 
						"github.com/ooni/probe-cli/v3/internal/atomicx"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/model"
 | 
						"github.com/ooni/probe-cli/v3/internal/model"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/model/mocks"
 | 
						"github.com/ooni/probe-cli/v3/internal/model/mocks"
 | 
				
			||||||
@ -51,8 +50,8 @@ func TestSerialResolver(t *testing.T) {
 | 
				
			|||||||
			txp := NewDNSOverTLS((&tls.Dialer{}).DialContext, "8.8.8.8:853")
 | 
								txp := NewDNSOverTLS((&tls.Dialer{}).DialContext, "8.8.8.8:853")
 | 
				
			||||||
			r := SerialResolver{
 | 
								r := SerialResolver{
 | 
				
			||||||
				Encoder: &mocks.DNSEncoder{
 | 
									Encoder: &mocks.DNSEncoder{
 | 
				
			||||||
					MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, error) {
 | 
										MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, uint16, error) {
 | 
				
			||||||
						return nil, mocked
 | 
											return nil, 0, mocked
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				Txp: txp,
 | 
									Txp: txp,
 | 
				
			||||||
@ -89,7 +88,7 @@ func TestSerialResolver(t *testing.T) {
 | 
				
			|||||||
		t.Run("empty reply", func(t *testing.T) {
 | 
							t.Run("empty reply", func(t *testing.T) {
 | 
				
			||||||
			txp := &mocks.DNSTransport{
 | 
								txp := &mocks.DNSTransport{
 | 
				
			||||||
				MockRoundTrip: func(ctx context.Context, query []byte) (reply []byte, err error) {
 | 
									MockRoundTrip: func(ctx context.Context, query []byte) (reply []byte, err error) {
 | 
				
			||||||
					return dnsGenLookupHostReplySuccess(t, dns.TypeA), nil
 | 
										return dnsGenLookupHostReplySuccess(query), nil
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				MockRequiresPadding: func() bool {
 | 
									MockRequiresPadding: func() bool {
 | 
				
			||||||
					return true
 | 
										return true
 | 
				
			||||||
@ -108,7 +107,7 @@ func TestSerialResolver(t *testing.T) {
 | 
				
			|||||||
		t.Run("with A reply", func(t *testing.T) {
 | 
							t.Run("with A reply", func(t *testing.T) {
 | 
				
			||||||
			txp := &mocks.DNSTransport{
 | 
								txp := &mocks.DNSTransport{
 | 
				
			||||||
				MockRoundTrip: func(ctx context.Context, query []byte) (reply []byte, err error) {
 | 
									MockRoundTrip: func(ctx context.Context, query []byte) (reply []byte, err error) {
 | 
				
			||||||
					return dnsGenLookupHostReplySuccess(t, dns.TypeA, "8.8.8.8"), nil
 | 
										return dnsGenLookupHostReplySuccess(query, "8.8.8.8"), nil
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				MockRequiresPadding: func() bool {
 | 
									MockRequiresPadding: func() bool {
 | 
				
			||||||
					return true
 | 
										return true
 | 
				
			||||||
@ -127,7 +126,7 @@ func TestSerialResolver(t *testing.T) {
 | 
				
			|||||||
		t.Run("with AAAA reply", func(t *testing.T) {
 | 
							t.Run("with AAAA reply", func(t *testing.T) {
 | 
				
			||||||
			txp := &mocks.DNSTransport{
 | 
								txp := &mocks.DNSTransport{
 | 
				
			||||||
				MockRoundTrip: func(ctx context.Context, query []byte) (reply []byte, err error) {
 | 
									MockRoundTrip: func(ctx context.Context, query []byte) (reply []byte, err error) {
 | 
				
			||||||
					return dnsGenLookupHostReplySuccess(t, dns.TypeAAAA, "::1"), nil
 | 
										return dnsGenLookupHostReplySuccess(query, "::1"), nil
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				MockRequiresPadding: func() bool {
 | 
									MockRequiresPadding: func() bool {
 | 
				
			||||||
					return true
 | 
										return true
 | 
				
			||||||
@ -189,8 +188,8 @@ func TestSerialResolver(t *testing.T) {
 | 
				
			|||||||
			expected := errors.New("mocked error")
 | 
								expected := errors.New("mocked error")
 | 
				
			||||||
			r := &SerialResolver{
 | 
								r := &SerialResolver{
 | 
				
			||||||
				Encoder: &mocks.DNSEncoder{
 | 
									Encoder: &mocks.DNSEncoder{
 | 
				
			||||||
					MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, error) {
 | 
										MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, uint16, error) {
 | 
				
			||||||
						return nil, expected
 | 
											return nil, 0, expected
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				Decoder:     nil,
 | 
									Decoder:     nil,
 | 
				
			||||||
@ -215,8 +214,8 @@ func TestSerialResolver(t *testing.T) {
 | 
				
			|||||||
			expected := errors.New("mocked error")
 | 
								expected := errors.New("mocked error")
 | 
				
			||||||
			r := &SerialResolver{
 | 
								r := &SerialResolver{
 | 
				
			||||||
				Encoder: &mocks.DNSEncoder{
 | 
									Encoder: &mocks.DNSEncoder{
 | 
				
			||||||
					MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, error) {
 | 
										MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, uint16, error) {
 | 
				
			||||||
						return make([]byte, 64), nil
 | 
											return make([]byte, 64), 0, nil
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				Decoder:     nil,
 | 
									Decoder:     nil,
 | 
				
			||||||
@ -244,12 +243,12 @@ func TestSerialResolver(t *testing.T) {
 | 
				
			|||||||
			expected := errors.New("mocked error")
 | 
								expected := errors.New("mocked error")
 | 
				
			||||||
			r := &SerialResolver{
 | 
								r := &SerialResolver{
 | 
				
			||||||
				Encoder: &mocks.DNSEncoder{
 | 
									Encoder: &mocks.DNSEncoder{
 | 
				
			||||||
					MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, error) {
 | 
										MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, uint16, error) {
 | 
				
			||||||
						return make([]byte, 64), nil
 | 
											return make([]byte, 64), 0, nil
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				Decoder: &mocks.DNSDecoder{
 | 
									Decoder: &mocks.DNSDecoder{
 | 
				
			||||||
					MockDecodeHTTPS: func(reply []byte) (*model.HTTPSSvc, error) {
 | 
										MockDecodeHTTPS: func(reply []byte, queryID uint16) (*model.HTTPSSvc, error) {
 | 
				
			||||||
						return nil, expected
 | 
											return nil, expected
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user