doc(netxlite): revamp the documentation (#523)
Part of https://github.com/ooni/probe-cli/pull/506. In parallel with tutorials, we also need to make sure we have good documentation.
This commit is contained in:
		
							parent
							
								
									b9a844ecee
								
							
						
					
					
						commit
						b2b1a4b2f1
					
				@ -1,3 +1,19 @@
 | 
				
			|||||||
# Directory github.com/ooni/probe-cli/internal
 | 
					# Directory github.com/ooni/probe-cli/internal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This directory contains private Go packages.
 | 
					This directory contains private Go packages.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					As a reminder, you can always check the Go documentation of
 | 
				
			||||||
 | 
					a package by using
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					go doc -all ./internal/$package
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					where `$package` is the name of the package.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Some notable packages:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [netxlite](netxlite) is the underlying networking library;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [tutorial](tutorial) contains tutorials on writing new experiments,
 | 
				
			||||||
 | 
					using measurements libraries, and networking code.
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
// Code generated by go generate; DO NOT EDIT.
 | 
					// Code generated by go generate; DO NOT EDIT.
 | 
				
			||||||
// 2021-09-29 10:21:32.800846 +0200 CEST m=+0.427651209
 | 
					// 2021-09-29 16:40:31.464953 +0200 CEST m=+0.466236210
 | 
				
			||||||
// https://curl.haxx.se/ca/cacert.pem
 | 
					// https://curl.haxx.se/ca/cacert.pem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package netxlite
 | 
					package netxlite
 | 
				
			||||||
 | 
				
			|||||||
@ -11,28 +11,29 @@ import (
 | 
				
			|||||||
	"github.com/ooni/probe-cli/v3/internal/scrubber"
 | 
						"github.com/ooni/probe-cli/v3/internal/scrubber"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ClassifyGenericError is the generic classifier mapping an error
 | 
					// ClassifyGenericError is maps an error occurred during an operation
 | 
				
			||||||
// occurred during an operation to an OONI failure string.
 | 
					// to an OONI failure string. This specific classifier is the most
 | 
				
			||||||
 | 
					// generic one. You usually use it when mapping I/O errors. You should
 | 
				
			||||||
 | 
					// check whether there is a specific classifier for more specific
 | 
				
			||||||
 | 
					// operations (e.g., DNS resolution, TLS handshake).
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// If the input error is already an ErrWrapper we don't perform
 | 
					// If the input error is an *ErrWrapper we don't perform
 | 
				
			||||||
// the classification again and we return its Failure to the caller.
 | 
					// the classification again and we return its Failure.
 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Classification rules
 | 
					 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// We put inside this classifier:
 | 
					// We put inside this classifier:
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// - system call errors
 | 
					// - system call errors;
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// - generic errors that can occur in multiple places
 | 
					// - generic errors that can occur in multiple places;
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// - all the errors that depend on strings
 | 
					// - all the errors that depend on strings.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// The more specific classifiers will call this classifier if
 | 
					// The more specific classifiers will call this classifier if
 | 
				
			||||||
// they fail to find a mapping for the input error.
 | 
					// they fail to find a mapping for the input error.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// If everything else fails, this classifier returns a string
 | 
					// If everything else fails, this classifier returns a string
 | 
				
			||||||
// like "unknown_failure: XXX" where XXX has been scrubbed
 | 
					// like "unknown_failure: XXX" where XXX has been scrubbed
 | 
				
			||||||
// so to remove any network endpoints from its value.
 | 
					// so to remove any network endpoints from the original error string.
 | 
				
			||||||
func ClassifyGenericError(err error) string {
 | 
					func ClassifyGenericError(err error) string {
 | 
				
			||||||
	// The list returned here matches the values used by MK unless
 | 
						// The list returned here matches the values used by MK unless
 | 
				
			||||||
	// explicitly noted otherwise with a comment.
 | 
						// explicitly noted otherwise with a comment.
 | 
				
			||||||
@ -133,14 +134,14 @@ const (
 | 
				
			|||||||
	quicTLSUnrecognizedName = 112
 | 
						quicTLSUnrecognizedName = 112
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ClassifyQUICHandshakeError maps an error occurred during the QUIC
 | 
					// ClassifyQUICHandshakeError maps errors during a QUIC
 | 
				
			||||||
// handshake to an OONI failure string.
 | 
					// handshake to OONI failure strings.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// If the input error is already an ErrWrapper we don't perform
 | 
					// If the input error is an *ErrWrapper we don't perform
 | 
				
			||||||
// the classification again and we return its Failure to the caller.
 | 
					// the classification again and we return its Failure.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// If this classifier fails, it calls ClassifyGenericError and
 | 
					// If this classifier fails, it calls ClassifyGenericError
 | 
				
			||||||
// returns to the caller its return value.
 | 
					// and returns to the caller its return value.
 | 
				
			||||||
func ClassifyQUICHandshakeError(err error) string {
 | 
					func ClassifyQUICHandshakeError(err error) string {
 | 
				
			||||||
	var errwrapper *ErrWrapper
 | 
						var errwrapper *ErrWrapper
 | 
				
			||||||
	if errors.As(err, &errwrapper) {
 | 
						if errors.As(err, &errwrapper) {
 | 
				
			||||||
@ -229,14 +230,17 @@ func quicIsCertificateError(alert uint8) bool {
 | 
				
			|||||||
// filters for DNS bogons MUST use this error.
 | 
					// filters for DNS bogons MUST use this error.
 | 
				
			||||||
var ErrDNSBogon = errors.New("dns: detected bogon address")
 | 
					var ErrDNSBogon = errors.New("dns: detected bogon address")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// These strings are same as the standard library.
 | 
					// We use these strings to string-match errors in the standard library
 | 
				
			||||||
 | 
					// and map such errors to OONI failures.
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	DNSNoSuchHostSuffix        = "no such host"
 | 
						DNSNoSuchHostSuffix        = "no such host"
 | 
				
			||||||
	DNSServerMisbehavingSuffix = "server misbehaving"
 | 
						DNSServerMisbehavingSuffix = "server misbehaving"
 | 
				
			||||||
	DNSNoAnswerSuffix          = "no answer from DNS server"
 | 
						DNSNoAnswerSuffix          = "no answer from DNS server"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// These errors are returned by the decoder and/or the serial resolver.
 | 
					// These errors are returned by custom DNSTransport instances (e.g.,
 | 
				
			||||||
 | 
					// DNSOverHTTPS and DNSOverUDP). Their suffix matches the equivalent
 | 
				
			||||||
 | 
					// unexported errors used by the Go standard library.
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	ErrOODNSNoSuchHost  = fmt.Errorf("ooniresolver: %s", DNSNoSuchHostSuffix)
 | 
						ErrOODNSNoSuchHost  = fmt.Errorf("ooniresolver: %s", DNSNoSuchHostSuffix)
 | 
				
			||||||
	ErrOODNSRefused     = errors.New("ooniresolver: refused")
 | 
						ErrOODNSRefused     = errors.New("ooniresolver: refused")
 | 
				
			||||||
@ -244,11 +248,11 @@ var (
 | 
				
			|||||||
	ErrOODNSNoAnswer    = fmt.Errorf("ooniresolver: %s", DNSNoAnswerSuffix)
 | 
						ErrOODNSNoAnswer    = fmt.Errorf("ooniresolver: %s", DNSNoAnswerSuffix)
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ClassifyResolverError maps an error occurred during a domain name
 | 
					// ClassifyResolverError maps DNS resolution errors to
 | 
				
			||||||
// resolution to the corresponding OONI failure string.
 | 
					// OONI failure strings.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// If the input error is already an ErrWrapper we don't perform
 | 
					// If the input error is an *ErrWrapper we don't perform
 | 
				
			||||||
// the classification again and we return its Failure to the caller.
 | 
					// the classification again and we return its Failure.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// If this classifier fails, it calls ClassifyGenericError and
 | 
					// If this classifier fails, it calls ClassifyGenericError and
 | 
				
			||||||
// returns to the caller its return value.
 | 
					// returns to the caller its return value.
 | 
				
			||||||
@ -271,8 +275,8 @@ func ClassifyResolverError(err error) string {
 | 
				
			|||||||
// ClassifyTLSHandshakeError maps an error occurred during the TLS
 | 
					// ClassifyTLSHandshakeError maps an error occurred during the TLS
 | 
				
			||||||
// handshake to an OONI failure string.
 | 
					// handshake to an OONI failure string.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// If the input error is already an ErrWrapper we don't perform
 | 
					// If the input error is an *ErrWrapper we don't perform
 | 
				
			||||||
// the classification again and we return its Failure to the caller.
 | 
					// the classification again and we return its Failure.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// If this classifier fails, it calls ClassifyGenericError and
 | 
					// If this classifier fails, it calls ClassifyGenericError and
 | 
				
			||||||
// returns to the caller its return value.
 | 
					// returns to the caller its return value.
 | 
				
			||||||
 | 
				
			|||||||
@ -17,8 +17,7 @@ type Dialer interface {
 | 
				
			|||||||
	CloseIdleConnections()
 | 
						CloseIdleConnections()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewDialerWithResolver is a convenience factory that calls
 | 
					// NewDialerWithResolver calls WrapDialer for the stdlib dialer.
 | 
				
			||||||
// WrapDialer for a stdlib dialer type.
 | 
					 | 
				
			||||||
func NewDialerWithResolver(logger Logger, resolver Resolver) Dialer {
 | 
					func NewDialerWithResolver(logger Logger, resolver Resolver) Dialer {
 | 
				
			||||||
	return WrapDialer(logger, resolver, &dialerSystem{})
 | 
						return WrapDialer(logger, resolver, &dialerSystem{})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -30,14 +29,14 @@ func NewDialerWithResolver(logger Logger, resolver Resolver) Dialer {
 | 
				
			|||||||
//
 | 
					//
 | 
				
			||||||
// 2. resolves domain names using the givern resolver;
 | 
					// 2. resolves domain names using the givern resolver;
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// 3. when using a resolver, each available enpoint is tried
 | 
					// 3. when the resolver is not a "null" resolver,
 | 
				
			||||||
 | 
					// each available enpoint is tried
 | 
				
			||||||
// sequentially. On error, the code will return what it believes
 | 
					// sequentially. On error, the code will return what it believes
 | 
				
			||||||
// to be the most representative error in the pack. Most often,
 | 
					// to be the most representative error in the pack. Most often,
 | 
				
			||||||
// such an error is the first one that occurred. Choosing the
 | 
					// the first error that occurred. Choosing the
 | 
				
			||||||
// error to return using this logic is a QUIRK that we owe
 | 
					// error to return using this logic is a QUIRK that we owe
 | 
				
			||||||
// to the original implementation of netx. We cannot change
 | 
					// to the original implementation of netx. We cannot change
 | 
				
			||||||
// this behavior until all the legacy code that relies on
 | 
					// this behavior until we refactor legacy code using it.
 | 
				
			||||||
// it has been migrated to more sane patterns.
 | 
					 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Removing this quirk from the codebase is documented as
 | 
					// Removing this quirk from the codebase is documented as
 | 
				
			||||||
// TODO(https://github.com/ooni/probe/issues/1779).
 | 
					// TODO(https://github.com/ooni/probe/issues/1779).
 | 
				
			||||||
@ -49,6 +48,9 @@ func NewDialerWithResolver(logger Logger, resolver Resolver) Dialer {
 | 
				
			|||||||
// 6. if a dialer wraps a resolver, the dialer will forward
 | 
					// 6. if a dialer wraps a resolver, the dialer will forward
 | 
				
			||||||
// the CloseIdleConnection call to its resolver (which is
 | 
					// the CloseIdleConnection call to its resolver (which is
 | 
				
			||||||
// instrumental to manage a DoH resolver connections properly).
 | 
					// instrumental to manage a DoH resolver connections properly).
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// In general, do not use WrapDialer directly but try to use
 | 
				
			||||||
 | 
					// more high-level factories, e.g., NewDialerWithResolver.
 | 
				
			||||||
func WrapDialer(logger Logger, resolver Resolver, dialer Dialer) Dialer {
 | 
					func WrapDialer(logger Logger, resolver Resolver, dialer Dialer) Dialer {
 | 
				
			||||||
	return &dialerLogger{
 | 
						return &dialerLogger{
 | 
				
			||||||
		Dialer: &dialerResolver{
 | 
							Dialer: &dialerResolver{
 | 
				
			||||||
@ -65,8 +67,9 @@ func WrapDialer(logger Logger, resolver Resolver, dialer Dialer) Dialer {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewDialerWithoutResolver is like NewDialerWithResolver except that
 | 
					// NewDialerWithoutResolver calls NewDialerWithResolver with a "null" resolver.
 | 
				
			||||||
// it will fail with ErrNoResolver if passed a domain name.
 | 
					//
 | 
				
			||||||
 | 
					// The returned dialer fails with ErrNoResolver if passed a domain name.
 | 
				
			||||||
func NewDialerWithoutResolver(logger Logger) Dialer {
 | 
					func NewDialerWithoutResolver(logger Logger) Dialer {
 | 
				
			||||||
	return NewDialerWithResolver(logger, &nullResolver{})
 | 
						return NewDialerWithResolver(logger, &nullResolver{})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -183,12 +186,15 @@ func (d *dialerLogger) CloseIdleConnections() {
 | 
				
			|||||||
	d.Dialer.CloseIdleConnections()
 | 
						d.Dialer.CloseIdleConnections()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ErrNoConnReuse indicates we cannot reuse the connection provided
 | 
					// ErrNoConnReuse is the type of error returned when you create a
 | 
				
			||||||
// to a single use (possibly TLS) dialer.
 | 
					// "single use" dialer or a "single use" TLS dialer and you dial
 | 
				
			||||||
 | 
					// more than once, which is not supported by such a dialer.
 | 
				
			||||||
var ErrNoConnReuse = errors.New("cannot reuse connection")
 | 
					var ErrNoConnReuse = errors.New("cannot reuse connection")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewSingleUseDialer returns a dialer that returns the given connection once
 | 
					// NewSingleUseDialer returns a "single use" dialer. The first
 | 
				
			||||||
// and after that always fails with the ErrNoConnReuse error.
 | 
					// dial will succed and return conn regardless of the network
 | 
				
			||||||
 | 
					// and address arguments passed to DialContext. Any subsequent
 | 
				
			||||||
 | 
					// dial returns ErrNoConnReuse.
 | 
				
			||||||
func NewSingleUseDialer(conn net.Conn) Dialer {
 | 
					func NewSingleUseDialer(conn net.Conn) Dialer {
 | 
				
			||||||
	return &dialerSingleUse{conn: conn}
 | 
						return &dialerSingleUse{conn: conn}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -263,10 +269,11 @@ func (c *dialerErrWrapperConn) Close() error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ErrNoDialer indicates that no dialer is configured.
 | 
					// ErrNoDialer is the type of error returned by "null" dialers
 | 
				
			||||||
 | 
					// when you attempt to dial with them.
 | 
				
			||||||
var ErrNoDialer = errors.New("no configured dialer")
 | 
					var ErrNoDialer = errors.New("no configured dialer")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewNullDialer returns a dialer that always fails.
 | 
					// NewNullDialer returns a dialer that always fails with ErrNoDialer.
 | 
				
			||||||
func NewNullDialer() Dialer {
 | 
					func NewNullDialer() Dialer {
 | 
				
			||||||
	return &nullDialer{}
 | 
						return &nullDialer{}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -5,9 +5,34 @@ import "github.com/miekg/dns"
 | 
				
			|||||||
// The DNSDecoder decodes DNS replies.
 | 
					// The DNSDecoder decodes DNS replies.
 | 
				
			||||||
type DNSDecoder interface {
 | 
					type DNSDecoder interface {
 | 
				
			||||||
	// DecodeLookupHost decodes an A or AAAA reply.
 | 
						// DecodeLookupHost decodes an A or AAAA reply.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Arguments:
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// - qtype is the query type (e.g., dns.TypeAAAA)
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// - data contains the reply bytes read from a DNSTransport
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Returns:
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// - on success, a list of IP addrs inside the reply and a nil error
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// - on failure, a nil list and an error.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Note that this function will return an error if there is no
 | 
				
			||||||
 | 
						// IP address inside of the reply.
 | 
				
			||||||
	DecodeLookupHost(qtype uint16, data []byte) ([]string, error)
 | 
						DecodeLookupHost(qtype uint16, data []byte) ([]string, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// DecodeHTTPS decodes an HTTPS reply.
 | 
						// DecodeHTTPS decodes an HTTPS reply.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// The argument is the reply as read by the DNSTransport.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// On success, this function returns an HTTPSSvc structure and
 | 
				
			||||||
 | 
						// a nil error. On failure, the HTTPSSvc pointer is nil and
 | 
				
			||||||
 | 
						// the error points to the error that occurred.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// This function will return an error if the HTTPS reply does not
 | 
				
			||||||
 | 
						// contain at least a valid ALPN entry. It will not return
 | 
				
			||||||
 | 
						// an error, though, when there are no IPv4/IPv6 hints in the reply.
 | 
				
			||||||
	DecodeHTTPS(data []byte) (*HTTPSSvc, error)
 | 
						DecodeHTTPS(data []byte) (*HTTPSSvc, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,18 @@ import "github.com/miekg/dns"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// The DNSEncoder encodes DNS queries to bytes
 | 
					// The DNSEncoder encodes DNS queries to bytes
 | 
				
			||||||
type DNSEncoder interface {
 | 
					type DNSEncoder interface {
 | 
				
			||||||
 | 
						// Encode transforms its arguments into a serialized DNS query.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Arguments:
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// - domain is the domain for the query (e.g., x.org);
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// - qtype is the query type (e.g., dns.TypeA);
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// - padding is whether to add padding to the query.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// On success, this function returns a valid byte array and
 | 
				
			||||||
 | 
						// a nil error. On failure, we have an error and the byte array is nil.
 | 
				
			||||||
	Encode(domain string, qtype uint16, padding bool) ([]byte, error)
 | 
						Encode(domain string, qtype uint16, padding bool) ([]byte, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -21,7 +33,6 @@ const (
 | 
				
			|||||||
	dnsDNSSECEnabled = true
 | 
						dnsDNSSECEnabled = true
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Encode implements Encoder.Encode
 | 
					 | 
				
			||||||
func (e *DNSEncoderMiekg) Encode(domain string, qtype uint16, padding bool) ([]byte, error) {
 | 
					func (e *DNSEncoderMiekg) Encode(domain string, qtype uint16, padding bool) ([]byte, error) {
 | 
				
			||||||
	question := dns.Question{
 | 
						question := dns.Question{
 | 
				
			||||||
		Name:   dns.Fqdn(domain),
 | 
							Name:   dns.Fqdn(domain),
 | 
				
			||||||
 | 
				
			|||||||
@ -10,34 +10,44 @@ import (
 | 
				
			|||||||
	"github.com/ooni/probe-cli/v3/internal/engine/httpheader"
 | 
						"github.com/ooni/probe-cli/v3/internal/engine/httpheader"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HTTPClient is the HTTP client expected by DNSOverHTTPS.
 | 
					// HTTPClient is an http.Client-like interface.
 | 
				
			||||||
type HTTPClient interface {
 | 
					type HTTPClient interface {
 | 
				
			||||||
	Do(req *http.Request) (*http.Response, error)
 | 
						Do(req *http.Request) (*http.Response, error)
 | 
				
			||||||
	CloseIdleConnections()
 | 
						CloseIdleConnections()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DNSOverHTTPS is a DNS over HTTPS RoundTripper. Requests are submitted over
 | 
					// DNSOverHTTPS is a DNS-over-HTTPS DNSTransport.
 | 
				
			||||||
// an HTTP/HTTPS channel provided by URL using the Do function.
 | 
					 | 
				
			||||||
type DNSOverHTTPS struct {
 | 
					type DNSOverHTTPS struct {
 | 
				
			||||||
	Client       HTTPClient
 | 
						// Client is the MANDATORY http client to use.
 | 
				
			||||||
	URL          string
 | 
						Client HTTPClient
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// URL is the MANDATORY URL of the DNS-over-HTTPS server.
 | 
				
			||||||
 | 
						URL string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// HostOverride is OPTIONAL and allows to override the
 | 
				
			||||||
 | 
						// Host header sent in every request.
 | 
				
			||||||
	HostOverride string
 | 
						HostOverride string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewDNSOverHTTPS creates a new DNSOverHTTP instance from the
 | 
					// NewDNSOverHTTPS creates a new DNSOverHTTPS instance.
 | 
				
			||||||
// specified http.Client and URL, as a convenience.
 | 
					//
 | 
				
			||||||
 | 
					// Arguments:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// - client in http.Client-like type (e.g., http.DefaultClient);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// - URL is the DoH resolver URL (e.g., https://1.1.1.1/dns-query).
 | 
				
			||||||
func NewDNSOverHTTPS(client HTTPClient, URL string) *DNSOverHTTPS {
 | 
					func NewDNSOverHTTPS(client HTTPClient, URL string) *DNSOverHTTPS {
 | 
				
			||||||
	return NewDNSOverHTTPSWithHostOverride(client, URL, "")
 | 
						return NewDNSOverHTTPSWithHostOverride(client, URL, "")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewDNSOverHTTPSWithHostOverride is like NewDNSOverHTTPS except that
 | 
					// NewDNSOverHTTPSWithHostOverride creates a new DNSOverHTTPS
 | 
				
			||||||
// it's creating a resolver where we use the specified host.
 | 
					// with the given Host header override.
 | 
				
			||||||
func NewDNSOverHTTPSWithHostOverride(
 | 
					func NewDNSOverHTTPSWithHostOverride(
 | 
				
			||||||
	client HTTPClient, URL, hostOverride string) *DNSOverHTTPS {
 | 
						client HTTPClient, URL, hostOverride string) *DNSOverHTTPS {
 | 
				
			||||||
	return &DNSOverHTTPS{Client: client, URL: URL, HostOverride: hostOverride}
 | 
						return &DNSOverHTTPS{Client: client, URL: URL, HostOverride: hostOverride}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RoundTrip implements RoundTripper.RoundTrip.
 | 
					// RoundTrip sends a query and receives a reply.
 | 
				
			||||||
func (t *DNSOverHTTPS) RoundTrip(ctx context.Context, query []byte) ([]byte, error) {
 | 
					func (t *DNSOverHTTPS) RoundTrip(ctx context.Context, query []byte) ([]byte, error) {
 | 
				
			||||||
	ctx, cancel := context.WithTimeout(ctx, 45*time.Second)
 | 
						ctx, cancel := context.WithTimeout(ctx, 45*time.Second)
 | 
				
			||||||
	defer cancel()
 | 
						defer cancel()
 | 
				
			||||||
@ -65,22 +75,22 @@ func (t *DNSOverHTTPS) RoundTrip(ctx context.Context, query []byte) ([]byte, err
 | 
				
			|||||||
	return ReadAllContext(ctx, resp.Body)
 | 
						return ReadAllContext(ctx, resp.Body)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RequiresPadding returns true for DoH according to RFC8467
 | 
					// RequiresPadding returns true for DoH according to RFC8467.
 | 
				
			||||||
func (t *DNSOverHTTPS) RequiresPadding() bool {
 | 
					func (t *DNSOverHTTPS) RequiresPadding() bool {
 | 
				
			||||||
	return true
 | 
						return true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Network returns the transport network (e.g., doh, dot)
 | 
					// Network returns the transport network, i.e., "doh".
 | 
				
			||||||
func (t *DNSOverHTTPS) Network() string {
 | 
					func (t *DNSOverHTTPS) Network() string {
 | 
				
			||||||
	return "doh"
 | 
						return "doh"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Address returns the upstream server address.
 | 
					// Address returns the URL we're using for the DoH server.
 | 
				
			||||||
func (t *DNSOverHTTPS) Address() string {
 | 
					func (t *DNSOverHTTPS) Address() string {
 | 
				
			||||||
	return t.URL
 | 
						return t.URL
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CloseIdleConnections closes idle connections.
 | 
					// CloseIdleConnections closes idle connections, if any.
 | 
				
			||||||
func (t *DNSOverHTTPS) CloseIdleConnections() {
 | 
					func (t *DNSOverHTTPS) CloseIdleConnections() {
 | 
				
			||||||
	t.Client.CloseIdleConnections()
 | 
						t.Client.CloseIdleConnections()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,15 +9,12 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DialContextFunc is a generic function for dialing a connection.
 | 
					// DialContextFunc is the type of net.Dialer.DialContext.
 | 
				
			||||||
type DialContextFunc func(context.Context, string, string) (net.Conn, error)
 | 
					type DialContextFunc func(context.Context, string, string) (net.Conn, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DNSOverTCP is a DNS over TCP/TLS RoundTripper. Use NewDNSOverTCP
 | 
					// DNSOverTCP is a DNS-over-{TCP,TLS} DNSTransport.
 | 
				
			||||||
// and NewDNSOverTLS to create specific instances that use plaintext
 | 
					 | 
				
			||||||
// queries or encrypted queries over TLS.
 | 
					 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// As a known bug, this implementation always creates a new connection
 | 
					// Bug: this implementation always creates a new connection for each query.
 | 
				
			||||||
// for each incoming query, thus increasing the response delay.
 | 
					 | 
				
			||||||
type DNSOverTCP struct {
 | 
					type DNSOverTCP struct {
 | 
				
			||||||
	dial            DialContextFunc
 | 
						dial            DialContextFunc
 | 
				
			||||||
	address         string
 | 
						address         string
 | 
				
			||||||
@ -26,6 +23,12 @@ type DNSOverTCP struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewDNSOverTCP creates a new DNSOverTCP transport.
 | 
					// NewDNSOverTCP creates a new DNSOverTCP transport.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Arguments:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// - dial is a function with the net.Dialer.DialContext's signature;
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// - address is the endpoint address (e.g., 8.8.8.8:53).
 | 
				
			||||||
func NewDNSOverTCP(dial DialContextFunc, address string) *DNSOverTCP {
 | 
					func NewDNSOverTCP(dial DialContextFunc, address string) *DNSOverTCP {
 | 
				
			||||||
	return &DNSOverTCP{
 | 
						return &DNSOverTCP{
 | 
				
			||||||
		dial:            dial,
 | 
							dial:            dial,
 | 
				
			||||||
@ -36,6 +39,12 @@ func NewDNSOverTCP(dial DialContextFunc, address string) *DNSOverTCP {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewDNSOverTLS creates a new DNSOverTLS transport.
 | 
					// NewDNSOverTLS creates a new DNSOverTLS transport.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Arguments:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// - dial is a function with the net.Dialer.DialContext's signature;
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// - address is the endpoint address (e.g., 8.8.8.8:853).
 | 
				
			||||||
func NewDNSOverTLS(dial DialContextFunc, address string) *DNSOverTCP {
 | 
					func NewDNSOverTLS(dial DialContextFunc, address string) *DNSOverTCP {
 | 
				
			||||||
	return &DNSOverTCP{
 | 
						return &DNSOverTCP{
 | 
				
			||||||
		dial:            dial,
 | 
							dial:            dial,
 | 
				
			||||||
@ -45,7 +54,7 @@ func NewDNSOverTLS(dial DialContextFunc, address string) *DNSOverTCP {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RoundTrip implements RoundTripper.RoundTrip.
 | 
					// RoundTrip sends a query and receives a reply.
 | 
				
			||||||
func (t *DNSOverTCP) RoundTrip(ctx context.Context, query []byte) ([]byte, error) {
 | 
					func (t *DNSOverTCP) RoundTrip(ctx context.Context, query []byte) ([]byte, error) {
 | 
				
			||||||
	if len(query) > math.MaxUint16 {
 | 
						if len(query) > math.MaxUint16 {
 | 
				
			||||||
		return nil, errors.New("query too long")
 | 
							return nil, errors.New("query too long")
 | 
				
			||||||
@ -84,17 +93,17 @@ func (t *DNSOverTCP) RequiresPadding() bool {
 | 
				
			|||||||
	return t.requiresPadding
 | 
						return t.requiresPadding
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Network returns the transport network (e.g., doh, dot)
 | 
					// Network returns the transport network, i.e., "dot" or "tcp".
 | 
				
			||||||
func (t *DNSOverTCP) Network() string {
 | 
					func (t *DNSOverTCP) Network() string {
 | 
				
			||||||
	return t.network
 | 
						return t.network
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Address returns the upstream server address.
 | 
					// Address returns the upstream server endpoint (e.g., "1.1.1.1:853").
 | 
				
			||||||
func (t *DNSOverTCP) Address() string {
 | 
					func (t *DNSOverTCP) Address() string {
 | 
				
			||||||
	return t.address
 | 
						return t.address
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CloseIdleConnections closes idle connections.
 | 
					// CloseIdleConnections closes idle connections, if any.
 | 
				
			||||||
func (t *DNSOverTCP) CloseIdleConnections() {
 | 
					func (t *DNSOverTCP) CloseIdleConnections() {
 | 
				
			||||||
	// nothing to do
 | 
						// nothing to do
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -5,18 +5,24 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DNSOverUDP is a DNS over UDP RoundTripper.
 | 
					// DNSOverUDP is a DNS-over-UDP DNSTransport.
 | 
				
			||||||
type DNSOverUDP struct {
 | 
					type DNSOverUDP struct {
 | 
				
			||||||
	dialer  Dialer
 | 
						dialer  Dialer
 | 
				
			||||||
	address string
 | 
						address string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewDNSOverUDP creates a DNSOverUDP instance.
 | 
					// NewDNSOverUDP creates a DNSOverUDP instance.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Arguments:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// - dialer is any type that implements the Dialer interface;
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// - address is the endpoint address (e.g., 8.8.8.8:53).
 | 
				
			||||||
func NewDNSOverUDP(dialer Dialer, address string) *DNSOverUDP {
 | 
					func NewDNSOverUDP(dialer Dialer, address string) *DNSOverUDP {
 | 
				
			||||||
	return &DNSOverUDP{dialer: dialer, address: address}
 | 
						return &DNSOverUDP{dialer: dialer, address: address}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RoundTrip implements RoundTripper.RoundTrip.
 | 
					// RoundTrip sends a query and receives a reply.
 | 
				
			||||||
func (t *DNSOverUDP) RoundTrip(ctx context.Context, query []byte) ([]byte, error) {
 | 
					func (t *DNSOverUDP) RoundTrip(ctx context.Context, query []byte) ([]byte, error) {
 | 
				
			||||||
	conn, err := t.dialer.DialContext(ctx, "udp", t.address)
 | 
						conn, err := t.dialer.DialContext(ctx, "udp", t.address)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@ -40,12 +46,12 @@ func (t *DNSOverUDP) RoundTrip(ctx context.Context, query []byte) ([]byte, error
 | 
				
			|||||||
	return reply[:n], nil
 | 
						return reply[:n], nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RequiresPadding returns false for UDP according to RFC8467
 | 
					// RequiresPadding returns false for UDP according to RFC8467.
 | 
				
			||||||
func (t *DNSOverUDP) RequiresPadding() bool {
 | 
					func (t *DNSOverUDP) RequiresPadding() bool {
 | 
				
			||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Network returns the transport network (e.g., doh, dot)
 | 
					// Network returns the transport network, i.e., "udp".
 | 
				
			||||||
func (t *DNSOverUDP) Network() string {
 | 
					func (t *DNSOverUDP) Network() string {
 | 
				
			||||||
	return "udp"
 | 
						return "udp"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -55,7 +61,7 @@ func (t *DNSOverUDP) Address() string {
 | 
				
			|||||||
	return t.address
 | 
						return t.address
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CloseIdleConnections closes idle connections.
 | 
					// CloseIdleConnections closes idle connections, if any.
 | 
				
			||||||
func (t *DNSOverUDP) CloseIdleConnections() {
 | 
					func (t *DNSOverUDP) CloseIdleConnections() {
 | 
				
			||||||
	// nothing to do
 | 
						// nothing to do
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -7,15 +7,15 @@ type DNSTransport interface {
 | 
				
			|||||||
	// RoundTrip sends a DNS query and receives the reply.
 | 
						// RoundTrip sends a DNS query and receives the reply.
 | 
				
			||||||
	RoundTrip(ctx context.Context, query []byte) (reply []byte, err error)
 | 
						RoundTrip(ctx context.Context, query []byte) (reply []byte, err error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// RequiresPadding return true for DoH and DoT according to RFC8467
 | 
						// RequiresPadding returns whether this transport needs padding.
 | 
				
			||||||
	RequiresPadding() bool
 | 
						RequiresPadding() bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Network is the network of the round tripper (e.g. "dot")
 | 
						// Network is the network of the round tripper (e.g. "dot").
 | 
				
			||||||
	Network() string
 | 
						Network() string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Address is the address of the round tripper (e.g. "1.1.1.1:853")
 | 
						// Address is the address of the round tripper (e.g. "1.1.1.1:853").
 | 
				
			||||||
	Address() string
 | 
						Address() string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// CloseIdleConnections closes idle connections.
 | 
						// CloseIdleConnections closes idle connections, if any.
 | 
				
			||||||
	CloseIdleConnections()
 | 
						CloseIdleConnections()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,14 @@
 | 
				
			|||||||
// Package dnsx contains the dnsx model.
 | 
					// Package dnsx contains DNS extension types.
 | 
				
			||||||
package dnsx
 | 
					package dnsx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HTTPSSvc is an HTTPSSvc reply.
 | 
					// HTTPSSvc is the reply to an HTTPS DNS query.
 | 
				
			||||||
type HTTPSSvc struct {
 | 
					type HTTPSSvc struct {
 | 
				
			||||||
	// ALPN contains the ALPNs inside the HTTPS reply
 | 
						// ALPN contains the ALPNs inside the HTTPS reply.
 | 
				
			||||||
	ALPN []string
 | 
						ALPN []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// IPv4 contains the IPv4 hints.
 | 
						// IPv4 contains the IPv4 hints (which may be empty).
 | 
				
			||||||
	IPv4 []string
 | 
						IPv4 []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// IPv6 contains the IPv6 hints.
 | 
						// IPv6 contains the IPv6 hints (which may be empty).
 | 
				
			||||||
	IPv6 []string
 | 
						IPv6 []string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,9 @@
 | 
				
			|||||||
// This package is the basic networking building block that you
 | 
					// This package is the basic networking building block that you
 | 
				
			||||||
// should be using every time you need networking.
 | 
					// should be using every time you need networking.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
 | 
					// You should consider checking the tutorial explaining how to use this package
 | 
				
			||||||
 | 
					// for network measurements: https://github.com/ooni/probe-cli/tree/master/internal/tutorial/netxlite.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
// Naming and history
 | 
					// Naming and history
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Previous versions of this package were called netx. Compared to such
 | 
					// Previous versions of this package were called netx. Compared to such
 | 
				
			||||||
@ -27,13 +30,16 @@
 | 
				
			|||||||
// We also want to map errors to OONI failures, which are described by
 | 
					// We also want to map errors to OONI failures, which are described by
 | 
				
			||||||
// https://github.com/ooni/spec/blob/master/data-formats/df-007-errors.md.
 | 
					// https://github.com/ooni/spec/blob/master/data-formats/df-007-errors.md.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
 | 
					// We want to have reasonable watchdog timeouts for each operation.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
// Operations
 | 
					// Operations
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This package implements the following operations:
 | 
					// This package implements the following operations:
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// 1. establishing a TCP connection;
 | 
					// 1. establishing a TCP connection;
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// 2. performing a domain name resolution;
 | 
					// 2. performing a domain name resolution with the "system" resolver
 | 
				
			||||||
 | 
					// (i.e., getaddrinfo on Unix) or custom DNS transports (e.g., DoT, DoH);
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// 3. performing the TLS handshake;
 | 
					// 3. performing the TLS handshake;
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,13 @@
 | 
				
			|||||||
// Code generated by go generate; DO NOT EDIT.
 | 
					// Code generated by go generate; DO NOT EDIT.
 | 
				
			||||||
// Generated: 2021-09-29 10:33:56.711301 +0200 CEST m=+0.645971001
 | 
					// Generated: 2021-09-29 16:40:32.42792 +0200 CEST m=+0.613746876
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package netxlite
 | 
					package netxlite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//go:generate go run ./internal/generrno/
 | 
					//go:generate go run ./internal/generrno/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// This enumeration lists the failures defined at
 | 
					// This enumeration lists the failures defined at
 | 
				
			||||||
// https://github.com/ooni/spec/blob/master/data-formats/df-007-errors.md
 | 
					// https://github.com/ooni/spec/blob/master/data-formats/df-007-errors.md.
 | 
				
			||||||
 | 
					// Please, refer to such a document for more information.
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	FailureAddressFamilyNotSupported   = "address_family_not_supported"
 | 
						FailureAddressFamilyNotSupported   = "address_family_not_supported"
 | 
				
			||||||
	FailureAddressInUse                = "address_in_use"
 | 
						FailureAddressInUse                = "address_in_use"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
// Code generated by go generate; DO NOT EDIT.
 | 
					// Code generated by go generate; DO NOT EDIT.
 | 
				
			||||||
// Generated: 2021-09-29 10:33:56.065909 +0200 CEST m=+0.000565085
 | 
					// Generated: 2021-09-29 16:40:31.814543 +0200 CEST m=+0.000360918
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package netxlite
 | 
					package netxlite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -11,7 +11,9 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// This enumeration provides a canonical name for
 | 
					// This enumeration provides a canonical name for
 | 
				
			||||||
// every system-call error we support on this systems.
 | 
					// every system-call error we support. Note: this list
 | 
				
			||||||
 | 
					// is system dependent. You're currently looking at
 | 
				
			||||||
 | 
					// the list of errors for android.
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	ECONNREFUSED    = unix.ECONNREFUSED
 | 
						ECONNREFUSED    = unix.ECONNREFUSED
 | 
				
			||||||
	ECONNRESET      = unix.ECONNRESET
 | 
						ECONNRESET      = unix.ECONNRESET
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
// Code generated by go generate; DO NOT EDIT.
 | 
					// Code generated by go generate; DO NOT EDIT.
 | 
				
			||||||
// Generated: 2021-09-29 10:33:56.171735 +0200 CEST m=+0.106392751
 | 
					// Generated: 2021-09-29 16:40:31.912358 +0200 CEST m=+0.098177043
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package netxlite
 | 
					package netxlite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
// Code generated by go generate; DO NOT EDIT.
 | 
					// Code generated by go generate; DO NOT EDIT.
 | 
				
			||||||
// Generated: 2021-09-29 10:33:56.208597 +0200 CEST m=+0.143256126
 | 
					// Generated: 2021-09-29 16:40:31.947263 +0200 CEST m=+0.133082793
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package netxlite
 | 
					package netxlite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -11,7 +11,9 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// This enumeration provides a canonical name for
 | 
					// This enumeration provides a canonical name for
 | 
				
			||||||
// every system-call error we support on this systems.
 | 
					// every system-call error we support. Note: this list
 | 
				
			||||||
 | 
					// is system dependent. You're currently looking at
 | 
				
			||||||
 | 
					// the list of errors for darwin.
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	ECONNREFUSED    = unix.ECONNREFUSED
 | 
						ECONNREFUSED    = unix.ECONNREFUSED
 | 
				
			||||||
	ECONNRESET      = unix.ECONNRESET
 | 
						ECONNRESET      = unix.ECONNRESET
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
// Code generated by go generate; DO NOT EDIT.
 | 
					// Code generated by go generate; DO NOT EDIT.
 | 
				
			||||||
// Generated: 2021-09-29 10:33:56.27181 +0200 CEST m=+0.206469960
 | 
					// Generated: 2021-09-29 16:40:32.010423 +0200 CEST m=+0.196243584
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package netxlite
 | 
					package netxlite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
// Code generated by go generate; DO NOT EDIT.
 | 
					// Code generated by go generate; DO NOT EDIT.
 | 
				
			||||||
// Generated: 2021-09-29 10:33:56.310236 +0200 CEST m=+0.244897126
 | 
					// Generated: 2021-09-29 16:40:32.047214 +0200 CEST m=+0.233035084
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package netxlite
 | 
					package netxlite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -11,7 +11,9 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// This enumeration provides a canonical name for
 | 
					// This enumeration provides a canonical name for
 | 
				
			||||||
// every system-call error we support on this systems.
 | 
					// every system-call error we support. Note: this list
 | 
				
			||||||
 | 
					// is system dependent. You're currently looking at
 | 
				
			||||||
 | 
					// the list of errors for freebsd.
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	ECONNREFUSED    = unix.ECONNREFUSED
 | 
						ECONNREFUSED    = unix.ECONNREFUSED
 | 
				
			||||||
	ECONNRESET      = unix.ECONNRESET
 | 
						ECONNRESET      = unix.ECONNRESET
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
// Code generated by go generate; DO NOT EDIT.
 | 
					// Code generated by go generate; DO NOT EDIT.
 | 
				
			||||||
// Generated: 2021-09-29 10:33:56.373611 +0200 CEST m=+0.308273168
 | 
					// Generated: 2021-09-29 16:40:32.110555 +0200 CEST m=+0.296377043
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package netxlite
 | 
					package netxlite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
// Code generated by go generate; DO NOT EDIT.
 | 
					// Code generated by go generate; DO NOT EDIT.
 | 
				
			||||||
// Generated: 2021-09-29 10:33:56.410358 +0200 CEST m=+0.345021835
 | 
					// Generated: 2021-09-29 16:40:32.146781 +0200 CEST m=+0.332603876
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package netxlite
 | 
					package netxlite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -11,7 +11,9 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// This enumeration provides a canonical name for
 | 
					// This enumeration provides a canonical name for
 | 
				
			||||||
// every system-call error we support on this systems.
 | 
					// every system-call error we support. Note: this list
 | 
				
			||||||
 | 
					// is system dependent. You're currently looking at
 | 
				
			||||||
 | 
					// the list of errors for ios.
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	ECONNREFUSED    = unix.ECONNREFUSED
 | 
						ECONNREFUSED    = unix.ECONNREFUSED
 | 
				
			||||||
	ECONNRESET      = unix.ECONNRESET
 | 
						ECONNRESET      = unix.ECONNRESET
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
// Code generated by go generate; DO NOT EDIT.
 | 
					// Code generated by go generate; DO NOT EDIT.
 | 
				
			||||||
// Generated: 2021-09-29 10:33:56.491959 +0200 CEST m=+0.426624626
 | 
					// Generated: 2021-09-29 16:40:32.210176 +0200 CEST m=+0.395999709
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package netxlite
 | 
					package netxlite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
// Code generated by go generate; DO NOT EDIT.
 | 
					// Code generated by go generate; DO NOT EDIT.
 | 
				
			||||||
// Generated: 2021-09-29 10:33:56.529823 +0200 CEST m=+0.464489585
 | 
					// Generated: 2021-09-29 16:40:32.246508 +0200 CEST m=+0.432331918
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package netxlite
 | 
					package netxlite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -11,7 +11,9 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// This enumeration provides a canonical name for
 | 
					// This enumeration provides a canonical name for
 | 
				
			||||||
// every system-call error we support on this systems.
 | 
					// every system-call error we support. Note: this list
 | 
				
			||||||
 | 
					// is system dependent. You're currently looking at
 | 
				
			||||||
 | 
					// the list of errors for linux.
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	ECONNREFUSED    = unix.ECONNREFUSED
 | 
						ECONNREFUSED    = unix.ECONNREFUSED
 | 
				
			||||||
	ECONNRESET      = unix.ECONNRESET
 | 
						ECONNRESET      = unix.ECONNRESET
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
// Code generated by go generate; DO NOT EDIT.
 | 
					// Code generated by go generate; DO NOT EDIT.
 | 
				
			||||||
// Generated: 2021-09-29 10:33:56.592275 +0200 CEST m=+0.526942210
 | 
					// Generated: 2021-09-29 16:40:32.309745 +0200 CEST m=+0.495570709
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package netxlite
 | 
					package netxlite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
// Code generated by go generate; DO NOT EDIT.
 | 
					// Code generated by go generate; DO NOT EDIT.
 | 
				
			||||||
// Generated: 2021-09-29 10:33:56.628771 +0200 CEST m=+0.563439293
 | 
					// Generated: 2021-09-29 16:40:32.346817 +0200 CEST m=+0.532646709
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package netxlite
 | 
					package netxlite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -11,7 +11,9 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// This enumeration provides a canonical name for
 | 
					// This enumeration provides a canonical name for
 | 
				
			||||||
// every system-call error we support on this systems.
 | 
					// every system-call error we support. Note: this list
 | 
				
			||||||
 | 
					// is system dependent. You're currently looking at
 | 
				
			||||||
 | 
					// the list of errors for windows.
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	ECONNREFUSED    = windows.WSAECONNREFUSED
 | 
						ECONNREFUSED    = windows.WSAECONNREFUSED
 | 
				
			||||||
	ECONNRESET      = windows.WSAECONNRESET
 | 
						ECONNRESET      = windows.WSAECONNRESET
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
// Code generated by go generate; DO NOT EDIT.
 | 
					// Code generated by go generate; DO NOT EDIT.
 | 
				
			||||||
// Generated: 2021-09-29 10:33:56.675065 +0200 CEST m=+0.609734418
 | 
					// Generated: 2021-09-29 16:40:32.393519 +0200 CEST m=+0.579345709
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package netxlite
 | 
					package netxlite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -53,12 +53,12 @@ type ErrWrapper struct {
 | 
				
			|||||||
	WrappedErr error
 | 
						WrappedErr error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Error returns a description of the error that occurred.
 | 
					// Error returns the OONI failure string for this error.
 | 
				
			||||||
func (e *ErrWrapper) Error() string {
 | 
					func (e *ErrWrapper) Error() string {
 | 
				
			||||||
	return e.Failure
 | 
						return e.Failure
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Unwrap allows to access the underlying error
 | 
					// Unwrap allows to access the underlying error.
 | 
				
			||||||
func (e *ErrWrapper) Unwrap() error {
 | 
					func (e *ErrWrapper) Unwrap() error {
 | 
				
			||||||
	return e.WrappedErr
 | 
						return e.WrappedErr
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -68,7 +68,9 @@ func (e *ErrWrapper) MarshalJSON() ([]byte, error) {
 | 
				
			|||||||
	return json.Marshal(e.Failure)
 | 
						return json.Marshal(e.Failure)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Classifier is the type of function that performs classification.
 | 
					// Classifier is the type of the function that maps a Go error
 | 
				
			||||||
 | 
					// to a OONI failure string defined at
 | 
				
			||||||
 | 
					// https://github.com/ooni/spec/blob/master/data-formats/df-007-errors.md.
 | 
				
			||||||
type Classifier func(err error) string
 | 
					type Classifier func(err error) string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewErrWrapper creates a new ErrWrapper using the given
 | 
					// NewErrWrapper creates a new ErrWrapper using the given
 | 
				
			||||||
@ -94,7 +96,7 @@ func NewErrWrapper(c Classifier, op string, err error) *ErrWrapper {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewTopLevelGenericErrWrapper wraps an error occurring at top
 | 
					// NewTopLevelGenericErrWrapper wraps an error occurring at top
 | 
				
			||||||
// level using the most generic available classifier.
 | 
					// level using ClassifyGenericError as classifier.
 | 
				
			||||||
func NewTopLevelGenericErrWrapper(err error) *ErrWrapper {
 | 
					func NewTopLevelGenericErrWrapper(err error) *ErrWrapper {
 | 
				
			||||||
	return NewErrWrapper(ClassifyGenericError, TopLevelOperation, err)
 | 
						return NewErrWrapper(ClassifyGenericError, TopLevelOperation, err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -72,17 +72,19 @@ func (txp *httpTransportConnectionsCloser) CloseIdleConnections() {
 | 
				
			|||||||
	txp.TLSDialer.CloseIdleConnections()
 | 
						txp.TLSDialer.CloseIdleConnections()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewHTTPTransport combines NewOOHTTPBaseTransport and
 | 
					// NewHTTPTransport combines NewOOHTTPBaseTransport and WrapHTTPTransport.
 | 
				
			||||||
// WrapHTTPTransport to construct a new HTTPTransport.
 | 
					//
 | 
				
			||||||
 | 
					// This factory and NewHTTPTransportStdlib are the recommended
 | 
				
			||||||
 | 
					// ways of creating a new HTTPTransport.
 | 
				
			||||||
func NewHTTPTransport(logger Logger, dialer Dialer, tlsDialer TLSDialer) HTTPTransport {
 | 
					func NewHTTPTransport(logger Logger, dialer Dialer, tlsDialer TLSDialer) HTTPTransport {
 | 
				
			||||||
	return WrapHTTPTransport(logger, NewOOHTTPBaseTransport(dialer, tlsDialer))
 | 
						return WrapHTTPTransport(logger, NewOOHTTPBaseTransport(dialer, tlsDialer))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewOOHTTPBaseTransport creates a new HTTP transport using the given
 | 
					// NewOOHTTPBaseTransport creates an HTTPTransport using the given dialers.
 | 
				
			||||||
// dialer and TLS dialer to create connections.
 | 
					 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// The returned transport will gracefully handle TLS connections
 | 
					// The returned transport will gracefully handle TLS connections
 | 
				
			||||||
// created using gitlab.com/yawning/utls.git.
 | 
					// created using gitlab.com/yawning/utls.git, if the TLS dialer
 | 
				
			||||||
 | 
					// is a dialer using such library for TLS operations.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// The returned transport will not have a configured proxy, not
 | 
					// The returned transport will not have a configured proxy, not
 | 
				
			||||||
// even the proxy configurable from the environment.
 | 
					// even the proxy configurable from the environment.
 | 
				
			||||||
@ -100,6 +102,8 @@ func NewHTTPTransport(logger Logger, dialer Dialer, tlsDialer TLSDialer) HTTPTra
 | 
				
			|||||||
// necessary to perform sane measurements with tracing. We will be
 | 
					// necessary to perform sane measurements with tracing. We will be
 | 
				
			||||||
// able to possibly relax this requirement after we change the
 | 
					// able to possibly relax this requirement after we change the
 | 
				
			||||||
// way in which we perform measurements.
 | 
					// way in which we perform measurements.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This is a low level factory. Consider not using it directly.
 | 
				
			||||||
func NewOOHTTPBaseTransport(dialer Dialer, tlsDialer TLSDialer) HTTPTransport {
 | 
					func NewOOHTTPBaseTransport(dialer Dialer, tlsDialer TLSDialer) HTTPTransport {
 | 
				
			||||||
	// Using oohttp to support any TLS library.
 | 
						// Using oohttp to support any TLS library.
 | 
				
			||||||
	txp := oohttp.DefaultTransport.(*oohttp.Transport).Clone()
 | 
						txp := oohttp.DefaultTransport.(*oohttp.Transport).Clone()
 | 
				
			||||||
@ -137,8 +141,9 @@ func NewOOHTTPBaseTransport(dialer Dialer, tlsDialer TLSDialer) HTTPTransport {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WrapHTTPTransport creates a new HTTP transport using
 | 
					// WrapHTTPTransport creates an HTTPTransport using the given logger.
 | 
				
			||||||
// the given logger for logging.
 | 
					//
 | 
				
			||||||
 | 
					// This is a low level factory. Consider not using it directly.
 | 
				
			||||||
func WrapHTTPTransport(logger Logger, txp HTTPTransport) HTTPTransport {
 | 
					func WrapHTTPTransport(logger Logger, txp HTTPTransport) HTTPTransport {
 | 
				
			||||||
	return &httpTransportLogger{
 | 
						return &httpTransportLogger{
 | 
				
			||||||
		HTTPTransport: txp,
 | 
							HTTPTransport: txp,
 | 
				
			||||||
@ -168,9 +173,9 @@ type httpTLSDialerWithReadTimeout struct {
 | 
				
			|||||||
	TLSDialer
 | 
						TLSDialer
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ErrNotTLSConn indicates that a TLSDialer returns a net.Conn
 | 
					// ErrNotTLSConn occur when an interface accepts a net.Conn but
 | 
				
			||||||
// that does not implement the TLSConn interface. This error should
 | 
					// internally needs a TLSConn and you pass a net.Conn that doesn't
 | 
				
			||||||
// only happen when we do something wrong setting up HTTP code.
 | 
					// implement TLSConn to such an interface.
 | 
				
			||||||
var ErrNotTLSConn = errors.New("not a TLSConn")
 | 
					var ErrNotTLSConn = errors.New("not a TLSConn")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DialTLSContext implements TLSDialer's DialTLSContext.
 | 
					// DialTLSContext implements TLSDialer's DialTLSContext.
 | 
				
			||||||
@ -233,9 +238,13 @@ func (c *httpTLSConnWithReadTimeout) Read(b []byte) (int, error) {
 | 
				
			|||||||
	return c.TLSConn.Read(b)
 | 
						return c.TLSConn.Read(b)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewHTTPTransportStdlib creates a new HTTPTransport that uses
 | 
					// NewHTTPTransportStdlib creates a new HTTPTransport using
 | 
				
			||||||
// the Go standard library for all operations, including DNS
 | 
					// the stdlib for DNS resolutions and TLS.
 | 
				
			||||||
// resolutions and TLS handshakes.
 | 
					//
 | 
				
			||||||
 | 
					// This factory calls NewHTTPTransport with suitable dialers.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This factory and NewHTTPTransport are the recommended
 | 
				
			||||||
 | 
					// ways of creating a new HTTPTransport.
 | 
				
			||||||
func NewHTTPTransportStdlib(logger Logger) HTTPTransport {
 | 
					func NewHTTPTransportStdlib(logger Logger) HTTPTransport {
 | 
				
			||||||
	dialer := NewDialerWithResolver(logger, NewResolverStdlib(logger))
 | 
						dialer := NewDialerWithResolver(logger, NewResolverStdlib(logger))
 | 
				
			||||||
	tlsDialer := NewTLSDialer(dialer, NewTLSHandshakerStdlib(logger))
 | 
						tlsDialer := NewTLSDialer(dialer, NewTLSHandshakerStdlib(logger))
 | 
				
			||||||
 | 
				
			|||||||
@ -228,7 +228,9 @@ func writeSystemSpecificFile(system string) {
 | 
				
			|||||||
	fileWrite(filep, ")\n\n")
 | 
						fileWrite(filep, ")\n\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fileWrite(filep, "// This enumeration provides a canonical name for\n")
 | 
						fileWrite(filep, "// This enumeration provides a canonical name for\n")
 | 
				
			||||||
	fileWrite(filep, "// every system-call error we support on this systems.\n")
 | 
						fileWrite(filep, "// every system-call error we support. Note: this list\n")
 | 
				
			||||||
 | 
						fileWrite(filep, "// is system dependent. You're currently looking at\n")
 | 
				
			||||||
 | 
						filePrintf(filep, "// the list of errors for %s.\n", system)
 | 
				
			||||||
	fileWrite(filep, "const (\n")
 | 
						fileWrite(filep, "const (\n")
 | 
				
			||||||
	for _, spec := range Specs {
 | 
						for _, spec := range Specs {
 | 
				
			||||||
		if !spec.IsSystemError() || !spec.IsForSystem(system) {
 | 
							if !spec.IsSystemError() || !spec.IsForSystem(system) {
 | 
				
			||||||
@ -272,7 +274,8 @@ func writeGenericFile() {
 | 
				
			|||||||
	fileWrite(filep, "//go:generate go run ./internal/generrno/\n\n")
 | 
						fileWrite(filep, "//go:generate go run ./internal/generrno/\n\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fileWrite(filep, "// This enumeration lists the failures defined at\n")
 | 
						fileWrite(filep, "// This enumeration lists the failures defined at\n")
 | 
				
			||||||
	fileWrite(filep, "// https://github.com/ooni/spec/blob/master/data-formats/df-007-errors.md\n")
 | 
						fileWrite(filep, "// https://github.com/ooni/spec/blob/master/data-formats/df-007-errors.md.\n")
 | 
				
			||||||
 | 
						fileWrite(filep, "// Please, refer to that document for more information.\n")
 | 
				
			||||||
	fileWrite(filep, "const (\n")
 | 
						fileWrite(filep, "const (\n")
 | 
				
			||||||
	names := make(map[string]string)
 | 
						names := make(map[string]string)
 | 
				
			||||||
	for _, spec := range Specs {
 | 
						for _, spec := range Specs {
 | 
				
			||||||
 | 
				
			|||||||
@ -8,10 +8,11 @@ import (
 | 
				
			|||||||
// ReadAllContext is like io.ReadAll but reads r in a
 | 
					// ReadAllContext is like io.ReadAll but reads r in a
 | 
				
			||||||
// background goroutine. This function will return
 | 
					// background goroutine. This function will return
 | 
				
			||||||
// earlier if the context is cancelled. In which case
 | 
					// earlier if the context is cancelled. In which case
 | 
				
			||||||
// we will continue reading from r in the background
 | 
					// we will continue reading from the reader in the background
 | 
				
			||||||
// goroutine, and we will discard the result. To stop
 | 
					// goroutine, and we will discard the result. To stop
 | 
				
			||||||
// the long-running goroutine, you need to close the
 | 
					// the long-running goroutine, close the connection
 | 
				
			||||||
// connection bound to the r reader, if possible.
 | 
					// bound to the reader. Until such a connection is closed,
 | 
				
			||||||
 | 
					// you're leaking the backround goroutine and doing I/O.
 | 
				
			||||||
func ReadAllContext(ctx context.Context, r io.Reader) ([]byte, error) {
 | 
					func ReadAllContext(ctx context.Context, r io.Reader) ([]byte, error) {
 | 
				
			||||||
	datach, errch := make(chan []byte, 1), make(chan error, 1) // buffers
 | 
						datach, errch := make(chan []byte, 1), make(chan error, 1) // buffers
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
@ -35,7 +36,7 @@ func ReadAllContext(ctx context.Context, r io.Reader) ([]byte, error) {
 | 
				
			|||||||
// CopyContext is like io.Copy but may terminate earlier
 | 
					// CopyContext is like io.Copy but may terminate earlier
 | 
				
			||||||
// when the context expires. This function has the same
 | 
					// when the context expires. This function has the same
 | 
				
			||||||
// caveats of ReadAllContext regarding the temporary leaking
 | 
					// caveats of ReadAllContext regarding the temporary leaking
 | 
				
			||||||
// of the background goroutine used to do I/O.
 | 
					// of the background I/O goroutine.
 | 
				
			||||||
func CopyContext(ctx context.Context, dst io.Writer, src io.Reader) (int64, error) {
 | 
					func CopyContext(ctx context.Context, dst io.Writer, src io.Reader) (int64, error) {
 | 
				
			||||||
	countch, errch := make(chan int64, 1), make(chan error, 1) // buffers
 | 
						countch, errch := make(chan int64, 1), make(chan error, 1) // buffers
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,8 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// These vars export internal names to legacy ooni/probe-cli code.
 | 
					// These vars export internal names to legacy ooni/probe-cli code.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Deprecated: do not use these names in new code.
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	DefaultDialer        = &dialerSystem{}
 | 
						DefaultDialer        = &dialerSystem{}
 | 
				
			||||||
	DefaultTLSHandshaker = defaultTLSHandshaker
 | 
						DefaultTLSHandshaker = defaultTLSHandshaker
 | 
				
			||||||
@ -17,6 +19,8 @@ var (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// These types export internal names to legacy ooni/probe-cli code.
 | 
					// These types export internal names to legacy ooni/probe-cli code.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Deprecated: do not use these names in new code.
 | 
				
			||||||
type (
 | 
					type (
 | 
				
			||||||
	DialerResolver            = dialerResolver
 | 
						DialerResolver            = dialerResolver
 | 
				
			||||||
	DialerLogger              = dialerLogger
 | 
						DialerLogger              = dialerLogger
 | 
				
			||||||
@ -37,8 +41,7 @@ type (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// ResolverLegacy performs domain name resolutions.
 | 
					// ResolverLegacy performs domain name resolutions.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This definition of Resolver is DEPRECATED. New code should use
 | 
					// Deprecated: new code should use Resolver.
 | 
				
			||||||
// the more complete definition in the new Resolver interface.
 | 
					 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Existing code in ooni/probe-cli is still using this definition.
 | 
					// Existing code in ooni/probe-cli is still using this definition.
 | 
				
			||||||
type ResolverLegacy interface {
 | 
					type ResolverLegacy interface {
 | 
				
			||||||
@ -52,10 +55,7 @@ func NewResolverLegacyAdapter(reso ResolverLegacy) Resolver {
 | 
				
			|||||||
	return &ResolverLegacyAdapter{reso}
 | 
						return &ResolverLegacyAdapter{reso}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ResolverLegacyAdapter makes a ResolverLegacy behave like
 | 
					// ResolverLegacyAdapter makes a ResolverLegacy behave like a Resolver.
 | 
				
			||||||
// it was a Resolver type. If ResolverLegacy is actually also
 | 
					 | 
				
			||||||
// a Resolver, this adapter will just forward missing calls,
 | 
					 | 
				
			||||||
// otherwise it will implement a sensible default action.
 | 
					 | 
				
			||||||
type ResolverLegacyAdapter struct {
 | 
					type ResolverLegacyAdapter struct {
 | 
				
			||||||
	ResolverLegacy
 | 
						ResolverLegacy
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -97,6 +97,7 @@ func (r *ResolverLegacyAdapter) CloseIdleConnections() {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LookupHTTPS always returns ErrDNSNoTransport.
 | 
				
			||||||
func (r *ResolverLegacyAdapter) LookupHTTPS(
 | 
					func (r *ResolverLegacyAdapter) LookupHTTPS(
 | 
				
			||||||
	ctx context.Context, domain string) (*HTTPSSvc, error) {
 | 
						ctx context.Context, domain string) (*HTTPSSvc, error) {
 | 
				
			||||||
	return nil, ErrNoDNSTransport
 | 
						return nil, ErrNoDNSTransport
 | 
				
			||||||
@ -104,7 +105,7 @@ func (r *ResolverLegacyAdapter) LookupHTTPS(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// DialerLegacy establishes network connections.
 | 
					// DialerLegacy establishes network connections.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This definition is DEPRECATED. Please, use Dialer.
 | 
					// Deprecated: please use Dialer instead.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Existing code in probe-cli can use it until we
 | 
					// Existing code in probe-cli can use it until we
 | 
				
			||||||
// have finished refactoring it.
 | 
					// have finished refactoring it.
 | 
				
			||||||
@ -115,6 +116,8 @@ type DialerLegacy interface {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// NewDialerLegacyAdapter adapts a DialerrLegacy to
 | 
					// NewDialerLegacyAdapter adapts a DialerrLegacy to
 | 
				
			||||||
// become compatible with the Dialer definition.
 | 
					// become compatible with the Dialer definition.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Deprecated: do not use this function in new code.
 | 
				
			||||||
func NewDialerLegacyAdapter(d DialerLegacy) Dialer {
 | 
					func NewDialerLegacyAdapter(d DialerLegacy) Dialer {
 | 
				
			||||||
	return &DialerLegacyAdapter{d}
 | 
						return &DialerLegacyAdapter{d}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -133,7 +136,7 @@ type dialerLegacyIdleConnectionsCloser interface {
 | 
				
			|||||||
	CloseIdleConnections()
 | 
						CloseIdleConnections()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CloseIdleConnections implements Resolver.CloseIdleConnections.
 | 
					// CloseIdleConnections implements Dialer.CloseIdleConnections.
 | 
				
			||||||
func (d *DialerLegacyAdapter) CloseIdleConnections() {
 | 
					func (d *DialerLegacyAdapter) CloseIdleConnections() {
 | 
				
			||||||
	if ra, ok := d.DialerLegacy.(dialerLegacyIdleConnectionsCloser); ok {
 | 
						if ra, ok := d.DialerLegacy.(dialerLegacyIdleConnectionsCloser); ok {
 | 
				
			||||||
		ra.CloseIdleConnections()
 | 
							ra.CloseIdleConnections()
 | 
				
			||||||
@ -142,10 +145,10 @@ func (d *DialerLegacyAdapter) CloseIdleConnections() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// QUICContextDialer is a dialer for QUIC using Context.
 | 
					// QUICContextDialer is a dialer for QUIC using Context.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This is a LEGACY name. New code should use QUICDialer directly.
 | 
					// Deprecated: new code should use QUICDialer.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Use NewQUICDialerFromContextDialerAdapter if you need to
 | 
					// Use NewQUICDialerFromContextDialerAdapter if you need to
 | 
				
			||||||
// adapt an existing QUICContextDialer to a QUICDialer.
 | 
					// adapt to QUICDialer.
 | 
				
			||||||
type QUICContextDialer interface {
 | 
					type QUICContextDialer interface {
 | 
				
			||||||
	// DialContext establishes a new QUIC session using the given
 | 
						// DialContext establishes a new QUIC session using the given
 | 
				
			||||||
	// network and address. The tlsConfig and the quicConfig arguments
 | 
						// network and address. The tlsConfig and the quicConfig arguments
 | 
				
			||||||
@ -160,7 +163,7 @@ func NewQUICDialerFromContextDialerAdapter(d QUICContextDialer) QUICDialer {
 | 
				
			|||||||
	return &QUICContextDialerAdapter{d}
 | 
						return &QUICContextDialerAdapter{d}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// QUICContextDialerAdapter adapta a QUICContextDialer to be a QUICDialer.
 | 
					// QUICContextDialerAdapter adapts a QUICContextDialer to be a QUICDialer.
 | 
				
			||||||
type QUICContextDialerAdapter struct {
 | 
					type QUICContextDialerAdapter struct {
 | 
				
			||||||
	QUICContextDialer
 | 
						QUICContextDialer
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
package netxlite
 | 
					package netxlite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Operations that we measure. They are the possibly values of
 | 
					// Operations that we measure. They are the possible values of
 | 
				
			||||||
// the ErrWrapper.Operation field.
 | 
					// the ErrWrapper.Operation field.
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	// ResolveOperation is the operation where we resolve a domain name.
 | 
						// ResolveOperation is the operation where we resolve a domain name.
 | 
				
			||||||
 | 
				
			|||||||
@ -12,10 +12,13 @@ import (
 | 
				
			|||||||
	"github.com/ooni/probe-cli/v3/internal/netxlite/quicx"
 | 
						"github.com/ooni/probe-cli/v3/internal/netxlite/quicx"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UDPLikeConn is the kind of UDP socket used by QUIC.
 | 
				
			||||||
 | 
					type UDPLikeConn = quicx.UDPLikeConn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// QUICListener listens for QUIC connections.
 | 
					// QUICListener listens for QUIC connections.
 | 
				
			||||||
type QUICListener interface {
 | 
					type QUICListener interface {
 | 
				
			||||||
	// Listen creates a new listening UDPConn.
 | 
						// Listen creates a new listening UDPLikeConn.
 | 
				
			||||||
	Listen(addr *net.UDPAddr) (quicx.UDPLikeConn, error)
 | 
						Listen(addr *net.UDPAddr) (UDPLikeConn, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewQUICListener creates a new QUICListener using the standard
 | 
					// NewQUICListener creates a new QUICListener using the standard
 | 
				
			||||||
@ -30,7 +33,7 @@ type quicListenerStdlib struct{}
 | 
				
			|||||||
var _ QUICListener = &quicListenerStdlib{}
 | 
					var _ QUICListener = &quicListenerStdlib{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Listen implements QUICListener.Listen.
 | 
					// Listen implements QUICListener.Listen.
 | 
				
			||||||
func (qls *quicListenerStdlib) Listen(addr *net.UDPAddr) (quicx.UDPLikeConn, error) {
 | 
					func (qls *quicListenerStdlib) Listen(addr *net.UDPAddr) (UDPLikeConn, error) {
 | 
				
			||||||
	return net.ListenUDP("udp", addr)
 | 
						return net.ListenUDP("udp", addr)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -39,6 +42,16 @@ type QUICDialer interface {
 | 
				
			|||||||
	// DialContext establishes a new QUIC session using the given
 | 
						// DialContext establishes a new QUIC session using the given
 | 
				
			||||||
	// network and address. The tlsConfig and the quicConfig arguments
 | 
						// network and address. The tlsConfig and the quicConfig arguments
 | 
				
			||||||
	// MUST NOT be nil. Returns either the session or an error.
 | 
						// MUST NOT be nil. Returns either the session or an error.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Recommended tlsConfig setup:
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// - set ServerName to be the SNI;
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// - set RootCAs to NewDefaultCertPool();
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// - set NextProtos to []string{"h3"}.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Typically, you want to pass `&quic.Config{}` as quicConfig.
 | 
				
			||||||
	DialContext(ctx context.Context, network, address string,
 | 
						DialContext(ctx context.Context, network, address string,
 | 
				
			||||||
		tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlySession, error)
 | 
							tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlySession, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -66,15 +79,7 @@ type QUICDialer interface {
 | 
				
			|||||||
//
 | 
					//
 | 
				
			||||||
// 6. if a dialer wraps a resolver, the dialer will forward
 | 
					// 6. if a dialer wraps a resolver, the dialer will forward
 | 
				
			||||||
// the CloseIdleConnection call to its resolver (which is
 | 
					// the CloseIdleConnection call to its resolver (which is
 | 
				
			||||||
// instrumental to manage a DoH resolver connections properly);
 | 
					// instrumental to manage a DoH resolver connections properly).
 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// 7. will use the bundled CA unless you provide another CA;
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// 8. will attempt to guess SNI when resolving domain names
 | 
					 | 
				
			||||||
// and otherwise will not set the SNI;
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// 9. will attempt to guess ALPN when the port is known and
 | 
					 | 
				
			||||||
// otherwise will not set the ALPN.
 | 
					 | 
				
			||||||
func NewQUICDialerWithResolver(listener QUICListener,
 | 
					func NewQUICDialerWithResolver(listener QUICListener,
 | 
				
			||||||
	logger Logger, resolver Resolver) QUICDialer {
 | 
						logger Logger, resolver Resolver) QUICDialer {
 | 
				
			||||||
	return &quicDialerLogger{
 | 
						return &quicDialerLogger{
 | 
				
			||||||
@ -195,7 +200,7 @@ type quicSessionOwnsConn struct {
 | 
				
			|||||||
	quic.EarlySession
 | 
						quic.EarlySession
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// conn is the connection we own
 | 
						// conn is the connection we own
 | 
				
			||||||
	conn quicx.UDPLikeConn
 | 
						conn UDPLikeConn
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CloseWithError implements quic.EarlySession.CloseWithError.
 | 
					// CloseWithError implements quic.EarlySession.CloseWithError.
 | 
				
			||||||
@ -314,8 +319,7 @@ func (d *quicDialerLogger) CloseIdleConnections() {
 | 
				
			|||||||
	d.Dialer.CloseIdleConnections()
 | 
						d.Dialer.CloseIdleConnections()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewSingleUseQUICDialer returns a dialer that returns the given connection
 | 
					// NewSingleUseQUICDialer is like NewSingleUseDialer but for QUIC.
 | 
				
			||||||
// once and after that always fails with the ErrNoConnReuse error.
 | 
					 | 
				
			||||||
func NewSingleUseQUICDialer(sess quic.EarlySession) QUICDialer {
 | 
					func NewSingleUseQUICDialer(sess quic.EarlySession) QUICDialer {
 | 
				
			||||||
	return &quicDialerSingleUse{sess: sess}
 | 
						return &quicDialerSingleUse{sess: sess}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -356,7 +360,7 @@ type quicListenerErrWrapper struct {
 | 
				
			|||||||
var _ QUICListener = &quicListenerErrWrapper{}
 | 
					var _ QUICListener = &quicListenerErrWrapper{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Listen implements QUICListener.Listen.
 | 
					// Listen implements QUICListener.Listen.
 | 
				
			||||||
func (qls *quicListenerErrWrapper) Listen(addr *net.UDPAddr) (quicx.UDPLikeConn, error) {
 | 
					func (qls *quicListenerErrWrapper) Listen(addr *net.UDPAddr) (UDPLikeConn, error) {
 | 
				
			||||||
	pconn, err := qls.QUICListener.Listen(addr)
 | 
						pconn, err := qls.QUICListener.Listen(addr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, NewErrWrapper(ClassifyGenericError, QUICListenOperation, err)
 | 
							return nil, NewErrWrapper(ClassifyGenericError, QUICListenOperation, err)
 | 
				
			||||||
@ -364,15 +368,15 @@ func (qls *quicListenerErrWrapper) Listen(addr *net.UDPAddr) (quicx.UDPLikeConn,
 | 
				
			|||||||
	return &quicErrWrapperUDPLikeConn{pconn}, nil
 | 
						return &quicErrWrapperUDPLikeConn{pconn}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// quicErrWrapperUDPLikeConn is a quicx.UDPLikeConn that wraps errors.
 | 
					// quicErrWrapperUDPLikeConn is a UDPLikeConn that wraps errors.
 | 
				
			||||||
type quicErrWrapperUDPLikeConn struct {
 | 
					type quicErrWrapperUDPLikeConn struct {
 | 
				
			||||||
	// UDPLikeConn is the underlying conn.
 | 
						// UDPLikeConn is the underlying conn.
 | 
				
			||||||
	quicx.UDPLikeConn
 | 
						UDPLikeConn
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var _ quicx.UDPLikeConn = &quicErrWrapperUDPLikeConn{}
 | 
					var _ UDPLikeConn = &quicErrWrapperUDPLikeConn{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WriteTo implements quicx.UDPLikeConn.WriteTo.
 | 
					// WriteTo implements UDPLikeConn.WriteTo.
 | 
				
			||||||
func (c *quicErrWrapperUDPLikeConn) WriteTo(p []byte, addr net.Addr) (int, error) {
 | 
					func (c *quicErrWrapperUDPLikeConn) WriteTo(p []byte, addr net.Addr) (int, error) {
 | 
				
			||||||
	count, err := c.UDPLikeConn.WriteTo(p, addr)
 | 
						count, err := c.UDPLikeConn.WriteTo(p, addr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@ -381,7 +385,7 @@ func (c *quicErrWrapperUDPLikeConn) WriteTo(p []byte, addr net.Addr) (int, error
 | 
				
			|||||||
	return count, nil
 | 
						return count, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ReadFrom implements quicx.UDPLikeConn.ReadFrom.
 | 
					// ReadFrom implements UDPLikeConn.ReadFrom.
 | 
				
			||||||
func (c *quicErrWrapperUDPLikeConn) ReadFrom(b []byte) (int, net.Addr, error) {
 | 
					func (c *quicErrWrapperUDPLikeConn) ReadFrom(b []byte) (int, net.Addr, error) {
 | 
				
			||||||
	n, addr, err := c.UDPLikeConn.ReadFrom(b)
 | 
						n, addr, err := c.UDPLikeConn.ReadFrom(b)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@ -390,7 +394,7 @@ func (c *quicErrWrapperUDPLikeConn) ReadFrom(b []byte) (int, net.Addr, error) {
 | 
				
			|||||||
	return n, addr, nil
 | 
						return n, addr, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Close implements quicx.UDPLikeConn.Close.
 | 
					// Close implements UDPLikeConn.Close.
 | 
				
			||||||
func (c *quicErrWrapperUDPLikeConn) Close() error {
 | 
					func (c *quicErrWrapperUDPLikeConn) Close() error {
 | 
				
			||||||
	err := c.UDPLikeConn.Close()
 | 
						err := c.UDPLikeConn.Close()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,6 @@ import (
 | 
				
			|||||||
	"github.com/google/go-cmp/cmp"
 | 
						"github.com/google/go-cmp/cmp"
 | 
				
			||||||
	"github.com/lucas-clemente/quic-go"
 | 
						"github.com/lucas-clemente/quic-go"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
 | 
						"github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
 | 
				
			||||||
	"github.com/ooni/probe-cli/v3/internal/netxlite/quicx"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestNewQUICListener(t *testing.T) {
 | 
					func TestNewQUICListener(t *testing.T) {
 | 
				
			||||||
@ -108,7 +107,7 @@ func TestQUICDialerQUICGo(t *testing.T) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			systemdialer := quicDialerQUICGo{
 | 
								systemdialer := quicDialerQUICGo{
 | 
				
			||||||
				QUICListener: &mocks.QUICListener{
 | 
									QUICListener: &mocks.QUICListener{
 | 
				
			||||||
					MockListen: func(addr *net.UDPAddr) (quicx.UDPLikeConn, error) {
 | 
										MockListen: func(addr *net.UDPAddr) (UDPLikeConn, error) {
 | 
				
			||||||
						return nil, expected
 | 
											return nil, expected
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
@ -478,7 +477,7 @@ func TestQUICListenerErrWrapper(t *testing.T) {
 | 
				
			|||||||
			expectedConn := &mocks.QUICUDPLikeConn{}
 | 
								expectedConn := &mocks.QUICUDPLikeConn{}
 | 
				
			||||||
			ql := &quicListenerErrWrapper{
 | 
								ql := &quicListenerErrWrapper{
 | 
				
			||||||
				QUICListener: &mocks.QUICListener{
 | 
									QUICListener: &mocks.QUICListener{
 | 
				
			||||||
					MockListen: func(addr *net.UDPAddr) (quicx.UDPLikeConn, error) {
 | 
										MockListen: func(addr *net.UDPAddr) (UDPLikeConn, error) {
 | 
				
			||||||
						return expectedConn, nil
 | 
											return expectedConn, nil
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
@ -497,7 +496,7 @@ func TestQUICListenerErrWrapper(t *testing.T) {
 | 
				
			|||||||
			expectedErr := io.EOF
 | 
								expectedErr := io.EOF
 | 
				
			||||||
			ql := &quicListenerErrWrapper{
 | 
								ql := &quicListenerErrWrapper{
 | 
				
			||||||
				QUICListener: &mocks.QUICListener{
 | 
									QUICListener: &mocks.QUICListener{
 | 
				
			||||||
					MockListen: func(addr *net.UDPAddr) (quicx.UDPLikeConn, error) {
 | 
										MockListen: func(addr *net.UDPAddr) (UDPLikeConn, error) {
 | 
				
			||||||
						return nil, expectedErr
 | 
											return nil, expectedErr
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,4 @@
 | 
				
			|||||||
// Package quicx contains lucas-clemente/quic-go extensions.
 | 
					// Package quicx contains lucas-clemente/quic-go extensions.
 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// This code introduces the UDPLikeConn, whose documentation explain
 | 
					 | 
				
			||||||
// why we need to introduce this new type. We could not put this
 | 
					 | 
				
			||||||
// code inside an existing package because it's used (as of 20 Aug 2021)
 | 
					 | 
				
			||||||
// by the netxlite package as well as by the mocks package.
 | 
					 | 
				
			||||||
package quicx
 | 
					package quicx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
@ -17,8 +12,8 @@ import (
 | 
				
			|||||||
//
 | 
					//
 | 
				
			||||||
// The QUIC library will treat this connection as a "dumb"
 | 
					// The QUIC library will treat this connection as a "dumb"
 | 
				
			||||||
// net.PacketConn, calling its ReadFrom and WriteTo methods
 | 
					// net.PacketConn, calling its ReadFrom and WriteTo methods
 | 
				
			||||||
// as opposed to more advanced methods that are available
 | 
					// as opposed to more efficient methods that are available
 | 
				
			||||||
// under Linux and FreeBSD and improve the performance.
 | 
					// under Linux and (maybe?) FreeBSD.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// It seems fine to avoid performance optimizations, because
 | 
					// It seems fine to avoid performance optimizations, because
 | 
				
			||||||
// they would complicate the implementation on our side and
 | 
					// they would complicate the implementation on our side and
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@ import (
 | 
				
			|||||||
	"golang.org/x/net/idna"
 | 
						"golang.org/x/net/idna"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HTTPSSvc is the type returned for HTTPSSvc queries.
 | 
					// HTTPSSvc is the type returned for HTTPS queries.
 | 
				
			||||||
type HTTPSSvc = dnsx.HTTPSSvc
 | 
					type HTTPSSvc = dnsx.HTTPSSvc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Resolver performs domain name resolutions.
 | 
					// Resolver performs domain name resolutions.
 | 
				
			||||||
@ -28,26 +28,31 @@ type Resolver interface {
 | 
				
			|||||||
	// CloseIdleConnections closes idle connections, if any.
 | 
						// CloseIdleConnections closes idle connections, if any.
 | 
				
			||||||
	CloseIdleConnections()
 | 
						CloseIdleConnections()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// LookupHTTPS issues a single HTTPS query for
 | 
						// LookupHTTPS issues an HTTPS query for a domain.
 | 
				
			||||||
	// a domain without any retry mechanism whatsoever.
 | 
					 | 
				
			||||||
	LookupHTTPS(
 | 
						LookupHTTPS(
 | 
				
			||||||
		ctx context.Context, domain string) (*HTTPSSvc, error)
 | 
							ctx context.Context, domain string) (*HTTPSSvc, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ErrNoDNSTransport indicates that the requested Resolver operation
 | 
					// ErrNoDNSTransport is the error returned when you attempt to perform
 | 
				
			||||||
// cannot be performed because we're using the "system" resolver.
 | 
					// a DNS operation that requires a custom DNSTransport (e.g., DNSOverHTTPS)
 | 
				
			||||||
 | 
					// but you are using the "system" resolver instead.
 | 
				
			||||||
var ErrNoDNSTransport = errors.New("operation requires a DNS transport")
 | 
					var ErrNoDNSTransport = errors.New("operation requires a DNS transport")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewResolverStdlib creates a new Resolver by combining
 | 
					// NewResolverStdlib creates a new Resolver by combining WrapResolver
 | 
				
			||||||
// WrapResolver with an internal "system" resolver type that
 | 
					// with an internal "system" resolver type.
 | 
				
			||||||
// adds extra functionality to net.Resolver.
 | 
					 | 
				
			||||||
func NewResolverStdlib(logger Logger) Resolver {
 | 
					func NewResolverStdlib(logger Logger) Resolver {
 | 
				
			||||||
	return WrapResolver(logger, &resolverSystem{})
 | 
						return WrapResolver(logger, &resolverSystem{})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewResolverUDP creates a new Resolver by combining
 | 
					// NewResolverUDP creates a new Resolver using DNS-over-UDP.
 | 
				
			||||||
// WrapResolver with a SerialResolver attached to
 | 
					//
 | 
				
			||||||
// a DNSOverUDP transport.
 | 
					// Arguments:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// - logger is the logger to use
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// - dialer is the dialer to create and connect UDP conns
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// - address is the server address (e.g., 1.1.1.1:53)
 | 
				
			||||||
func NewResolverUDP(logger Logger, dialer Dialer, address string) Resolver {
 | 
					func NewResolverUDP(logger Logger, dialer Dialer, address string) Resolver {
 | 
				
			||||||
	return WrapResolver(logger, NewSerialResolver(
 | 
						return WrapResolver(logger, NewSerialResolver(
 | 
				
			||||||
		NewDNSOverUDP(dialer, address),
 | 
							NewDNSOverUDP(dialer, address),
 | 
				
			||||||
@ -68,6 +73,8 @@ func NewResolverUDP(logger Logger, dialer Dialer, address string) Resolver {
 | 
				
			|||||||
//
 | 
					//
 | 
				
			||||||
// 5. enforces reasonable timeouts (
 | 
					// 5. enforces reasonable timeouts (
 | 
				
			||||||
// see https://github.com/ooni/probe/issues/1726).
 | 
					// see https://github.com/ooni/probe/issues/1726).
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This is a low-level factory. Use only if out of alternatives.
 | 
				
			||||||
func WrapResolver(logger Logger, resolver Resolver) Resolver {
 | 
					func WrapResolver(logger Logger, resolver Resolver) Resolver {
 | 
				
			||||||
	return &resolverIDNA{
 | 
						return &resolverIDNA{
 | 
				
			||||||
		Resolver: &resolverLogger{
 | 
							Resolver: &resolverLogger{
 | 
				
			||||||
@ -223,7 +230,9 @@ func (r *resolverShortCircuitIPAddr) LookupHost(ctx context.Context, hostname st
 | 
				
			|||||||
	return r.Resolver.LookupHost(ctx, hostname)
 | 
						return r.Resolver.LookupHost(ctx, hostname)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ErrNoResolver indicates you are using a dialer without a resolver.
 | 
					// ErrNoResolver is the type of error returned by "without resolver"
 | 
				
			||||||
 | 
					// dialer when asked to dial for and endpoint containing a domain name,
 | 
				
			||||||
 | 
					// since they can only dial for endpoints containing IP addresses.
 | 
				
			||||||
var ErrNoResolver = errors.New("no configured resolver")
 | 
					var ErrNoResolver = errors.New("no configured resolver")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// nullResolver is a resolver that is not capable of resolving
 | 
					// nullResolver is a resolver that is not capable of resolving
 | 
				
			||||||
 | 
				
			|||||||
@ -9,16 +9,26 @@ import (
 | 
				
			|||||||
	"github.com/ooni/probe-cli/v3/internal/atomicx"
 | 
						"github.com/ooni/probe-cli/v3/internal/atomicx"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SerialResolver is a resolver that first issues an A query and then
 | 
					// SerialResolver uses a transport and sends performs a LookupHost
 | 
				
			||||||
// issues an AAAA query for the requested domain.
 | 
					// operation in a serial fashion (query for A first, wait for response,
 | 
				
			||||||
 | 
					// then query for AAAA, and wait for response), hence its name.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should probably use NewSerialResolver to create a new instance.
 | 
				
			||||||
type SerialResolver struct {
 | 
					type SerialResolver struct {
 | 
				
			||||||
	Encoder     DNSEncoder
 | 
						// Encoder is the MANDATORY encoder to use.
 | 
				
			||||||
	Decoder     DNSDecoder
 | 
						Encoder DNSEncoder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Decoder is the MANDATORY decoder to use.
 | 
				
			||||||
 | 
						Decoder DNSDecoder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// NumTimeouts is MANDATORY and counts the number of timeouts.
 | 
				
			||||||
	NumTimeouts *atomicx.Int64
 | 
						NumTimeouts *atomicx.Int64
 | 
				
			||||||
	Txp         DNSTransport
 | 
					
 | 
				
			||||||
 | 
						// Txp is the underlying DNS transport.
 | 
				
			||||||
 | 
						Txp DNSTransport
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewSerialResolver creates a new OONI Resolver instance.
 | 
					// NewSerialResolver creates a new SerialResolver instance.
 | 
				
			||||||
func NewSerialResolver(t DNSTransport) *SerialResolver {
 | 
					func NewSerialResolver(t DNSTransport) *SerialResolver {
 | 
				
			||||||
	return &SerialResolver{
 | 
						return &SerialResolver{
 | 
				
			||||||
		Encoder:     &DNSEncoderMiekg{},
 | 
							Encoder:     &DNSEncoderMiekg{},
 | 
				
			||||||
@ -33,22 +43,22 @@ func (r *SerialResolver) Transport() DNSTransport {
 | 
				
			|||||||
	return r.Txp
 | 
						return r.Txp
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Network implements Resolver.Network
 | 
					// Network returns the "network" of the underlying transport.
 | 
				
			||||||
func (r *SerialResolver) Network() string {
 | 
					func (r *SerialResolver) Network() string {
 | 
				
			||||||
	return r.Txp.Network()
 | 
						return r.Txp.Network()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Address implements Resolver.Address
 | 
					// Address returns the "address" of the underlying transport.
 | 
				
			||||||
func (r *SerialResolver) Address() string {
 | 
					func (r *SerialResolver) Address() string {
 | 
				
			||||||
	return r.Txp.Address()
 | 
						return r.Txp.Address()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CloseIdleConnections closes idle connections.
 | 
					// CloseIdleConnections closes idle connections, if any.
 | 
				
			||||||
func (r *SerialResolver) CloseIdleConnections() {
 | 
					func (r *SerialResolver) CloseIdleConnections() {
 | 
				
			||||||
	r.Txp.CloseIdleConnections()
 | 
						r.Txp.CloseIdleConnections()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LookupHost implements Resolver.LookupHost.
 | 
					// LookupHost performs an A lookup followed by an AAAA lookup for hostname.
 | 
				
			||||||
func (r *SerialResolver) LookupHost(ctx context.Context, hostname string) ([]string, error) {
 | 
					func (r *SerialResolver) LookupHost(ctx context.Context, hostname string) ([]string, error) {
 | 
				
			||||||
	var addrs []string
 | 
						var addrs []string
 | 
				
			||||||
	addrsA, errA := r.lookupHostWithRetry(ctx, hostname, dns.TypeA)
 | 
						addrsA, errA := r.lookupHostWithRetry(ctx, hostname, dns.TypeA)
 | 
				
			||||||
 | 
				
			|||||||
@ -51,7 +51,10 @@ var (
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TLSVersionString returns a TLS version string.
 | 
					// TLSVersionString returns a TLS version string. If value is zero, we
 | 
				
			||||||
 | 
					// return the empty string. If the value is unknown, we return
 | 
				
			||||||
 | 
					// `TLS_VERSION_UNKNOWN_ddd` where `ddd` is the numeric value passed
 | 
				
			||||||
 | 
					// to this function.
 | 
				
			||||||
func TLSVersionString(value uint16) string {
 | 
					func TLSVersionString(value uint16) string {
 | 
				
			||||||
	if str, found := tlsVersionString[value]; found {
 | 
						if str, found := tlsVersionString[value]; found {
 | 
				
			||||||
		return str
 | 
							return str
 | 
				
			||||||
@ -59,7 +62,10 @@ func TLSVersionString(value uint16) string {
 | 
				
			|||||||
	return fmt.Sprintf("TLS_VERSION_UNKNOWN_%d", value)
 | 
						return fmt.Sprintf("TLS_VERSION_UNKNOWN_%d", value)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TLSCipherSuiteString returns the TLS cipher suite as a string.
 | 
					// TLSCipherSuiteString returns the TLS cipher suite as a string. If value
 | 
				
			||||||
 | 
					// is zero, we return the empty string. If we don't know the mapping from
 | 
				
			||||||
 | 
					// the value to a cipher suite name, we return `TLS_CIPHER_SUITE_UNKNOWN_ddd`
 | 
				
			||||||
 | 
					// where `ddd` is the numeric value passed to this function.
 | 
				
			||||||
func TLSCipherSuiteString(value uint16) string {
 | 
					func TLSCipherSuiteString(value uint16) string {
 | 
				
			||||||
	if str, found := tlsCipherSuiteString[value]; found {
 | 
						if str, found := tlsCipherSuiteString[value]; found {
 | 
				
			||||||
		return str
 | 
							return str
 | 
				
			||||||
@ -67,8 +73,9 @@ func TLSCipherSuiteString(value uint16) string {
 | 
				
			|||||||
	return fmt.Sprintf("TLS_CIPHER_SUITE_UNKNOWN_%d", value)
 | 
						return fmt.Sprintf("TLS_CIPHER_SUITE_UNKNOWN_%d", value)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewDefaultCertPool returns a copy of the default x509
 | 
					// NewDefaultCertPool returns the default x509 certificate pool
 | 
				
			||||||
// certificate pool that we bundle from Mozilla.
 | 
					// that we bundle from Mozilla. It's safe to modify the returned
 | 
				
			||||||
 | 
					// value: every invocation returns a distinct *x509.CertPool instance.
 | 
				
			||||||
func NewDefaultCertPool() *x509.CertPool {
 | 
					func NewDefaultCertPool() *x509.CertPool {
 | 
				
			||||||
	pool := x509.NewCertPool()
 | 
						pool := x509.NewCertPool()
 | 
				
			||||||
	// Assumption: AppendCertsFromPEM cannot fail because we
 | 
						// Assumption: AppendCertsFromPEM cannot fail because we
 | 
				
			||||||
@ -82,7 +89,9 @@ func NewDefaultCertPool() *x509.CertPool {
 | 
				
			|||||||
var ErrInvalidTLSVersion = errors.New("invalid TLS version")
 | 
					var ErrInvalidTLSVersion = errors.New("invalid TLS version")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ConfigureTLSVersion configures the correct TLS version into
 | 
					// ConfigureTLSVersion configures the correct TLS version into
 | 
				
			||||||
// the specified *tls.Config or returns an error.
 | 
					// a *tls.Config or returns ErrInvalidTLSVersion.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Recognized strings: TLSv1.3, TLSv1.2, TLSv1.1, TLSv1.0.
 | 
				
			||||||
func ConfigureTLSVersion(config *tls.Config, version string) error {
 | 
					func ConfigureTLSVersion(config *tls.Config, version string) error {
 | 
				
			||||||
	switch version {
 | 
						switch version {
 | 
				
			||||||
	case "TLSv1.3":
 | 
						case "TLSv1.3":
 | 
				
			||||||
@ -106,7 +115,10 @@ func ConfigureTLSVersion(config *tls.Config, version string) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TLSConn is the type of connection that oohttp expects from
 | 
					// TLSConn is the type of connection that oohttp expects from
 | 
				
			||||||
// any library that implements TLS functionality.
 | 
					// any library that implements TLS functionality. By using this
 | 
				
			||||||
 | 
					// kind of TLSConn we're able to use both the standard library
 | 
				
			||||||
 | 
					// and gitlab.com/yawning/utls.git to perform TLS operations. Note
 | 
				
			||||||
 | 
					// that the stdlib's tls.Conn implements this interface.
 | 
				
			||||||
type TLSConn = oohttp.TLSConn
 | 
					type TLSConn = oohttp.TLSConn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Ensures that a tls.Conn implements the TLSConn interface.
 | 
					// Ensures that a tls.Conn implements the TLSConn interface.
 | 
				
			||||||
@ -118,15 +130,24 @@ type TLSHandshaker interface {
 | 
				
			|||||||
	// the given config. This function DOES NOT take ownership of the connection
 | 
						// the given config. This function DOES NOT take ownership of the connection
 | 
				
			||||||
	// and it's your responsibility to close it on failure.
 | 
						// and it's your responsibility to close it on failure.
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
 | 
						// Recommended tlsConfig setup:
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// - set ServerName to be the SNI;
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// - set RootCAs to NewDefaultCertPool();
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// - set NextProtos to []string{"h2", "http/1.1"} for HTTPS
 | 
				
			||||||
 | 
						// and []string{"dot"} for DNS-over-TLS.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
	// QUIRK: The returned connection will always implement the TLSConn interface
 | 
						// QUIRK: The returned connection will always implement the TLSConn interface
 | 
				
			||||||
	// exposed by this package. A future version of this interface will instead
 | 
						// exposed by this package. A future version of this interface will instead
 | 
				
			||||||
	// return directly a TLSConn to avoid unconditional castings.
 | 
						// return directly a TLSConn to avoid unconditional castings.
 | 
				
			||||||
	Handshake(ctx context.Context, conn net.Conn, config *tls.Config) (
 | 
						Handshake(ctx context.Context, conn net.Conn, tlsConfig *tls.Config) (
 | 
				
			||||||
		net.Conn, tls.ConnectionState, error)
 | 
							net.Conn, tls.ConnectionState, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewTLSHandshakerStdlib creates a new TLS handshaker using the
 | 
					// NewTLSHandshakerStdlib creates a new TLS handshaker using the
 | 
				
			||||||
// go standard library to create TLS connections.
 | 
					// go standard library to manage TLS.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// The handshaker guarantees:
 | 
					// The handshaker guarantees:
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
@ -235,18 +256,17 @@ type TLSDialer interface {
 | 
				
			|||||||
	// CloseIdleConnections closes idle connections, if any.
 | 
						// CloseIdleConnections closes idle connections, if any.
 | 
				
			||||||
	CloseIdleConnections()
 | 
						CloseIdleConnections()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// DialTLSContext dials a TLS connection.
 | 
						// DialTLSContext dials a TLS connection. This method will always
 | 
				
			||||||
 | 
						// return to you a TLSConn, so you can always safely cast to TLSConn.
 | 
				
			||||||
	DialTLSContext(ctx context.Context, network, address string) (net.Conn, error)
 | 
						DialTLSContext(ctx context.Context, network, address string) (net.Conn, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewTLSDialer creates a new TLS dialer using the given dialer
 | 
					// NewTLSDialer creates a new TLS dialer using the given dialer and handshaker.
 | 
				
			||||||
// and TLS handshaker to establish TLS connections.
 | 
					 | 
				
			||||||
func NewTLSDialer(dialer Dialer, handshaker TLSHandshaker) TLSDialer {
 | 
					func NewTLSDialer(dialer Dialer, handshaker TLSHandshaker) TLSDialer {
 | 
				
			||||||
	return NewTLSDialerWithConfig(dialer, handshaker, &tls.Config{})
 | 
						return NewTLSDialerWithConfig(dialer, handshaker, &tls.Config{})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewTLSDialerWithConfig is like NewTLSDialer but takes an optional config
 | 
					// NewTLSDialerWithConfig is like NewTLSDialer with an optional config.
 | 
				
			||||||
// parameter containing your desired TLS configuration.
 | 
					 | 
				
			||||||
func NewTLSDialerWithConfig(d Dialer, h TLSHandshaker, c *tls.Config) TLSDialer {
 | 
					func NewTLSDialerWithConfig(d Dialer, h TLSHandshaker, c *tls.Config) TLSDialer {
 | 
				
			||||||
	return &tlsDialer{Config: c, Dialer: d, TLSHandshaker: h}
 | 
						return &tlsDialer{Config: c, Dialer: d, TLSHandshaker: h}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -351,10 +371,11 @@ func (h *tlsHandshakerErrWrapper) Handshake(
 | 
				
			|||||||
	return tlsconn, state, nil
 | 
						return tlsconn, state, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ErrNoTLSDialer indicates that no TLS dialer is configured.
 | 
					// ErrNoTLSDialer is the type of error returned by "null" TLS dialers
 | 
				
			||||||
 | 
					// when you attempt to dial with them.
 | 
				
			||||||
var ErrNoTLSDialer = errors.New("no configured TLS dialer")
 | 
					var ErrNoTLSDialer = errors.New("no configured TLS dialer")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewNullTLSDialer returns a TLS dialer that always fails.
 | 
					// NewNullTLSDialer returns a TLS dialer that always fails with ErrNoTLSDialer.
 | 
				
			||||||
func NewNullTLSDialer() TLSDialer {
 | 
					func NewNullTLSDialer() TLSDialer {
 | 
				
			||||||
	return &nullTLSDialer{}
 | 
						return &nullTLSDialer{}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,8 +9,10 @@ import (
 | 
				
			|||||||
	utls "gitlab.com/yawning/utls.git"
 | 
						utls "gitlab.com/yawning/utls.git"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewTLSHandshakerUTLS creates a new TLS handshaker using the
 | 
					// NewTLSHandshakerUTLS creates a new TLS handshaker using
 | 
				
			||||||
// gitlab.com/yawning/utls library to create TLS conns.
 | 
					// gitlab.com/yawning/utls for TLS.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The id is the address of something like utls.HelloFirefox_55.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// The handshaker guarantees:
 | 
					// The handshaker guarantees:
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
@ -51,8 +53,7 @@ func newConnUTLS(clientHello *utls.ClientHelloID) func(conn net.Conn, config *tl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// ErrUTLSHandshakePanic indicates that there was panic handshaking
 | 
					// ErrUTLSHandshakePanic indicates that there was panic handshaking
 | 
				
			||||||
// when we were using the yawning/utls library for parroting.
 | 
					// when we were using the yawning/utls library for parroting.
 | 
				
			||||||
//
 | 
					// See https://github.com/ooni/probe/issues/1770 for more information.
 | 
				
			||||||
// See https://github.com/ooni/probe/issues/1770
 | 
					 | 
				
			||||||
var ErrUTLSHandshakePanic = errors.New("utls: handshake panic")
 | 
					var ErrUTLSHandshakePanic = errors.New("utls: handshake panic")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *utlsConn) HandshakeContext(ctx context.Context) (err error) {
 | 
					func (c *utlsConn) HandshakeContext(ctx context.Context) (err error) {
 | 
				
			||||||
 | 
				
			|||||||
@ -2,9 +2,34 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Netxlite is the underlying networking library we use in OONI. In
 | 
					Netxlite is the underlying networking library we use in OONI. In
 | 
				
			||||||
most cases, network experiments do not use netxlite directly, rather
 | 
					most cases, network experiments do not use netxlite directly, rather
 | 
				
			||||||
they use abstractions built on top of netxlite. Though, you need to
 | 
					they use abstractions built on top of netxlite (e.g., measurex).
 | 
				
			||||||
know about netxlite if you need to modify these abstractions.
 | 
					Though, you need to know about netxlite if you need to modify
 | 
				
			||||||
 | 
					these abstractions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
For this reason, this chapter shows the basic netxlite primitives
 | 
					For this reason, this chapter shows the basic netxlite primitives
 | 
				
			||||||
that we use when writing higher-level measurement primitives.
 | 
					that we use when writing higher-level measurement primitives.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					We will start from simple primitives and we will combine them
 | 
				
			||||||
 | 
					together to reach to the point where we can perform GET requests
 | 
				
			||||||
 | 
					to websites using already existing TLS or QUIC connections. (The code
 | 
				
			||||||
 | 
					we will end up writing will look like a stripped down version of
 | 
				
			||||||
 | 
					the measurex library, for which there is a separate tutorial.)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Index:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [chapter01](chapter01) shows how to establish TCP connections;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [chapter02](chapter02) covers TLS handshakes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [chapter03](chapter03) discusses TLS parroting;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [chapter04](chapter04) shows how to establish QUIC sessions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [chapter05](chapter05) is about the "system" DNS resolver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [chapter06](chapter06) discusses custom DNS-over-UDP resolvers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [chapter07](chapter07) shows how to perform an HTTP GET
 | 
				
			||||||
 | 
					using an already existing TLS connection to a website;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [chapter08](chapter08) is like chapter07 but for QUIC.
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user