github.com/quic-go/quic-go@v0.44.0/transport.go (about)

     1  package quic
     2  
     3  import (
     4  	"context"
     5  	"crypto/rand"
     6  	"crypto/tls"
     7  	"errors"
     8  	"net"
     9  	"sync"
    10  	"sync/atomic"
    11  	"time"
    12  
    13  	"github.com/quic-go/quic-go/internal/protocol"
    14  	"github.com/quic-go/quic-go/internal/utils"
    15  	"github.com/quic-go/quic-go/internal/wire"
    16  	"github.com/quic-go/quic-go/logging"
    17  )
    18  
    19  var errListenerAlreadySet = errors.New("listener already set")
    20  
    21  // The Transport is the central point to manage incoming and outgoing QUIC connections.
    22  // QUIC demultiplexes connections based on their QUIC Connection IDs, not based on the 4-tuple.
    23  // This means that a single UDP socket can be used for listening for incoming connections, as well as
    24  // for dialing an arbitrary number of outgoing connections.
    25  // A Transport handles a single net.PacketConn, and offers a range of configuration options
    26  // compared to the simple helper functions like Listen and Dial that this package provides.
    27  type Transport struct {
    28  	// A single net.PacketConn can only be handled by one Transport.
    29  	// Bad things will happen if passed to multiple Transports.
    30  	//
    31  	// A number of optimizations will be enabled if the connections implements the OOBCapablePacketConn interface,
    32  	// as a *net.UDPConn does.
    33  	// 1. It enables the Don't Fragment (DF) bit on the IP header.
    34  	//    This is required to run DPLPMTUD (Path MTU Discovery, RFC 8899).
    35  	// 2. It enables reading of the ECN bits from the IP header.
    36  	//    This allows the remote node to speed up its loss detection and recovery.
    37  	// 3. It uses batched syscalls (recvmmsg) to more efficiently receive packets from the socket.
    38  	// 4. It uses Generic Segmentation Offload (GSO) to efficiently send batches of packets (on Linux).
    39  	//
    40  	// After passing the connection to the Transport, it's invalid to call ReadFrom or WriteTo on the connection.
    41  	Conn net.PacketConn
    42  
    43  	// The length of the connection ID in bytes.
    44  	// It can be any value between 1 and 20.
    45  	// Due to the increased risk of collisions, it is not recommended to use connection IDs shorter than 4 bytes.
    46  	// If unset, a 4 byte connection ID will be used.
    47  	ConnectionIDLength int
    48  
    49  	// Use for generating new connection IDs.
    50  	// This allows the application to control of the connection IDs used,
    51  	// which allows routing / load balancing based on connection IDs.
    52  	// All Connection IDs returned by the ConnectionIDGenerator MUST
    53  	// have the same length.
    54  	ConnectionIDGenerator ConnectionIDGenerator
    55  
    56  	// The StatelessResetKey is used to generate stateless reset tokens.
    57  	// If no key is configured, sending of stateless resets is disabled.
    58  	// It is highly recommended to configure a stateless reset key, as stateless resets
    59  	// allow the peer to quickly recover from crashes and reboots of this node.
    60  	// See section 10.3 of RFC 9000 for details.
    61  	StatelessResetKey *StatelessResetKey
    62  
    63  	// The TokenGeneratorKey is used to encrypt session resumption tokens.
    64  	// If no key is configured, a random key will be generated.
    65  	// If multiple servers are authoritative for the same domain, they should use the same key,
    66  	// see section 8.1.3 of RFC 9000 for details.
    67  	TokenGeneratorKey *TokenGeneratorKey
    68  
    69  	// MaxTokenAge is the maximum age of the resumption token presented during the handshake.
    70  	// These tokens allow skipping address resumption when resuming a QUIC connection,
    71  	// and are especially useful when using 0-RTT.
    72  	// If not set, it defaults to 24 hours.
    73  	// See section 8.1.3 of RFC 9000 for details.
    74  	MaxTokenAge time.Duration
    75  
    76  	// DisableVersionNegotiationPackets disables the sending of Version Negotiation packets.
    77  	// This can be useful if version information is exchanged out-of-band.
    78  	// It has no effect for clients.
    79  	DisableVersionNegotiationPackets bool
    80  
    81  	// VerifySourceAddress decides if a connection attempt originating from unvalidated source
    82  	// addresses first needs to go through source address validation using QUIC's Retry mechanism,
    83  	// as described in RFC 9000 section 8.1.2.
    84  	// Note that the address passed to this callback is unvalidated, and might be spoofed in case
    85  	// of an attack.
    86  	// Validating the source address adds one additional network roundtrip to the handshake,
    87  	// and should therefore only be used if a suspiciously high number of incoming connection is recorded.
    88  	// For most use cases, wrapping the Allow function of a rate.Limiter will be a reasonable
    89  	// implementation of this callback (negating its return value).
    90  	VerifySourceAddress func(net.Addr) bool
    91  
    92  	// A Tracer traces events that don't belong to a single QUIC connection.
    93  	// Tracer.Close is called when the transport is closed.
    94  	Tracer *logging.Tracer
    95  
    96  	handlerMap packetHandlerManager
    97  
    98  	mutex    sync.Mutex
    99  	initOnce sync.Once
   100  	initErr  error
   101  
   102  	// Set in init.
   103  	// If no ConnectionIDGenerator is set, this is the ConnectionIDLength.
   104  	connIDLen int
   105  	// Set in init.
   106  	// If no ConnectionIDGenerator is set, this is set to a default.
   107  	connIDGenerator ConnectionIDGenerator
   108  
   109  	server *baseServer
   110  
   111  	conn rawConn
   112  
   113  	closeQueue          chan closePacket
   114  	statelessResetQueue chan receivedPacket
   115  
   116  	listening   chan struct{} // is closed when listen returns
   117  	closed      bool
   118  	createdConn bool
   119  	isSingleUse bool // was created for a single server or client, i.e. by calling quic.Listen or quic.Dial
   120  
   121  	readingNonQUICPackets atomic.Bool
   122  	nonQUICPackets        chan receivedPacket
   123  
   124  	logger utils.Logger
   125  }
   126  
   127  // Listen starts listening for incoming QUIC connections.
   128  // There can only be a single listener on any net.PacketConn.
   129  // Listen may only be called again after the current Listener was closed.
   130  func (t *Transport) Listen(tlsConf *tls.Config, conf *Config) (*Listener, error) {
   131  	s, err := t.createServer(tlsConf, conf, false)
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  	return &Listener{baseServer: s}, nil
   136  }
   137  
   138  // ListenEarly starts listening for incoming QUIC connections.
   139  // There can only be a single listener on any net.PacketConn.
   140  // Listen may only be called again after the current Listener was closed.
   141  func (t *Transport) ListenEarly(tlsConf *tls.Config, conf *Config) (*EarlyListener, error) {
   142  	s, err := t.createServer(tlsConf, conf, true)
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  	return &EarlyListener{baseServer: s}, nil
   147  }
   148  
   149  func (t *Transport) createServer(tlsConf *tls.Config, conf *Config, allow0RTT bool) (*baseServer, error) {
   150  	if tlsConf == nil {
   151  		return nil, errors.New("quic: tls.Config not set")
   152  	}
   153  	if err := validateConfig(conf); err != nil {
   154  		return nil, err
   155  	}
   156  
   157  	t.mutex.Lock()
   158  	defer t.mutex.Unlock()
   159  
   160  	if t.server != nil {
   161  		return nil, errListenerAlreadySet
   162  	}
   163  	conf = populateConfig(conf)
   164  	if err := t.init(false); err != nil {
   165  		return nil, err
   166  	}
   167  	s := newServer(
   168  		t.conn,
   169  		t.handlerMap,
   170  		t.connIDGenerator,
   171  		tlsConf,
   172  		conf,
   173  		t.Tracer,
   174  		t.closeServer,
   175  		*t.TokenGeneratorKey,
   176  		t.MaxTokenAge,
   177  		t.VerifySourceAddress,
   178  		t.DisableVersionNegotiationPackets,
   179  		allow0RTT,
   180  	)
   181  	t.server = s
   182  	return s, nil
   183  }
   184  
   185  // Dial dials a new connection to a remote host (not using 0-RTT).
   186  func (t *Transport) Dial(ctx context.Context, addr net.Addr, tlsConf *tls.Config, conf *Config) (Connection, error) {
   187  	return t.dial(ctx, addr, "", tlsConf, conf, false)
   188  }
   189  
   190  // DialEarly dials a new connection, attempting to use 0-RTT if possible.
   191  func (t *Transport) DialEarly(ctx context.Context, addr net.Addr, tlsConf *tls.Config, conf *Config) (EarlyConnection, error) {
   192  	return t.dial(ctx, addr, "", tlsConf, conf, true)
   193  }
   194  
   195  func (t *Transport) dial(ctx context.Context, addr net.Addr, host string, tlsConf *tls.Config, conf *Config, use0RTT bool) (EarlyConnection, error) {
   196  	if err := validateConfig(conf); err != nil {
   197  		return nil, err
   198  	}
   199  	conf = populateConfig(conf)
   200  	if err := t.init(t.isSingleUse); err != nil {
   201  		return nil, err
   202  	}
   203  	var onClose func()
   204  	if t.isSingleUse {
   205  		onClose = func() { t.Close() }
   206  	}
   207  	tlsConf = tlsConf.Clone()
   208  	setTLSConfigServerName(tlsConf, addr, host)
   209  	return dial(ctx, newSendConn(t.conn, addr, packetInfo{}, utils.DefaultLogger), t.connIDGenerator, t.handlerMap, tlsConf, conf, onClose, use0RTT)
   210  }
   211  
   212  func (t *Transport) init(allowZeroLengthConnIDs bool) error {
   213  	t.initOnce.Do(func() {
   214  		var conn rawConn
   215  		if c, ok := t.Conn.(rawConn); ok {
   216  			conn = c
   217  		} else {
   218  			var err error
   219  			conn, err = wrapConn(t.Conn)
   220  			if err != nil {
   221  				t.initErr = err
   222  				return
   223  			}
   224  		}
   225  
   226  		t.logger = utils.DefaultLogger // TODO: make this configurable
   227  		t.conn = conn
   228  		t.handlerMap = newPacketHandlerMap(t.StatelessResetKey, t.enqueueClosePacket, t.logger)
   229  		t.listening = make(chan struct{})
   230  
   231  		t.closeQueue = make(chan closePacket, 4)
   232  		t.statelessResetQueue = make(chan receivedPacket, 4)
   233  		if t.TokenGeneratorKey == nil {
   234  			var key TokenGeneratorKey
   235  			if _, err := rand.Read(key[:]); err != nil {
   236  				t.initErr = err
   237  				return
   238  			}
   239  			t.TokenGeneratorKey = &key
   240  		}
   241  
   242  		if t.ConnectionIDGenerator != nil {
   243  			t.connIDGenerator = t.ConnectionIDGenerator
   244  			t.connIDLen = t.ConnectionIDGenerator.ConnectionIDLen()
   245  		} else {
   246  			connIDLen := t.ConnectionIDLength
   247  			if t.ConnectionIDLength == 0 && !allowZeroLengthConnIDs {
   248  				connIDLen = protocol.DefaultConnectionIDLength
   249  			}
   250  			t.connIDLen = connIDLen
   251  			t.connIDGenerator = &protocol.DefaultConnectionIDGenerator{ConnLen: t.connIDLen}
   252  		}
   253  
   254  		getMultiplexer().AddConn(t.Conn)
   255  		go t.listen(conn)
   256  		go t.runSendQueue()
   257  	})
   258  	return t.initErr
   259  }
   260  
   261  // WriteTo sends a packet on the underlying connection.
   262  func (t *Transport) WriteTo(b []byte, addr net.Addr) (int, error) {
   263  	if err := t.init(false); err != nil {
   264  		return 0, err
   265  	}
   266  	return t.conn.WritePacket(b, addr, nil, 0, protocol.ECNUnsupported)
   267  }
   268  
   269  func (t *Transport) enqueueClosePacket(p closePacket) {
   270  	select {
   271  	case t.closeQueue <- p:
   272  	default:
   273  		// Oops, we're backlogged.
   274  		// Just drop the packet, sending CONNECTION_CLOSE copies is best effort anyway.
   275  	}
   276  }
   277  
   278  func (t *Transport) runSendQueue() {
   279  	for {
   280  		select {
   281  		case <-t.listening:
   282  			return
   283  		case p := <-t.closeQueue:
   284  			t.conn.WritePacket(p.payload, p.addr, p.info.OOB(), 0, protocol.ECNUnsupported)
   285  		case p := <-t.statelessResetQueue:
   286  			t.sendStatelessReset(p)
   287  		}
   288  	}
   289  }
   290  
   291  // Close closes the underlying connection.
   292  // If any listener was started, it will be closed as well.
   293  // It is invalid to start new listeners or connections after that.
   294  func (t *Transport) Close() error {
   295  	t.close(errors.New("closing"))
   296  	if t.createdConn {
   297  		if err := t.Conn.Close(); err != nil {
   298  			return err
   299  		}
   300  	} else if t.conn != nil {
   301  		t.conn.SetReadDeadline(time.Now())
   302  		defer func() { t.conn.SetReadDeadline(time.Time{}) }()
   303  	}
   304  	if t.listening != nil {
   305  		<-t.listening // wait until listening returns
   306  	}
   307  	return nil
   308  }
   309  
   310  func (t *Transport) closeServer() {
   311  	t.mutex.Lock()
   312  	t.server = nil
   313  	if t.isSingleUse {
   314  		t.closed = true
   315  	}
   316  	t.mutex.Unlock()
   317  	if t.createdConn {
   318  		t.Conn.Close()
   319  	}
   320  	if t.isSingleUse {
   321  		t.conn.SetReadDeadline(time.Now())
   322  		defer func() { t.conn.SetReadDeadline(time.Time{}) }()
   323  		<-t.listening // wait until listening returns
   324  	}
   325  }
   326  
   327  func (t *Transport) close(e error) {
   328  	t.mutex.Lock()
   329  	defer t.mutex.Unlock()
   330  	if t.closed {
   331  		return
   332  	}
   333  
   334  	if t.handlerMap != nil {
   335  		t.handlerMap.Close(e)
   336  	}
   337  	if t.server != nil {
   338  		t.server.close(e, false)
   339  	}
   340  	if t.Tracer != nil && t.Tracer.Close != nil {
   341  		t.Tracer.Close()
   342  	}
   343  	t.closed = true
   344  }
   345  
   346  // only print warnings about the UDP receive buffer size once
   347  var setBufferWarningOnce sync.Once
   348  
   349  func (t *Transport) listen(conn rawConn) {
   350  	defer close(t.listening)
   351  	defer getMultiplexer().RemoveConn(t.Conn)
   352  
   353  	for {
   354  		p, err := conn.ReadPacket()
   355  		//nolint:staticcheck // SA1019 ignore this!
   356  		// TODO: This code is used to ignore wsa errors on Windows.
   357  		// Since net.Error.Temporary is deprecated as of Go 1.18, we should find a better solution.
   358  		// See https://github.com/quic-go/quic-go/issues/1737 for details.
   359  		if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
   360  			t.mutex.Lock()
   361  			closed := t.closed
   362  			t.mutex.Unlock()
   363  			if closed {
   364  				return
   365  			}
   366  			t.logger.Debugf("Temporary error reading from conn: %w", err)
   367  			continue
   368  		}
   369  		if err != nil {
   370  			// Windows returns an error when receiving a UDP datagram that doesn't fit into the provided buffer.
   371  			if isRecvMsgSizeErr(err) {
   372  				continue
   373  			}
   374  			t.close(err)
   375  			return
   376  		}
   377  		t.handlePacket(p)
   378  	}
   379  }
   380  
   381  func (t *Transport) handlePacket(p receivedPacket) {
   382  	if len(p.data) == 0 {
   383  		return
   384  	}
   385  	if !wire.IsPotentialQUICPacket(p.data[0]) && !wire.IsLongHeaderPacket(p.data[0]) {
   386  		t.handleNonQUICPacket(p)
   387  		return
   388  	}
   389  	connID, err := wire.ParseConnectionID(p.data, t.connIDLen)
   390  	if err != nil {
   391  		t.logger.Debugf("error parsing connection ID on packet from %s: %s", p.remoteAddr, err)
   392  		if t.Tracer != nil && t.Tracer.DroppedPacket != nil {
   393  			t.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError)
   394  		}
   395  		p.buffer.MaybeRelease()
   396  		return
   397  	}
   398  
   399  	// If there's a connection associated with the connection ID, pass the packet there.
   400  	if handler, ok := t.handlerMap.Get(connID); ok {
   401  		handler.handlePacket(p)
   402  		return
   403  	}
   404  	// RFC 9000 section 10.3.1 requires that the stateless reset detection logic is run for both
   405  	// packets that cannot be associated with any connections, and for packets that can't be decrypted.
   406  	// We deviate from the RFC and ignore the latter: If a packet's connection ID is associated with an
   407  	// existing connection, it is dropped there if if it can't be decrypted.
   408  	// Stateless resets use random connection IDs, and at reasonable connection ID lengths collisions are
   409  	// exceedingly rare. In the unlikely event that a stateless reset is misrouted to an existing connection,
   410  	// it is to be expected that the next stateless reset will be correctly detected.
   411  	if isStatelessReset := t.maybeHandleStatelessReset(p.data); isStatelessReset {
   412  		return
   413  	}
   414  	if !wire.IsLongHeaderPacket(p.data[0]) {
   415  		t.maybeSendStatelessReset(p)
   416  		return
   417  	}
   418  
   419  	t.mutex.Lock()
   420  	defer t.mutex.Unlock()
   421  	if t.server == nil { // no server set
   422  		t.logger.Debugf("received a packet with an unexpected connection ID %s", connID)
   423  		return
   424  	}
   425  	t.server.handlePacket(p)
   426  }
   427  
   428  func (t *Transport) maybeSendStatelessReset(p receivedPacket) {
   429  	if t.StatelessResetKey == nil {
   430  		p.buffer.Release()
   431  		return
   432  	}
   433  
   434  	// Don't send a stateless reset in response to very small packets.
   435  	// This includes packets that could be stateless resets.
   436  	if len(p.data) <= protocol.MinStatelessResetSize {
   437  		p.buffer.Release()
   438  		return
   439  	}
   440  
   441  	select {
   442  	case t.statelessResetQueue <- p:
   443  	default:
   444  		// it's fine to not send a stateless reset when we're busy
   445  		p.buffer.Release()
   446  	}
   447  }
   448  
   449  func (t *Transport) sendStatelessReset(p receivedPacket) {
   450  	defer p.buffer.Release()
   451  
   452  	connID, err := wire.ParseConnectionID(p.data, t.connIDLen)
   453  	if err != nil {
   454  		t.logger.Errorf("error parsing connection ID on packet from %s: %s", p.remoteAddr, err)
   455  		return
   456  	}
   457  	token := t.handlerMap.GetStatelessResetToken(connID)
   458  	t.logger.Debugf("Sending stateless reset to %s (connection ID: %s). Token: %#x", p.remoteAddr, connID, token)
   459  	data := make([]byte, protocol.MinStatelessResetSize-16, protocol.MinStatelessResetSize)
   460  	rand.Read(data)
   461  	data[0] = (data[0] & 0x7f) | 0x40
   462  	data = append(data, token[:]...)
   463  	if _, err := t.conn.WritePacket(data, p.remoteAddr, p.info.OOB(), 0, protocol.ECNUnsupported); err != nil {
   464  		t.logger.Debugf("Error sending Stateless Reset to %s: %s", p.remoteAddr, err)
   465  	}
   466  }
   467  
   468  func (t *Transport) maybeHandleStatelessReset(data []byte) bool {
   469  	// stateless resets are always short header packets
   470  	if wire.IsLongHeaderPacket(data[0]) {
   471  		return false
   472  	}
   473  	if len(data) < 17 /* type byte + 16 bytes for the reset token */ {
   474  		return false
   475  	}
   476  
   477  	token := *(*protocol.StatelessResetToken)(data[len(data)-16:])
   478  	if conn, ok := t.handlerMap.GetByResetToken(token); ok {
   479  		t.logger.Debugf("Received a stateless reset with token %#x. Closing connection.", token)
   480  		go conn.destroy(&StatelessResetError{Token: token})
   481  		return true
   482  	}
   483  	return false
   484  }
   485  
   486  func (t *Transport) handleNonQUICPacket(p receivedPacket) {
   487  	// Strictly speaking, this is racy,
   488  	// but we only care about receiving packets at some point after ReadNonQUICPacket has been called.
   489  	if !t.readingNonQUICPackets.Load() {
   490  		return
   491  	}
   492  	select {
   493  	case t.nonQUICPackets <- p:
   494  	default:
   495  		if t.Tracer != nil && t.Tracer.DroppedPacket != nil {
   496  			t.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropDOSPrevention)
   497  		}
   498  	}
   499  }
   500  
   501  const maxQueuedNonQUICPackets = 32
   502  
   503  // ReadNonQUICPacket reads non-QUIC packets received on the underlying connection.
   504  // The detection logic is very simple: Any packet that has the first and second bit of the packet set to 0.
   505  // Note that this is stricter than the detection logic defined in RFC 9443.
   506  func (t *Transport) ReadNonQUICPacket(ctx context.Context, b []byte) (int, net.Addr, error) {
   507  	if err := t.init(false); err != nil {
   508  		return 0, nil, err
   509  	}
   510  	if !t.readingNonQUICPackets.Load() {
   511  		t.nonQUICPackets = make(chan receivedPacket, maxQueuedNonQUICPackets)
   512  		t.readingNonQUICPackets.Store(true)
   513  	}
   514  	select {
   515  	case <-ctx.Done():
   516  		return 0, nil, ctx.Err()
   517  	case p := <-t.nonQUICPackets:
   518  		n := copy(b, p.data)
   519  		return n, p.remoteAddr, nil
   520  	case <-t.listening:
   521  		return 0, nil, errors.New("closed")
   522  	}
   523  }
   524  
   525  func setTLSConfigServerName(tlsConf *tls.Config, addr net.Addr, host string) {
   526  	// If no ServerName is set, infer the ServerName from the host we're connecting to.
   527  	if tlsConf.ServerName != "" {
   528  		return
   529  	}
   530  	if host == "" {
   531  		if udpAddr, ok := addr.(*net.UDPAddr); ok {
   532  			tlsConf.ServerName = udpAddr.IP.String()
   533  			return
   534  		}
   535  	}
   536  	h, _, err := net.SplitHostPort(host)
   537  	if err != nil { // This happens if the host doesn't contain a port number.
   538  		tlsConf.ServerName = host
   539  		return
   540  	}
   541  	tlsConf.ServerName = h
   542  }