c74c94d616
We are not using them anymore. The only nettest still using the legacy netx implementation is tor, for which setting these fields is useless, because it performs each measurement into a separate goroutine. Hence, let us start removing this part of the legacy netx codebase, which is hampering progress in other areas. Occurred to me while doing testing for the recent changes in error mapping (https://github.com/ooni/probe/issues/1505).
143 lines
4.2 KiB
Go
143 lines
4.2 KiB
Go
// Package tlsdialer contains code to establish TLS connections.
|
|
package tlsdialer
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/engine/legacy/netx/modelx"
|
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/errorx"
|
|
)
|
|
|
|
// UnderlyingDialer is the underlying dialer type.
|
|
type UnderlyingDialer interface {
|
|
DialContext(ctx context.Context, network, address string) (net.Conn, error)
|
|
}
|
|
|
|
// TLSHandshaker is the generic TLS handshaker
|
|
type TLSHandshaker interface {
|
|
Handshake(ctx context.Context, conn net.Conn, config *tls.Config) (
|
|
net.Conn, tls.ConnectionState, error)
|
|
}
|
|
|
|
// SystemTLSHandshaker is the system TLS handshaker.
|
|
type SystemTLSHandshaker struct{}
|
|
|
|
// Handshake implements Handshaker.Handshake
|
|
func (h SystemTLSHandshaker) Handshake(
|
|
ctx context.Context, conn net.Conn, config *tls.Config,
|
|
) (net.Conn, tls.ConnectionState, error) {
|
|
tlsconn := tls.Client(conn, config)
|
|
if err := tlsconn.Handshake(); err != nil {
|
|
return nil, tls.ConnectionState{}, err
|
|
}
|
|
return tlsconn, tlsconn.ConnectionState(), nil
|
|
}
|
|
|
|
// TimeoutTLSHandshaker is a TLSHandshaker with timeout
|
|
type TimeoutTLSHandshaker struct {
|
|
TLSHandshaker
|
|
HandshakeTimeout time.Duration // default: 10 second
|
|
}
|
|
|
|
// Handshake implements Handshaker.Handshake
|
|
func (h TimeoutTLSHandshaker) Handshake(
|
|
ctx context.Context, conn net.Conn, config *tls.Config,
|
|
) (net.Conn, tls.ConnectionState, error) {
|
|
timeout := 10 * time.Second
|
|
if h.HandshakeTimeout != 0 {
|
|
timeout = h.HandshakeTimeout
|
|
}
|
|
if err := conn.SetDeadline(time.Now().Add(timeout)); err != nil {
|
|
return nil, tls.ConnectionState{}, err
|
|
}
|
|
tlsconn, connstate, err := h.TLSHandshaker.Handshake(ctx, conn, config)
|
|
conn.SetDeadline(time.Time{})
|
|
return tlsconn, connstate, err
|
|
}
|
|
|
|
// ErrorWrapperTLSHandshaker wraps the returned error to be an OONI error
|
|
type ErrorWrapperTLSHandshaker struct {
|
|
TLSHandshaker
|
|
}
|
|
|
|
// Handshake implements Handshaker.Handshake
|
|
func (h ErrorWrapperTLSHandshaker) Handshake(
|
|
ctx context.Context, conn net.Conn, config *tls.Config,
|
|
) (net.Conn, tls.ConnectionState, error) {
|
|
tlsconn, state, err := h.TLSHandshaker.Handshake(ctx, conn, config)
|
|
err = errorx.SafeErrWrapperBuilder{
|
|
Classifier: errorx.ClassifyTLSFailure,
|
|
Error: err,
|
|
Operation: errorx.TLSHandshakeOperation,
|
|
}.MaybeBuild()
|
|
return tlsconn, state, err
|
|
}
|
|
|
|
// EmitterTLSHandshaker emits events using the MeasurementRoot
|
|
type EmitterTLSHandshaker struct {
|
|
TLSHandshaker
|
|
}
|
|
|
|
// Handshake implements Handshaker.Handshake
|
|
func (h EmitterTLSHandshaker) Handshake(
|
|
ctx context.Context, conn net.Conn, config *tls.Config,
|
|
) (net.Conn, tls.ConnectionState, error) {
|
|
root := modelx.ContextMeasurementRootOrDefault(ctx)
|
|
root.Handler.OnMeasurement(modelx.Measurement{
|
|
TLSHandshakeStart: &modelx.TLSHandshakeStartEvent{
|
|
DurationSinceBeginning: time.Since(root.Beginning),
|
|
SNI: config.ServerName,
|
|
},
|
|
})
|
|
tlsconn, state, err := h.TLSHandshaker.Handshake(ctx, conn, config)
|
|
root.Handler.OnMeasurement(modelx.Measurement{
|
|
TLSHandshakeDone: &modelx.TLSHandshakeDoneEvent{
|
|
ConnectionState: modelx.NewTLSConnectionState(state),
|
|
Error: err,
|
|
DurationSinceBeginning: time.Since(root.Beginning),
|
|
},
|
|
})
|
|
return tlsconn, state, err
|
|
}
|
|
|
|
// TLSDialer is the TLS dialer
|
|
type TLSDialer struct {
|
|
Config *tls.Config
|
|
Dialer UnderlyingDialer
|
|
TLSHandshaker TLSHandshaker
|
|
}
|
|
|
|
// DialTLSContext is like tls.DialTLS but with the signature of net.Dialer.DialContext
|
|
func (d TLSDialer) DialTLSContext(ctx context.Context, network, address string) (net.Conn, error) {
|
|
// Implementation note: when DialTLS is not set, the code in
|
|
// net/http will perform the handshake. Otherwise, if DialTLS
|
|
// is set, we will end up here. This code is still used when
|
|
// performing non-HTTP TLS-enabled dial operations.
|
|
host, _, err := net.SplitHostPort(address)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conn, err := d.Dialer.DialContext(ctx, network, address)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
config := d.Config
|
|
if config == nil {
|
|
config = new(tls.Config)
|
|
} else {
|
|
config = config.Clone()
|
|
}
|
|
if config.ServerName == "" {
|
|
config.ServerName = host
|
|
}
|
|
tlsconn, _, err := d.TLSHandshaker.Handshake(ctx, conn, config)
|
|
if err != nil {
|
|
conn.Close()
|
|
return nil, err
|
|
}
|
|
return tlsconn, nil
|
|
}
|