ooni-probe-cli/internal/engine/netx/tlsdialer/tls.go
Simone Basso c74c94d616
cleanup: remove ConnID, DialID, TransactionID (#395)
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).
2021-06-23 13:36:45 +02:00

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
}