ooni-probe-cli/internal/netxlite/utls.go
Simone Basso c1b06a2d09
fix(netxlite): prefer composition over embedding (#733)
This diff has been extracted and adapted from 8848c8c516

The reason to prefer composition over embedding is that we want the
build to break if we add new methods to interfaces we define. If the build
does not break, we may forget about wrapping methods we should
actually be wrapping. I noticed this issue inside netxlite when I was working
on websteps-illustrated and I added support for NS and PTR queries.

See https://github.com/ooni/probe/issues/2096

While there, perform comprehensive netxlite code review
and apply minor changes and improve the docs.
2022-05-15 19:25:27 +02:00

107 lines
3.1 KiB
Go

package netxlite
//
// Code to use yawning/utls or refraction-networking/utls
//
import (
"context"
"crypto/tls"
"errors"
"net"
"github.com/ooni/probe-cli/v3/internal/model"
utls "gitlab.com/yawning/utls.git"
)
// NewTLSHandshakerUTLS creates a new TLS handshaker using
// gitlab.com/yawning/utls for TLS.
//
// The id is the address of something like utls.HelloFirefox_55.
//
// The handshaker guarantees:
//
// 1. logging
//
// 2. error wrapping
//
// Passing a nil `id` will make this function panic.
func NewTLSHandshakerUTLS(logger model.DebugLogger, id *utls.ClientHelloID) model.TLSHandshaker {
return newTLSHandshaker(&tlsHandshakerConfigurable{
NewConn: newConnUTLS(id),
}, logger)
}
// utlsConn implements TLSConn and uses a utls UConn as its underlying connection
type utlsConn struct {
*utls.UConn
testableHandshake func() error
}
// Ensures that a utlsConn implements the TLSConn interface.
var _ TLSConn = &utlsConn{}
// newConnUTLS returns a NewConn function for creating utlsConn instances.
func newConnUTLS(clientHello *utls.ClientHelloID) func(conn net.Conn, config *tls.Config) TLSConn {
return func(conn net.Conn, config *tls.Config) TLSConn {
uConfig := &utls.Config{
RootCAs: config.RootCAs,
NextProtos: config.NextProtos,
ServerName: config.ServerName,
InsecureSkipVerify: config.InsecureSkipVerify,
DynamicRecordSizingDisabled: config.DynamicRecordSizingDisabled,
}
tlsConn := utls.UClient(conn, uConfig, *clientHello)
return &utlsConn{UConn: tlsConn}
}
}
// ErrUTLSHandshakePanic indicates that there was panic handshaking
// when we were using the yawning/utls library for parroting.
// See https://github.com/ooni/probe/issues/1770 for more information.
var ErrUTLSHandshakePanic = errors.New("utls: handshake panic")
func (c *utlsConn) HandshakeContext(ctx context.Context) (err error) {
errch := make(chan error, 1)
go func() {
defer func() {
// See https://github.com/ooni/probe/issues/1770
if recover() != nil {
errch <- ErrUTLSHandshakePanic
}
}()
errch <- c.handshakefn()()
}()
select {
case err = <-errch:
case <-ctx.Done():
err = ctx.Err()
}
return
}
func (c *utlsConn) handshakefn() func() error {
if c.testableHandshake != nil {
return c.testableHandshake
}
return c.UConn.Handshake
}
func (c *utlsConn) ConnectionState() tls.ConnectionState {
uState := c.Conn.ConnectionState()
return tls.ConnectionState{
Version: uState.Version,
HandshakeComplete: uState.HandshakeComplete,
DidResume: uState.DidResume,
CipherSuite: uState.CipherSuite,
NegotiatedProtocol: uState.NegotiatedProtocol,
NegotiatedProtocolIsMutual: uState.NegotiatedProtocolIsMutual,
ServerName: uState.ServerName,
PeerCertificates: uState.PeerCertificates,
VerifiedChains: uState.VerifiedChains,
SignedCertificateTimestamps: uState.SignedCertificateTimestamps,
OCSPResponse: uState.OCSPResponse,
TLSUnique: uState.TLSUnique,
}
}