diff --git a/internal/experiment/echcheck/handshake.go b/internal/experiment/echcheck/handshake.go index 3ecc648262..1906cef26d 100644 --- a/internal/experiment/echcheck/handshake.go +++ b/internal/experiment/echcheck/handshake.go @@ -2,23 +2,21 @@ package echcheck import ( "context" - "crypto/rand" "crypto/tls" "net" "time" - "github.com/apex/log" "github.com/ooni/probe-cli/v3/internal/logx" "github.com/ooni/probe-cli/v3/internal/measurexlite" "github.com/ooni/probe-cli/v3/internal/model" "github.com/ooni/probe-cli/v3/internal/netxlite" - utls "gitlab.com/yawning/utls.git" ) const echExtensionType uint16 = 0xfe0d func connectAndHandshake( ctx context.Context, + trace *measurexlite.Trace, startTime time.Time, address string, sni string, outerSni string, logger model.Logger) (chan model.ArchivalTLSOrQUICHandshakeResult, error) { @@ -38,6 +36,7 @@ func connectAndHandshake( if outerSni == "" { res = handshake( ctx, + trace, conn, startTime, address, @@ -47,6 +46,7 @@ func connectAndHandshake( } else { res = handshakeWithEch( ctx, + trace, conn, startTime, address, @@ -63,24 +63,15 @@ func connectAndHandshake( return channel, nil } -func handshake(ctx context.Context, conn net.Conn, zeroTime time.Time, +func handshake(ctx context.Context, trace *measurexlite.Trace, conn net.Conn, zeroTime time.Time, address string, sni string, logger model.Logger) *model.ArchivalTLSOrQUICHandshakeResult { - return handshakeWithExtension(ctx, conn, zeroTime, address, sni, []utls.TLSExtension{}, logger) + return doHandshake(ctx, trace, conn, zeroTime, address, sni, []byte{}, logger) } -func handshakeWithEch(ctx context.Context, conn net.Conn, zeroTime time.Time, +func handshakeWithEch(ctx context.Context, trace *measurexlite.Trace, conn net.Conn, zeroTime time.Time, address string, sni string, logger model.Logger) *model.ArchivalTLSOrQUICHandshakeResult { - payload, err := generateGreaseExtension(rand.Reader) - if err != nil { - panic("failed to generate grease ECH: " + err.Error()) - } - - var utlsEchExtension utls.GenericExtension - utlsEchExtension.Id = echExtensionType - utlsEchExtension.Data = payload - - hs := handshakeWithExtension(ctx, conn, zeroTime, address, sni, []utls.TLSExtension{&utlsEchExtension}, logger) + hs := doHandshake(ctx, trace, conn, zeroTime, address, sni, []byte("ECHCONFIG"), logger) hs.ECHConfig = "GREASE" hs.OuterServerName = sni return hs @@ -93,14 +84,18 @@ func handshakeMaybePrintWithECH(doprint bool) string { return "" } -func handshakeWithExtension(ctx context.Context, conn net.Conn, zeroTime time.Time, address string, sni string, - extensions []utls.TLSExtension, logger model.Logger) *model.ArchivalTLSOrQUICHandshakeResult { +func doHandshake(ctx context.Context, trace *measurexlite.Trace, conn net.Conn, zeroTime time.Time, address string, sni string, + echConfig []byte, logger model.Logger) *model.ArchivalTLSOrQUICHandshakeResult { + ctx, cancel := context.WithTimeout(ctx, 3*time.Second) + defer cancel() tlsConfig := genTLSConfig(sni) + if len(echConfig) > 0 { + tlsConfig.EncryptedClientHelloConfigList = echConfig + } - handshakerConstructor := newHandshakerWithExtensions(extensions) - tracedHandshaker := handshakerConstructor(log.Log, &utls.HelloFirefox_Auto) + tracedHandshaker := trace.NewTLSHandshakerStdlib(logger) - ol := logx.NewOperationLogger(logger, "echcheck: TLSHandshake%s", handshakeMaybePrintWithECH(len(extensions) > 0)) + ol := logx.NewOperationLogger(logger, "echcheck: TLSHandshake with ECH") start := time.Now() maybeTLSConn, err := tracedHandshaker.Handshake(ctx, conn, tlsConfig) finish := time.Now() diff --git a/internal/experiment/echcheck/measure.go b/internal/experiment/echcheck/measure.go index 243c44c88f..8dac6f1d3a 100644 --- a/internal/experiment/echcheck/measure.go +++ b/internal/experiment/echcheck/measure.go @@ -78,17 +78,17 @@ func (m *Measurer) Run( handshakes := []func() (chan model.ArchivalTLSOrQUICHandshakeResult, error){ // handshake with ECH disabled and SNI coming from the URL func() (chan model.ArchivalTLSOrQUICHandshakeResult, error) { - return connectAndHandshake(ctx, args.Measurement.MeasurementStartTimeSaved, + return connectAndHandshake(ctx, trace, args.Measurement.MeasurementStartTimeSaved, address, parsed.Host, "", args.Session.Logger()) }, // handshake with ECH enabled and ClientHelloOuter SNI coming from the URL func() (chan model.ArchivalTLSOrQUICHandshakeResult, error) { - return connectAndHandshake(ctx, args.Measurement.MeasurementStartTimeSaved, + return connectAndHandshake(ctx, trace, args.Measurement.MeasurementStartTimeSaved, address, parsed.Host, parsed.Host, args.Session.Logger()) }, // handshake with ECH enabled and hardcoded different ClientHelloOuter SNI func() (chan model.ArchivalTLSOrQUICHandshakeResult, error) { - return connectAndHandshake(ctx, args.Measurement.MeasurementStartTimeSaved, + return connectAndHandshake(ctx, trace, args.Measurement.MeasurementStartTimeSaved, address, parsed.Host, "cloudflare.com", args.Session.Logger()) }, } diff --git a/internal/experiment/echcheck/utls.go b/internal/experiment/echcheck/utls.go deleted file mode 100644 index 50eb632377..0000000000 --- a/internal/experiment/echcheck/utls.go +++ /dev/null @@ -1,49 +0,0 @@ -package echcheck - -import ( - "context" - "crypto/tls" - "net" - - "github.com/ooni/probe-cli/v3/internal/model" - "github.com/ooni/probe-cli/v3/internal/netxlite" - "github.com/ooni/probe-cli/v3/internal/runtimex" - utls "gitlab.com/yawning/utls.git" -) - -type tlsHandshakerWithExtensions struct { - extensions []utls.TLSExtension - dl model.DebugLogger - id *utls.ClientHelloID -} - -var _ model.TLSHandshaker = &tlsHandshakerWithExtensions{} - -// newHandshakerWithExtensions returns a NewHandshaker function for creating -// tlsHandshakerWithExtensions instances. -func newHandshakerWithExtensions(extensions []utls.TLSExtension) func(dl model.DebugLogger, id *utls.ClientHelloID) model.TLSHandshaker { - return func(dl model.DebugLogger, id *utls.ClientHelloID) model.TLSHandshaker { - return &tlsHandshakerWithExtensions{ - extensions: extensions, - dl: dl, - id: id, - } - } -} - -func (t *tlsHandshakerWithExtensions) Handshake( - ctx context.Context, tcpConn net.Conn, tlsConfig *tls.Config) (model.TLSConn, error) { - tlsConn, err := netxlite.NewUTLSConn(tcpConn, tlsConfig, t.id) - runtimex.Assert(err == nil, "unexpected error when creating UTLSConn") - - if t.extensions != nil && len(t.extensions) != 0 { - runtimex.Try0(tlsConn.BuildHandshakeState()) - tlsConn.Extensions = append(tlsConn.Extensions, t.extensions...) - } - - if err := tlsConn.Handshake(); err != nil { - return nil, err - } - - return tlsConn, nil -} diff --git a/internal/experiment/tlsmiddlebox/tracing.go b/internal/experiment/tlsmiddlebox/tracing.go index dd55b40457..6e887a1818 100644 --- a/internal/experiment/tlsmiddlebox/tracing.go +++ b/internal/experiment/tlsmiddlebox/tracing.go @@ -18,18 +18,8 @@ import ( "github.com/ooni/probe-cli/v3/internal/measurexlite" "github.com/ooni/probe-cli/v3/internal/model" "github.com/ooni/probe-cli/v3/internal/netxlite" - utls "gitlab.com/yawning/utls.git" ) -// ClientIDs to map configurable inputs to uTLS fingerprints -// We use a non-zero index to map to each ClientID -var ClientIDs = map[int]*utls.ClientHelloID{ - 1: &utls.HelloGolang, - 2: &utls.HelloChrome_Auto, - 3: &utls.HelloFirefox_Auto, - 4: &utls.HelloIOS_Auto, -} - // TLSTrace performs tracing using control and target SNI func (m *Measurer) TLSTrace(ctx context.Context, index int64, zeroTime time.Time, logger model.Logger, address string, targetSNI string, trace *CompleteTrace) { @@ -93,10 +83,6 @@ func (m *Measurer) handshakeWithTTL(ctx context.Context, index int64, zeroTime t // 3. Perform the handshake and extract the SO_ERROR value (if any) // Note: we switch to a uTLS Handshaker if the configured ClientID is non-zero thx := trace.NewTLSHandshakerStdlib(logger) - clientId := m.config.clientid() - if clientId > 0 { - thx = trace.NewTLSHandshakerUTLS(logger, ClientIDs[clientId]) - } _, err = thx.Handshake(ctx, conn, genTLSConfig(sni)) ol.Stop(err) soErr := extractSoError(conn) diff --git a/internal/measurexlite/utls.go b/internal/measurexlite/utls.go deleted file mode 100644 index 9250b884c3..0000000000 --- a/internal/measurexlite/utls.go +++ /dev/null @@ -1,19 +0,0 @@ -package measurexlite - -// -// Support for using utls -// - -import ( - "github.com/ooni/probe-cli/v3/internal/model" - utls "gitlab.com/yawning/utls.git" -) - -// NewTLSHandshakerUTLS is equivalent to netxlite.Netx.NewTLSHandshakerUTLS -// except that it returns a model.TLSHandshaker that uses this trace. -func (tx *Trace) NewTLSHandshakerUTLS(dl model.DebugLogger, id *utls.ClientHelloID) model.TLSHandshaker { - return &tlsHandshakerTrace{ - thx: tx.Netx.NewTLSHandshakerUTLS(dl, id), - tx: tx, - } -} diff --git a/internal/measurexlite/utls_test.go b/internal/measurexlite/utls_test.go deleted file mode 100644 index 959aff0657..0000000000 --- a/internal/measurexlite/utls_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package measurexlite - -import ( - "testing" - "time" - - "github.com/ooni/probe-cli/v3/internal/mocks" - "github.com/ooni/probe-cli/v3/internal/model" - utls "gitlab.com/yawning/utls.git" -) - -func TestNewTLSHandshakerUTLS(t *testing.T) { - t.Run("NewTLSHandshakerUTLS creates a wrapped TLSHandshaker", func(t *testing.T) { - underlying := &mocks.TLSHandshaker{} - zeroTime := time.Now() - trace := NewTrace(0, zeroTime) - trace.Netx = &mocks.MeasuringNetwork{ - MockNewTLSHandshakerUTLS: func(logger model.DebugLogger, id *utls.ClientHelloID) model.TLSHandshaker { - return underlying - }, - } - thx := trace.NewTLSHandshakerUTLS(model.DiscardLogger, &utls.HelloGolang) - thxt := thx.(*tlsHandshakerTrace) - if thxt.thx != underlying { - t.Fatal("invalid TLS handshaker") - } - if thxt.tx != trace { - t.Fatal("invalid trace") - } - }) -} diff --git a/internal/model/netx.go b/internal/model/netx.go index f6f4bc78ff..3538a9ffb1 100644 --- a/internal/model/netx.go +++ b/internal/model/netx.go @@ -13,9 +13,7 @@ import ( "syscall" "time" - oohttp "github.com/ooni/oohttp" "github.com/quic-go/quic-go" - utls "gitlab.com/yawning/utls.git" ) // DNSResponse is a parsed DNS response ready for further processing. @@ -225,14 +223,6 @@ type MeasuringNetwork interface { // that is using the go standard library to manage TLS. NewTLSHandshakerStdlib(logger DebugLogger) TLSHandshaker - // NewTLSHandshakerUTLS creates a new TLS handshaker using - // gitlab.com/yawning/utls for TLS that implements error wrapping. - // - // The id is the address of something like utls.HelloFirefox_55. - // - // Passing a nil `id` will make this function panic. - NewTLSHandshakerUTLS(logger DebugLogger, id *utls.ClientHelloID) TLSHandshaker - // NewUDPListener creates a new UDPListener with error wrapping. NewUDPListener() UDPListener } @@ -314,10 +304,7 @@ type Resolver interface { // 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 - -// Ensures that a [*tls.Conn] implements the [TLSConn] interface. -var _ TLSConn = &tls.Conn{} +type TLSConn = *tls.Conn // TLSDialer is a Dialer dialing TLS connections. type TLSDialer interface { diff --git a/internal/netxlite/httpfactory.go b/internal/netxlite/httpfactory.go index 0e57f249fe..1769390e18 100644 --- a/internal/netxlite/httpfactory.go +++ b/internal/netxlite/httpfactory.go @@ -2,14 +2,14 @@ package netxlite import ( "crypto/tls" + "net/http" "net/url" - oohttp "github.com/ooni/oohttp" "github.com/ooni/probe-cli/v3/internal/model" ) // HTTPTransportOption is an initialization option for [NewHTTPTransport]. -type HTTPTransportOption func(txp *oohttp.Transport) +type HTTPTransportOption func(txp *http.Transport) // NewHTTPTransport is the high-level factory to create a [model.HTTPTransport] using // github.com/ooni/oohttp as the HTTP library with HTTP/1.1 and HTTP2 support. @@ -42,7 +42,7 @@ type HTTPTransportOption func(txp *oohttp.Transport) func NewHTTPTransportWithOptions(logger model.Logger, dialer model.Dialer, tlsDialer model.TLSDialer, options ...HTTPTransportOption) model.HTTPTransport { // Using oohttp to support any TLS library. - txp := oohttp.DefaultTransport.(*oohttp.Transport).Clone() + txp := http.DefaultTransport.(*http.Transport).Clone() // This wrapping ensures that we always have a timeout when we // are using HTTP; see https://github.com/ooni/probe/issues/1609. @@ -63,7 +63,7 @@ func NewHTTPTransportWithOptions(logger model.Logger, // Return a fully wrapped HTTP transport return WrapHTTPTransport(logger, &httpTransportConnectionsCloser{ - HTTPTransport: &httpTransportStdlib{&oohttp.StdlibTransport{Transport: txp}}, + HTTPTransport: &httpTransportStdlib{StdlibTransport: txp}, Dialer: dialer, TLSDialer: tlsDialer, }) @@ -72,8 +72,8 @@ func NewHTTPTransportWithOptions(logger model.Logger, // HTTPTransportOptionProxyURL configures the transport to use the given proxyURL // or disables proxying (already the default) if the proxyURL is nil. func HTTPTransportOptionProxyURL(proxyURL *url.URL) HTTPTransportOption { - return func(txp *oohttp.Transport) { - txp.Proxy = func(r *oohttp.Request) (*url.URL, error) { + return func(txp *http.Transport) { + txp.Proxy = func(r *http.Request) (*url.URL, error) { // "If Proxy is nil or returns a nil *URL, no proxy is used." return proxyURL, nil } @@ -83,7 +83,7 @@ func HTTPTransportOptionProxyURL(proxyURL *url.URL) HTTPTransportOption { // HTTPTransportOptionMaxConnsPerHost configures the .MaxConnPerHosts field, which // otherwise uses the default set in github.com/ooni/oohttp. func HTTPTransportOptionMaxConnsPerHost(value int) HTTPTransportOption { - return func(txp *oohttp.Transport) { + return func(txp *http.Transport) { txp.MaxConnsPerHost = value } } @@ -91,7 +91,7 @@ func HTTPTransportOptionMaxConnsPerHost(value int) HTTPTransportOption { // HTTPTransportOptionDisableCompression configures the .DisableCompression field, which // otherwise is set to true, so that this code is ready for measuring out of the box. func HTTPTransportOptionDisableCompression(value bool) HTTPTransportOption { - return func(txp *oohttp.Transport) { + return func(txp *http.Transport) { txp.DisableCompression = value } } @@ -104,7 +104,7 @@ func HTTPTransportOptionDisableCompression(value bool) HTTPTransportOption { // this limitation. Future releases MIGHT use a different technique and, as such, // we MAY remove this option when we don't need it anymore. func HTTPTransportOptionTLSClientConfig(config *tls.Config) HTTPTransportOption { - return func(txp *oohttp.Transport) { + return func(txp *http.Transport) { txp.TLSClientConfig = config } } diff --git a/internal/netxlite/httpfactory_test.go b/internal/netxlite/httpfactory_test.go index c02955c11c..34b67734bb 100644 --- a/internal/netxlite/httpfactory_test.go +++ b/internal/netxlite/httpfactory_test.go @@ -2,12 +2,12 @@ package netxlite import ( "crypto/tls" + "net/http" "net/url" "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - oohttp "github.com/ooni/oohttp" "github.com/ooni/probe-cli/v3/internal/mocks" "github.com/ooni/probe-cli/v3/internal/model" ) @@ -43,17 +43,17 @@ func TestNewHTTPTransportWithOptions(t *testing.T) { // make sure there's the stdlib adapter stdlibAdapter := txpCloser.HTTPTransport.(*httpTransportStdlib) oohttpStdlibAdapter := stdlibAdapter.StdlibTransport - underlying := oohttpStdlibAdapter.Transport + underlying := oohttpStdlibAdapter // now let's check that everything is configured as intended - expectedTxp := oohttp.DefaultTransport.(*oohttp.Transport).Clone() + expectedTxp := http.DefaultTransport.(*http.Transport).Clone() diff := cmp.Diff( expectedTxp, underlying, - cmpopts.IgnoreUnexported(oohttp.Transport{}), + cmpopts.IgnoreUnexported(http.Transport{}), cmpopts.IgnoreUnexported(tls.Config{}), cmpopts.IgnoreFields( - oohttp.Transport{}, + http.Transport{}, "DialContext", "DialTLSContext", "DisableCompression", @@ -83,13 +83,13 @@ func TestNewHTTPTransportWithOptions(t *testing.T) { } }) - unwrap := func(txp model.HTTPTransport) *oohttp.Transport { + unwrap := func(txp model.HTTPTransport) *http.Transport { txpLogger := txp.(*httpTransportLogger) txpErrWrapper := txpLogger.HTTPTransport.(*httpTransportErrWrapper) txpCloser := txpErrWrapper.HTTPTransport.(*httpTransportConnectionsCloser) stdlibAdapter := txpCloser.HTTPTransport.(*httpTransportStdlib) oohttpStdlibAdapter := stdlibAdapter.StdlibTransport - return oohttpStdlibAdapter.Transport + return oohttpStdlibAdapter } t.Run("make sure HTTPTransportOptionProxyURL is WAI", func(t *testing.T) { @@ -107,7 +107,7 @@ func TestNewHTTPTransportWithOptions(t *testing.T) { if underlying.Proxy == nil { t.Fatal("expected non-nil .Proxy") } - got, err := underlying.Proxy(&oohttp.Request{}) + got, err := underlying.Proxy(&http.Request{}) if err != nil { t.Fatal(err) } diff --git a/internal/netxlite/httpquirks.go b/internal/netxlite/httpquirks.go index 271b6e3b95..324c2aeb9b 100644 --- a/internal/netxlite/httpquirks.go +++ b/internal/netxlite/httpquirks.go @@ -11,7 +11,6 @@ package netxlite import ( "net/http" - oohttp "github.com/ooni/oohttp" "github.com/ooni/probe-cli/v3/internal/model" ) @@ -61,7 +60,7 @@ func NewHTTPTransport(logger model.DebugLogger, dialer model.Dialer, tlsDialer m // This function behavior is QUIRKY as documented in [NewHTTPTransport]. func newOOHTTPBaseTransport(dialer model.Dialer, tlsDialer model.TLSDialer) model.HTTPTransport { // Using oohttp to support any TLS library. - txp := oohttp.DefaultTransport.(*oohttp.Transport).Clone() + txp := http.DefaultTransport.(*http.Transport).Clone() // This wrapping ensures that we always have a timeout when we // are using HTTP; see https://github.com/ooni/probe/issues/1609. @@ -90,7 +89,7 @@ func newOOHTTPBaseTransport(dialer model.Dialer, tlsDialer model.TLSDialer) mode // Ensure we correctly forward CloseIdleConnections. return &httpTransportConnectionsCloser{ - HTTPTransport: &httpTransportStdlib{&oohttp.StdlibTransport{Transport: txp}}, + HTTPTransport: &httpTransportStdlib{StdlibTransport: txp}, Dialer: dialer, TLSDialer: tlsDialer, } diff --git a/internal/netxlite/httpstdlib.go b/internal/netxlite/httpstdlib.go index 2f3063dbf9..29e896e827 100644 --- a/internal/netxlite/httpstdlib.go +++ b/internal/netxlite/httpstdlib.go @@ -7,13 +7,12 @@ package netxlite import ( "net/http" - oohttp "github.com/ooni/oohttp" "github.com/ooni/probe-cli/v3/internal/model" ) // stdlibTransport wraps oohttp.StdlibTransport to add .Network() type httpTransportStdlib struct { - StdlibTransport *oohttp.StdlibTransport + StdlibTransport *http.Transport } var _ model.HTTPTransport = &httpTransportStdlib{} diff --git a/internal/netxlite/tls.go b/internal/netxlite/tls.go index 3ad5308a27..90e16fe749 100644 --- a/internal/netxlite/tls.go +++ b/internal/netxlite/tls.go @@ -13,7 +13,6 @@ import ( "net" "time" - ootls "github.com/ooni/oocrypto/tls" "github.com/ooni/probe-cli/v3/internal/model" "github.com/ooni/probe-cli/v3/internal/runtimex" ) @@ -241,7 +240,7 @@ func (h *tlsHandshakerConfigurable) newConn(conn net.Conn, config *tls.Config) ( if h.NewConn != nil { return h.NewConn(conn, config) } - return ootls.NewClientConnStdlib(conn, config) + return tls.Client(conn, config), nil } // tlsHandshakerLogger is a TLSHandshaker with logging. diff --git a/internal/netxlite/utls.go b/internal/netxlite/utls.go deleted file mode 100644 index ba5e096c6a..0000000000 --- a/internal/netxlite/utls.go +++ /dev/null @@ -1,143 +0,0 @@ -package netxlite - -// -// Code to use yawning/utls or refraction-networking/utls -// - -import ( - "context" - "crypto/tls" - "errors" - "fmt" - "net" - "reflect" - - "github.com/ooni/probe-cli/v3/internal/model" - utls "gitlab.com/yawning/utls.git" -) - -// NewTLSHandshakerUTLS implements [model.MeasuringNetwork]. -func (netx *Netx) NewTLSHandshakerUTLS(logger model.DebugLogger, id *utls.ClientHelloID) model.TLSHandshaker { - return newTLSHandshakerLogger(&tlsHandshakerConfigurable{ - NewConn: newUTLSConnFactory(id), - provider: netx.MaybeCustomUnderlyingNetwork(), - }, logger) -} - -// UTLSConn implements TLSConn and uses a utls UConn as its underlying connection -type UTLSConn struct { - // We include the real UConn - *utls.UConn - - // This field helps with writing tests - testableHandshake func() error - - // Required by NetConn - nc net.Conn -} - -// Ensures that a UTLSConn implements the TLSConn interface. -var _ TLSConn = &UTLSConn{} - -// newUTLSConnFactory returns a NewConn function for creating UTLSConn instances. -func newUTLSConnFactory(clientHello *utls.ClientHelloID) func(conn net.Conn, config *tls.Config) (TLSConn, error) { - return func(conn net.Conn, config *tls.Config) (TLSConn, error) { - return NewUTLSConn(conn, config, clientHello) - } -} - -// errUTLSIncompatibleStdlibConfig indicates that the stdlib config you passed to -// NewUTLSConn contains some fields we don't support. -var errUTLSIncompatibleStdlibConfig = errors.New("utls: incompatible stdlib config") - -// NewUTLSConn creates a new connection with the given client hello ID. -func NewUTLSConn(conn net.Conn, config *tls.Config, cid *utls.ClientHelloID) (*UTLSConn, error) { - supportedFields := map[string]bool{ - "DynamicRecordSizingDisabled": true, - "InsecureSkipVerify": true, - "NextProtos": true, - "RootCAs": true, - "ServerName": true, - } - value := reflect.ValueOf(config).Elem() - kind := value.Type() - for idx := 0; idx < value.NumField(); idx++ { - field := value.Field(idx) - if field.IsZero() { - continue - } - fieldKind := kind.Field(idx) - if supportedFields[fieldKind.Name] { - continue - } - err := fmt.Errorf("%w: field %s is nonzero", errUTLSIncompatibleStdlibConfig, fieldKind.Name) - return nil, err - } - uConfig := &utls.Config{ - DynamicRecordSizingDisabled: config.DynamicRecordSizingDisabled, - InsecureSkipVerify: config.InsecureSkipVerify, - RootCAs: config.RootCAs, - NextProtos: config.NextProtos, - ServerName: config.ServerName, - } - tlsConn := utls.UClient(conn, uConfig, *cid) - oconn := &UTLSConn{ - UConn: tlsConn, - testableHandshake: nil, - nc: conn, - } - return oconn, nil -} - -// 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, - } -} - -func (c *UTLSConn) NetConn() net.Conn { - return c.nc -}