diff --git a/internal/netxlite/utls.go b/internal/netxlite/utls.go index aedaacd..c9968b7 100644 --- a/internal/netxlite/utls.go +++ b/internal/netxlite/utls.go @@ -3,6 +3,7 @@ package netxlite import ( "context" "crypto/tls" + "errors" "net" utls "gitlab.com/yawning/utls.git" @@ -43,17 +44,29 @@ func newConnUTLS(clientHello *utls.ClientHelloID) func(conn net.Conn, config *tl } } -func (c *utlsConn) HandshakeContext(ctx context.Context) error { +// 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 +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: - return err + case err = <-errch: case <-ctx.Done(): - return ctx.Err() + err = ctx.Err() } + return } func (c *utlsConn) handshakefn() func() error { diff --git a/internal/netxlite/utls_test.go b/internal/netxlite/utls_test.go index d62c24c..12388a1 100644 --- a/internal/netxlite/utls_test.go +++ b/internal/netxlite/utls_test.go @@ -49,7 +49,7 @@ func TestNewTLSHandshakerUTLSTypes(t *testing.T) { } } -func TestUTLSConnHandshakeNotInterrupted(t *testing.T) { +func TestUTLSConnHandshakeNotInterruptedSuccess(t *testing.T) { ctx := context.Background() conn := &utlsConn{ testableHandshake: func() error { @@ -62,6 +62,20 @@ func TestUTLSConnHandshakeNotInterrupted(t *testing.T) { } } +func TestUTLSConnHandshakeNotInterruptedFailure(t *testing.T) { + expected := errors.New("mocked error") + ctx := context.Background() + conn := &utlsConn{ + testableHandshake: func() error { + return expected + }, + } + err := conn.HandshakeContext(ctx) + if !errors.Is(err, expected) { + t.Fatal("not the error we expected", err) + } +} + func TestUTLSConnHandshakeInterrupted(t *testing.T) { wg := sync.WaitGroup{} wg.Add(1) @@ -82,3 +96,20 @@ func TestUTLSConnHandshakeInterrupted(t *testing.T) { close(sigch) wg.Wait() } + +func TestUTLSConnHandshakePanic(t *testing.T) { + wg := sync.WaitGroup{} + wg.Add(1) + ctx := context.Background() + conn := &utlsConn{ + testableHandshake: func() error { + defer wg.Done() + panic("mascetti") + }, + } + err := conn.HandshakeContext(ctx) + if !errors.Is(err, ErrUTLSHandshakePanic) { + t.Fatal("not the error we expected", err) + } + wg.Wait() +}