2021-06-25 17:04:24 +02:00
|
|
|
package netxlite
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"crypto/tls"
|
|
|
|
"errors"
|
|
|
|
"net"
|
|
|
|
"strconv"
|
2021-09-06 22:14:49 +02:00
|
|
|
"sync"
|
2021-06-25 17:04:24 +02:00
|
|
|
|
|
|
|
"github.com/lucas-clemente/quic-go"
|
2022-01-03 13:53:23 +01:00
|
|
|
"github.com/ooni/probe-cli/v3/internal/model"
|
2021-06-25 17:04:24 +02:00
|
|
|
)
|
|
|
|
|
2021-09-06 21:34:14 +02:00
|
|
|
// NewQUICListener creates a new QUICListener using the standard
|
|
|
|
// library to create listening UDP sockets.
|
2022-01-03 13:53:23 +01:00
|
|
|
func NewQUICListener() model.QUICListener {
|
2021-09-07 19:56:42 +02:00
|
|
|
return &quicListenerErrWrapper{&quicListenerStdlib{}}
|
2021-09-06 21:34:14 +02:00
|
|
|
}
|
|
|
|
|
2021-09-05 14:49:38 +02:00
|
|
|
// quicListenerStdlib is a QUICListener using the standard library.
|
|
|
|
type quicListenerStdlib struct{}
|
2021-06-25 17:04:24 +02:00
|
|
|
|
2022-01-03 13:53:23 +01:00
|
|
|
var _ model.QUICListener = &quicListenerStdlib{}
|
2021-06-25 17:04:24 +02:00
|
|
|
|
|
|
|
// Listen implements QUICListener.Listen.
|
2022-01-03 13:53:23 +01:00
|
|
|
func (qls *quicListenerStdlib) Listen(addr *net.UDPAddr) (model.UDPLikeConn, error) {
|
2021-11-02 12:04:40 +01:00
|
|
|
return TProxy.ListenUDP("udp", addr)
|
2021-06-25 17:04:24 +02:00
|
|
|
}
|
|
|
|
|
2021-09-06 21:34:14 +02:00
|
|
|
// NewQUICDialerWithResolver returns a QUICDialer using the given
|
|
|
|
// QUICListener to create listening connections and the given Resolver
|
|
|
|
// to resolve domain names (if needed).
|
2021-09-08 22:48:10 +02:00
|
|
|
//
|
|
|
|
// Properties of the dialer:
|
|
|
|
//
|
|
|
|
// 1. logs events using the given logger;
|
|
|
|
//
|
|
|
|
// 2. resolves domain names using the givern resolver;
|
|
|
|
//
|
|
|
|
// 3. when using a resolver, _may_ attempt multiple dials
|
|
|
|
// in parallel (happy eyeballs) and _may_ return an aggregate
|
|
|
|
// error to the caller;
|
|
|
|
//
|
|
|
|
// 4. wraps errors;
|
|
|
|
//
|
|
|
|
// 5. has a configured connect timeout;
|
|
|
|
//
|
|
|
|
// 6. if a dialer wraps a resolver, the dialer will forward
|
|
|
|
// the CloseIdleConnection call to its resolver (which is
|
2021-09-29 20:21:25 +02:00
|
|
|
// instrumental to manage a DoH resolver connections properly).
|
2022-01-03 13:53:23 +01:00
|
|
|
func NewQUICDialerWithResolver(listener model.QUICListener,
|
|
|
|
logger model.DebugLogger, resolver model.Resolver) model.QUICDialer {
|
2021-09-06 21:34:14 +02:00
|
|
|
return &quicDialerLogger{
|
|
|
|
Dialer: &quicDialerResolver{
|
|
|
|
Dialer: &quicDialerLogger{
|
2021-09-07 19:56:42 +02:00
|
|
|
Dialer: &quicDialerErrWrapper{
|
2022-05-14 16:32:32 +02:00
|
|
|
QUICDialer: &quicDialerHandshakeCompleter{
|
|
|
|
Dialer: &quicDialerQUICGo{
|
|
|
|
QUICListener: listener,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2021-09-06 21:34:14 +02:00
|
|
|
Logger: logger,
|
|
|
|
operationSuffix: "_address",
|
|
|
|
},
|
|
|
|
Resolver: resolver,
|
|
|
|
},
|
|
|
|
Logger: logger,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewQUICDialerWithoutResolver is like NewQUICDialerWithResolver
|
|
|
|
// except that there is no configured resolver. So, if you pass in
|
|
|
|
// an address containing a domain name, the dial will fail with
|
|
|
|
// the ErrNoResolver failure.
|
2022-01-03 13:53:23 +01:00
|
|
|
func NewQUICDialerWithoutResolver(listener model.QUICListener, logger model.DebugLogger) model.QUICDialer {
|
2021-09-06 21:34:14 +02:00
|
|
|
return NewQUICDialerWithResolver(listener, logger, &nullResolver{})
|
|
|
|
}
|
|
|
|
|
2021-09-05 14:49:38 +02:00
|
|
|
// quicDialerQUICGo dials using the lucas-clemente/quic-go library.
|
|
|
|
type quicDialerQUICGo struct {
|
2021-06-25 17:04:24 +02:00
|
|
|
// QUICListener is the underlying QUICListener to use.
|
2022-01-03 13:53:23 +01:00
|
|
|
QUICListener model.QUICListener
|
2021-06-25 20:51:59 +02:00
|
|
|
|
|
|
|
// mockDialEarlyContext allows to mock quic.DialEarlyContext.
|
|
|
|
mockDialEarlyContext func(ctx context.Context, pconn net.PacketConn,
|
|
|
|
remoteAddr net.Addr, host string, tlsConfig *tls.Config,
|
2022-05-06 12:24:03 +02:00
|
|
|
quicConfig *quic.Config) (quic.EarlyConnection, error)
|
2021-06-25 17:04:24 +02:00
|
|
|
}
|
|
|
|
|
2022-01-03 13:53:23 +01:00
|
|
|
var _ model.QUICDialer = &quicDialerQUICGo{}
|
2021-06-25 17:04:24 +02:00
|
|
|
|
2022-05-13 15:32:47 +02:00
|
|
|
// ErrInvalidIP indicates that a string is not a valid IP.
|
|
|
|
var ErrInvalidIP = errors.New("netxlite: invalid IP")
|
2021-06-25 17:04:24 +02:00
|
|
|
|
2022-05-14 17:15:08 +02:00
|
|
|
// ParseUDPAddr maps the string representation of an UDP endpoint to the
|
|
|
|
// corresponding *net.UDPAddr representation.
|
|
|
|
func ParseUDPAddr(address string) (*net.UDPAddr, error) {
|
|
|
|
addr, port, err := net.SplitHostPort(address)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ipAddr := net.ParseIP(addr)
|
|
|
|
if ipAddr == nil {
|
|
|
|
return nil, ErrInvalidIP
|
|
|
|
}
|
|
|
|
dport, err := strconv.Atoi(port)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
udpAddr := &net.UDPAddr{
|
|
|
|
IP: ipAddr,
|
|
|
|
Port: dport,
|
|
|
|
Zone: "",
|
|
|
|
}
|
|
|
|
return udpAddr, nil
|
|
|
|
}
|
|
|
|
|
2021-09-06 20:56:14 +02:00
|
|
|
// DialContext implements QUICDialer.DialContext. This function will
|
2021-06-25 20:51:59 +02:00
|
|
|
// apply the following TLS defaults:
|
|
|
|
//
|
|
|
|
// 1. if tlsConfig.RootCAs is nil, we use the Mozilla CA that we
|
|
|
|
// bundle with this measurement library;
|
|
|
|
//
|
|
|
|
// 2. if tlsConfig.NextProtos is empty _and_ the port is 443 or 8853,
|
|
|
|
// then we configure, respectively, "h3" and "dq".
|
2021-09-05 14:49:38 +02:00
|
|
|
func (d *quicDialerQUICGo) DialContext(ctx context.Context, network string,
|
2021-06-25 17:04:24 +02:00
|
|
|
address string, tlsConfig *tls.Config, quicConfig *quic.Config) (
|
2022-05-06 12:24:03 +02:00
|
|
|
quic.EarlyConnection, error) {
|
2022-05-14 17:15:08 +02:00
|
|
|
udpAddr, err := ParseUDPAddr(address)
|
2021-06-25 17:04:24 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-05-14 17:15:08 +02:00
|
|
|
pconn, err := d.QUICListener.Listen(&net.UDPAddr{IP: net.IPv4zero, Port: 0, Zone: ""})
|
2021-06-25 17:04:24 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-05-14 17:15:08 +02:00
|
|
|
tlsConfig = d.maybeApplyTLSDefaults(tlsConfig, udpAddr.Port)
|
2022-05-06 12:24:03 +02:00
|
|
|
qconn, err := d.dialEarlyContext(
|
2021-06-25 17:04:24 +02:00
|
|
|
ctx, pconn, udpAddr, address, tlsConfig, quicConfig)
|
2021-06-25 17:58:42 +02:00
|
|
|
if err != nil {
|
2021-09-30 18:00:38 +02:00
|
|
|
pconn.Close() // we own it on failure
|
2021-06-25 17:58:42 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
2022-05-06 12:24:03 +02:00
|
|
|
return &quicConnectionOwnsConn{EarlyConnection: qconn, conn: pconn}, nil
|
2021-06-25 17:58:42 +02:00
|
|
|
}
|
|
|
|
|
2021-09-05 14:49:38 +02:00
|
|
|
func (d *quicDialerQUICGo) dialEarlyContext(ctx context.Context,
|
2021-06-25 20:51:59 +02:00
|
|
|
pconn net.PacketConn, remoteAddr net.Addr, address string,
|
2022-05-06 12:24:03 +02:00
|
|
|
tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlyConnection, error) {
|
2021-06-25 20:51:59 +02:00
|
|
|
if d.mockDialEarlyContext != nil {
|
|
|
|
return d.mockDialEarlyContext(
|
|
|
|
ctx, pconn, remoteAddr, address, tlsConfig, quicConfig)
|
|
|
|
}
|
|
|
|
return quic.DialEarlyContext(
|
|
|
|
ctx, pconn, remoteAddr, address, tlsConfig, quicConfig)
|
|
|
|
}
|
|
|
|
|
|
|
|
// maybeApplyTLSDefaults ensures that we're using our certificate pool, if
|
|
|
|
// needed, and that we use a suitable ALPN, if needed, for h3 and dq.
|
2021-09-05 14:49:38 +02:00
|
|
|
func (d *quicDialerQUICGo) maybeApplyTLSDefaults(config *tls.Config, port int) *tls.Config {
|
2021-06-25 20:51:59 +02:00
|
|
|
config = config.Clone()
|
|
|
|
if config.RootCAs == nil {
|
|
|
|
config.RootCAs = defaultCertPool
|
|
|
|
}
|
|
|
|
if len(config.NextProtos) <= 0 {
|
|
|
|
switch port {
|
|
|
|
case 443:
|
|
|
|
config.NextProtos = []string{"h3"}
|
|
|
|
case 8853:
|
|
|
|
// See https://datatracker.ietf.org/doc/html/draft-ietf-dprive-dnsoquic-02#section-10
|
|
|
|
config.NextProtos = []string{"dq"}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
|
2021-09-06 20:56:14 +02:00
|
|
|
// CloseIdleConnections closes idle connections.
|
|
|
|
func (d *quicDialerQUICGo) CloseIdleConnections() {
|
|
|
|
// nothing to do
|
|
|
|
}
|
|
|
|
|
2022-05-14 16:32:32 +02:00
|
|
|
// quicDialerHandshakeCompleter ensures we complete the handshake.
|
|
|
|
type quicDialerHandshakeCompleter struct {
|
|
|
|
Dialer model.QUICDialer
|
|
|
|
}
|
|
|
|
|
|
|
|
// DialContext implements model.QUICDialer.DialContext.
|
|
|
|
func (d *quicDialerHandshakeCompleter) DialContext(
|
|
|
|
ctx context.Context, network, address string,
|
|
|
|
tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlyConnection, error) {
|
|
|
|
conn, err := d.Dialer.DialContext(ctx, network, address, tlsConfig, quicConfig)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
select {
|
|
|
|
case <-conn.HandshakeComplete().Done():
|
|
|
|
return conn, nil
|
|
|
|
case <-ctx.Done():
|
|
|
|
conn.CloseWithError(0, "") // we own the conn
|
|
|
|
return nil, ctx.Err()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// CloseIdleConnections implements model.QUICDialer.CloseIdleConnections.
|
|
|
|
func (d *quicDialerHandshakeCompleter) CloseIdleConnections() {
|
|
|
|
d.Dialer.CloseIdleConnections()
|
|
|
|
}
|
|
|
|
|
2022-05-06 12:24:03 +02:00
|
|
|
// quicConnectionOwnsConn ensures that we close the UDPLikeConn.
|
|
|
|
type quicConnectionOwnsConn struct {
|
|
|
|
// EarlyConnection is the embedded early connection
|
|
|
|
quic.EarlyConnection
|
2021-06-25 17:58:42 +02:00
|
|
|
|
|
|
|
// conn is the connection we own
|
2022-01-03 13:53:23 +01:00
|
|
|
conn model.UDPLikeConn
|
2021-06-25 17:58:42 +02:00
|
|
|
}
|
|
|
|
|
2022-05-06 12:24:03 +02:00
|
|
|
// CloseWithError implements quic.EarlyConnection.CloseWithError.
|
|
|
|
func (qconn *quicConnectionOwnsConn) CloseWithError(
|
2021-06-25 17:58:42 +02:00
|
|
|
code quic.ApplicationErrorCode, reason string) error {
|
2022-05-06 12:24:03 +02:00
|
|
|
err := qconn.EarlyConnection.CloseWithError(code, reason)
|
|
|
|
qconn.conn.Close()
|
2021-06-25 17:58:42 +02:00
|
|
|
return err
|
2021-06-25 17:04:24 +02:00
|
|
|
}
|
2021-06-25 18:38:13 +02:00
|
|
|
|
2021-09-05 14:49:38 +02:00
|
|
|
// quicDialerResolver is a dialer that uses the configured Resolver
|
2021-06-25 18:38:13 +02:00
|
|
|
// to resolve a domain name to IP addrs.
|
2021-09-05 14:49:38 +02:00
|
|
|
type quicDialerResolver struct {
|
2021-09-06 20:56:14 +02:00
|
|
|
// Dialer is the underlying QUICDialer.
|
2022-01-03 13:53:23 +01:00
|
|
|
Dialer model.QUICDialer
|
2021-06-25 18:38:13 +02:00
|
|
|
|
2021-09-06 20:56:14 +02:00
|
|
|
// Resolver is the underlying Resolver.
|
2022-01-03 13:53:23 +01:00
|
|
|
Resolver model.Resolver
|
2021-06-25 18:38:13 +02:00
|
|
|
}
|
|
|
|
|
2022-01-03 13:53:23 +01:00
|
|
|
var _ model.QUICDialer = &quicDialerResolver{}
|
2021-06-26 16:54:02 +02:00
|
|
|
|
2021-09-06 20:56:14 +02:00
|
|
|
// DialContext implements QUICDialer.DialContext. This function
|
2021-06-25 20:51:59 +02:00
|
|
|
// will apply the following TLS defaults:
|
|
|
|
//
|
|
|
|
// 1. if tlsConfig.ServerName is empty, we will use the hostname
|
|
|
|
// contained inside of the `address` endpoint.
|
2021-09-05 14:49:38 +02:00
|
|
|
func (d *quicDialerResolver) DialContext(
|
2021-06-25 18:38:13 +02:00
|
|
|
ctx context.Context, network, address string,
|
2022-05-06 12:24:03 +02:00
|
|
|
tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlyConnection, error) {
|
2021-06-25 18:38:13 +02:00
|
|
|
onlyhost, onlyport, err := net.SplitHostPort(address)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
addrs, err := d.lookupHost(ctx, onlyhost)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-06-25 20:51:59 +02:00
|
|
|
tlsConfig = d.maybeApplyTLSDefaults(tlsConfig, onlyhost)
|
2021-09-08 22:48:10 +02:00
|
|
|
// See TODO(https://github.com/ooni/probe/issues/1779) however
|
|
|
|
// this is less of a problem for QUIC because so far we have been
|
|
|
|
// using it to perform research only (i.e., urlgetter).
|
2021-09-06 20:17:45 +02:00
|
|
|
addrs = quirkSortIPAddrs(addrs)
|
2021-06-25 18:38:13 +02:00
|
|
|
var errorslist []error
|
|
|
|
for _, addr := range addrs {
|
|
|
|
target := net.JoinHostPort(addr, onlyport)
|
2022-05-06 12:24:03 +02:00
|
|
|
qconn, err := d.Dialer.DialContext(
|
2021-06-25 18:38:13 +02:00
|
|
|
ctx, network, target, tlsConfig, quicConfig)
|
|
|
|
if err == nil {
|
2022-05-06 12:24:03 +02:00
|
|
|
return qconn, nil
|
2021-06-25 18:38:13 +02:00
|
|
|
}
|
|
|
|
errorslist = append(errorslist, err)
|
|
|
|
}
|
2021-09-06 20:17:45 +02:00
|
|
|
return nil, quirkReduceErrors(errorslist)
|
2021-06-25 18:38:13 +02:00
|
|
|
}
|
|
|
|
|
2021-06-25 20:51:59 +02:00
|
|
|
// maybeApplyTLSDefaults sets the SNI if it's not already configured.
|
2021-09-05 14:49:38 +02:00
|
|
|
func (d *quicDialerResolver) maybeApplyTLSDefaults(config *tls.Config, host string) *tls.Config {
|
2021-06-25 20:51:59 +02:00
|
|
|
config = config.Clone()
|
|
|
|
if config.ServerName == "" {
|
|
|
|
config.ServerName = host
|
|
|
|
}
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
|
2021-06-25 18:38:13 +02:00
|
|
|
// lookupHost performs a domain name resolution.
|
2021-09-05 14:49:38 +02:00
|
|
|
func (d *quicDialerResolver) lookupHost(ctx context.Context, hostname string) ([]string, error) {
|
2021-06-25 18:38:13 +02:00
|
|
|
if net.ParseIP(hostname) != nil {
|
|
|
|
return []string{hostname}, nil
|
|
|
|
}
|
|
|
|
return d.Resolver.LookupHost(ctx, hostname)
|
|
|
|
}
|
2021-06-26 16:54:02 +02:00
|
|
|
|
2021-09-06 20:56:14 +02:00
|
|
|
// CloseIdleConnections implements QUICDialer.CloseIdleConnections.
|
|
|
|
func (d *quicDialerResolver) CloseIdleConnections() {
|
|
|
|
d.Dialer.CloseIdleConnections()
|
|
|
|
d.Resolver.CloseIdleConnections()
|
|
|
|
}
|
|
|
|
|
2021-09-05 14:49:38 +02:00
|
|
|
// quicDialerLogger is a dialer with logging.
|
|
|
|
type quicDialerLogger struct {
|
2021-06-26 16:54:02 +02:00
|
|
|
// Dialer is the underlying QUIC dialer.
|
2022-01-03 13:53:23 +01:00
|
|
|
Dialer model.QUICDialer
|
2021-06-26 16:54:02 +02:00
|
|
|
|
|
|
|
// Logger is the underlying logger.
|
2022-01-03 13:53:23 +01:00
|
|
|
Logger model.DebugLogger
|
2021-09-06 21:34:14 +02:00
|
|
|
|
|
|
|
// operationSuffix is appended to the operation name.
|
|
|
|
//
|
|
|
|
// We use this suffix to distinguish the output from dialing
|
|
|
|
// with the output from dialing an IP address when we are
|
|
|
|
// using a dialer without resolver, where otherwise both lines
|
|
|
|
// would read something like `dial 8.8.8.8:443...`
|
|
|
|
operationSuffix string
|
2021-06-26 16:54:02 +02:00
|
|
|
}
|
|
|
|
|
2022-01-03 13:53:23 +01:00
|
|
|
var _ model.QUICDialer = &quicDialerLogger{}
|
2021-06-26 16:54:02 +02:00
|
|
|
|
|
|
|
// DialContext implements QUICContextDialer.DialContext.
|
2021-09-05 14:49:38 +02:00
|
|
|
func (d *quicDialerLogger) DialContext(
|
2021-06-26 16:54:02 +02:00
|
|
|
ctx context.Context, network, address string,
|
2022-05-06 12:24:03 +02:00
|
|
|
tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlyConnection, error) {
|
2021-09-06 21:34:14 +02:00
|
|
|
d.Logger.Debugf("quic_dial%s %s/%s...", d.operationSuffix, address, network)
|
2022-05-06 12:24:03 +02:00
|
|
|
qconn, err := d.Dialer.DialContext(ctx, network, address, tlsConfig, quicConfig)
|
2021-06-26 16:54:02 +02:00
|
|
|
if err != nil {
|
2021-09-06 21:34:14 +02:00
|
|
|
d.Logger.Debugf("quic_dial%s %s/%s... %s", d.operationSuffix,
|
|
|
|
address, network, err)
|
2021-06-26 16:54:02 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
2021-09-06 21:34:14 +02:00
|
|
|
d.Logger.Debugf("quic_dial%s %s/%s... ok", d.operationSuffix, address, network)
|
2022-05-06 12:24:03 +02:00
|
|
|
return qconn, nil
|
2021-06-26 16:54:02 +02:00
|
|
|
}
|
2021-09-06 20:56:14 +02:00
|
|
|
|
|
|
|
// CloseIdleConnections implements QUICDialer.CloseIdleConnections.
|
|
|
|
func (d *quicDialerLogger) CloseIdleConnections() {
|
|
|
|
d.Dialer.CloseIdleConnections()
|
|
|
|
}
|
2021-09-06 22:14:49 +02:00
|
|
|
|
2021-09-29 20:21:25 +02:00
|
|
|
// NewSingleUseQUICDialer is like NewSingleUseDialer but for QUIC.
|
2022-05-06 12:24:03 +02:00
|
|
|
func NewSingleUseQUICDialer(qconn quic.EarlyConnection) model.QUICDialer {
|
|
|
|
return &quicDialerSingleUse{qconn: qconn}
|
2021-09-06 22:14:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// quicDialerSingleUse is the QUICDialer returned by NewSingleQUICDialer.
|
|
|
|
type quicDialerSingleUse struct {
|
|
|
|
sync.Mutex
|
2022-05-06 12:24:03 +02:00
|
|
|
qconn quic.EarlyConnection
|
2021-09-06 22:14:49 +02:00
|
|
|
}
|
|
|
|
|
2022-01-03 13:53:23 +01:00
|
|
|
var _ model.QUICDialer = &quicDialerSingleUse{}
|
2021-09-06 22:14:49 +02:00
|
|
|
|
|
|
|
// DialContext implements QUICDialer.DialContext.
|
|
|
|
func (s *quicDialerSingleUse) DialContext(
|
|
|
|
ctx context.Context, network, addr string, tlsCfg *tls.Config,
|
2022-05-06 12:24:03 +02:00
|
|
|
cfg *quic.Config) (quic.EarlyConnection, error) {
|
|
|
|
var qconn quic.EarlyConnection
|
2021-09-06 22:14:49 +02:00
|
|
|
defer s.Unlock()
|
|
|
|
s.Lock()
|
2022-05-06 12:24:03 +02:00
|
|
|
if s.qconn == nil {
|
2021-09-06 22:14:49 +02:00
|
|
|
return nil, ErrNoConnReuse
|
|
|
|
}
|
2022-05-06 12:24:03 +02:00
|
|
|
qconn, s.qconn = s.qconn, nil
|
|
|
|
return qconn, nil
|
2021-09-06 22:14:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// CloseIdleConnections closes idle connections.
|
|
|
|
func (s *quicDialerSingleUse) CloseIdleConnections() {
|
|
|
|
// nothing to do
|
|
|
|
}
|
2021-09-07 19:56:42 +02:00
|
|
|
|
|
|
|
// quicListenerErrWrapper is a QUICListener that wraps errors.
|
|
|
|
type quicListenerErrWrapper struct {
|
|
|
|
// QUICListener is the underlying listener.
|
2022-01-03 13:53:23 +01:00
|
|
|
model.QUICListener
|
2021-09-07 19:56:42 +02:00
|
|
|
}
|
|
|
|
|
2022-01-03 13:53:23 +01:00
|
|
|
var _ model.QUICListener = &quicListenerErrWrapper{}
|
2021-09-07 19:56:42 +02:00
|
|
|
|
|
|
|
// Listen implements QUICListener.Listen.
|
2022-01-03 13:53:23 +01:00
|
|
|
func (qls *quicListenerErrWrapper) Listen(addr *net.UDPAddr) (model.UDPLikeConn, error) {
|
2021-09-07 19:56:42 +02:00
|
|
|
pconn, err := qls.QUICListener.Listen(addr)
|
|
|
|
if err != nil {
|
2022-01-07 17:31:21 +01:00
|
|
|
return nil, NewErrWrapper(classifyGenericError, QUICListenOperation, err)
|
2021-09-07 19:56:42 +02:00
|
|
|
}
|
|
|
|
return &quicErrWrapperUDPLikeConn{pconn}, nil
|
|
|
|
}
|
|
|
|
|
2021-09-29 20:21:25 +02:00
|
|
|
// quicErrWrapperUDPLikeConn is a UDPLikeConn that wraps errors.
|
2021-09-07 19:56:42 +02:00
|
|
|
type quicErrWrapperUDPLikeConn struct {
|
|
|
|
// UDPLikeConn is the underlying conn.
|
2022-01-03 13:53:23 +01:00
|
|
|
model.UDPLikeConn
|
2021-09-07 19:56:42 +02:00
|
|
|
}
|
|
|
|
|
2022-01-03 13:53:23 +01:00
|
|
|
var _ model.UDPLikeConn = &quicErrWrapperUDPLikeConn{}
|
2021-09-07 19:56:42 +02:00
|
|
|
|
2021-09-29 20:21:25 +02:00
|
|
|
// WriteTo implements UDPLikeConn.WriteTo.
|
2021-09-07 19:56:42 +02:00
|
|
|
func (c *quicErrWrapperUDPLikeConn) WriteTo(p []byte, addr net.Addr) (int, error) {
|
|
|
|
count, err := c.UDPLikeConn.WriteTo(p, addr)
|
|
|
|
if err != nil {
|
2022-01-07 17:31:21 +01:00
|
|
|
return 0, NewErrWrapper(classifyGenericError, WriteToOperation, err)
|
2021-09-07 19:56:42 +02:00
|
|
|
}
|
|
|
|
return count, nil
|
|
|
|
}
|
|
|
|
|
2021-09-29 20:21:25 +02:00
|
|
|
// ReadFrom implements UDPLikeConn.ReadFrom.
|
2021-09-07 19:56:42 +02:00
|
|
|
func (c *quicErrWrapperUDPLikeConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
|
|
|
n, addr, err := c.UDPLikeConn.ReadFrom(b)
|
|
|
|
if err != nil {
|
2022-01-07 17:31:21 +01:00
|
|
|
return 0, nil, NewErrWrapper(classifyGenericError, ReadFromOperation, err)
|
2021-09-07 19:56:42 +02:00
|
|
|
}
|
|
|
|
return n, addr, nil
|
|
|
|
}
|
|
|
|
|
2021-09-29 20:21:25 +02:00
|
|
|
// Close implements UDPLikeConn.Close.
|
2021-09-27 14:14:17 +02:00
|
|
|
func (c *quicErrWrapperUDPLikeConn) Close() error {
|
|
|
|
err := c.UDPLikeConn.Close()
|
|
|
|
if err != nil {
|
2022-01-07 17:31:21 +01:00
|
|
|
return NewErrWrapper(classifyGenericError, ReadFromOperation, err)
|
2021-09-27 14:14:17 +02:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-09-07 19:56:42 +02:00
|
|
|
// quicDialerErrWrapper is a dialer that performs quic err wrapping
|
|
|
|
type quicDialerErrWrapper struct {
|
2022-01-03 13:53:23 +01:00
|
|
|
model.QUICDialer
|
2021-09-07 19:56:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// DialContext implements ContextDialer.DialContext
|
|
|
|
func (d *quicDialerErrWrapper) DialContext(
|
|
|
|
ctx context.Context, network string, host string,
|
2022-05-06 12:24:03 +02:00
|
|
|
tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
|
|
|
|
qconn, err := d.QUICDialer.DialContext(ctx, network, host, tlsCfg, cfg)
|
2021-09-07 19:56:42 +02:00
|
|
|
if err != nil {
|
2021-09-28 12:42:01 +02:00
|
|
|
return nil, NewErrWrapper(
|
2022-01-07 17:31:21 +01:00
|
|
|
classifyQUICHandshakeError, QUICHandshakeOperation, err)
|
2021-09-07 19:56:42 +02:00
|
|
|
}
|
2022-05-06 12:24:03 +02:00
|
|
|
return qconn, nil
|
2021-09-07 19:56:42 +02:00
|
|
|
}
|