ooni-probe-cli/internal/netxlite/utls_test.go
Simone Basso cc24f28b9d
feat(netxlite): support extracting the CNAME (#875)
* feat(netxlite): support extracting the CNAME

Closes https://github.com/ooni/probe/issues/2225

* fix(netxlite): attempt to increase coverage and improve tests

1. dnsovergetaddrinfo: specify the behavior of a DNSResponse returned
by this file to make it line with normal responses and write unit tests
to make sure we adhere to expectations;

2. dnsoverudp: make sure we wait to deferred responses also w/o a
custom context and post on a private channel and test that;

3. utls: recognize that we can actually write a test for NetConn and
what needs to change when we'll use go1.19 by default will just be
a cast that at that point can be removed.
2022-08-23 13:04:00 +02:00

162 lines
3.9 KiB
Go

package netxlite
import (
"context"
"crypto/tls"
"errors"
"net"
"sync"
"testing"
"time"
"github.com/apex/log"
"github.com/ooni/probe-cli/v3/internal/model/mocks"
utls "gitlab.com/yawning/utls.git"
)
func TestNewTLSHandshakerUTLS(t *testing.T) {
th := NewTLSHandshakerUTLS(log.Log, &utls.HelloChrome_83)
logger := th.(*tlsHandshakerLogger)
if logger.DebugLogger != log.Log {
t.Fatal("invalid logger")
}
configurable := logger.TLSHandshaker.(*tlsHandshakerConfigurable)
if configurable.NewConn == nil {
t.Fatal("expected non-nil NewConn")
}
}
func TestUTLSConn(t *testing.T) {
t.Run("Handshake", func(t *testing.T) {
t.Run("not interrupted with success", func(t *testing.T) {
ctx := context.Background()
conn := &utlsConn{
testableHandshake: func() error {
return nil
},
}
err := conn.HandshakeContext(ctx)
if err != nil {
t.Fatal(err)
}
})
t.Run("not interrupted with failure", func(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)
}
})
t.Run("interrupted", func(t *testing.T) {
wg := sync.WaitGroup{}
wg.Add(1)
sigch := make(chan interface{})
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
defer cancel()
conn := &utlsConn{
testableHandshake: func() error {
defer wg.Done()
<-sigch
return nil
},
}
err := conn.HandshakeContext(ctx)
if !errors.Is(err, context.DeadlineExceeded) {
t.Fatal("not the error we expected", err)
}
close(sigch)
wg.Wait()
})
t.Run("with panic", func(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()
})
})
t.Run("NetConn", func(t *testing.T) {
factory := newConnUTLS(&utls.HelloChrome_70)
conn := &mocks.Conn{}
tconn, err := factory(conn, &tls.Config{})
if err != nil {
t.Fatal(err)
}
// TODO(https://github.com/ooni/probe/issues/2222): we cannot avoid
// castedTconn until we use oocrypto >= v0.2 which uses go1.19. In turn,
// we cannot use go1.19 as our main version until we upgrade psiphon
// such that it builds using go1.19, which is the issue in #2222.
castedTconn := tconn.(*utlsConn)
if castedTconn.NetConn() != conn {
t.Fatal("NetConn is not WAI")
}
})
}
func Test_newConnUTLSWithHelloID(t *testing.T) {
tests := []struct {
name string
config *tls.Config
cid *utls.ClientHelloID
wantNilConn bool
wantErr error
}{{
name: "with only supported fields",
config: &tls.Config{
DynamicRecordSizingDisabled: true,
InsecureSkipVerify: true,
NextProtos: []string{"h3"},
RootCAs: NewDefaultCertPool(),
ServerName: "ooni.org",
},
cid: &utls.HelloFirefox_55,
wantNilConn: false,
wantErr: nil,
}, {
name: "with unsupported fields",
config: &tls.Config{
Time: func() time.Time {
return time.Now()
},
},
cid: &utls.HelloChrome_58,
wantNilConn: true,
wantErr: errUTLSIncompatibleStdlibConfig,
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
conn, err := net.Dial("udp", "8.8.8.8:443") // we just need a conn
if err != nil {
t.Fatal(err)
}
defer conn.Close()
got, err := newConnUTLSWithHelloID(conn, tt.config, tt.cid)
if !errors.Is(err, tt.wantErr) {
t.Fatal("unexpected err", err)
}
if got != nil && tt.wantNilConn {
t.Fatal("expected nil conn here")
}
})
}
}