github.com/3andne/restls-client-go@v0.1.6/u_quic.go (about)

     1  // Copyright 2023 The uTLS Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package tls
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  )
    11  
    12  // A UQUICConn represents a connection which uses a QUIC implementation as the underlying
    13  // transport as described in RFC 9001.
    14  //
    15  // Methods of UQUICConn are not safe for concurrent use.
    16  type UQUICConn struct {
    17  	conn *UConn
    18  
    19  	sessionTicketSent bool
    20  }
    21  
    22  // QUICClient returns a new TLS client side connection using QUICTransport as the
    23  // underlying transport. The config cannot be nil.
    24  //
    25  // The config's MinVersion must be at least TLS 1.3.
    26  func UQUICClient(config *QUICConfig, clientHelloID ClientHelloID) *UQUICConn {
    27  	return newUQUICConn(UClient(nil, config.TLSConfig, clientHelloID))
    28  }
    29  
    30  func newUQUICConn(uconn *UConn) *UQUICConn {
    31  	uconn.quic = &quicState{
    32  		signalc:  make(chan struct{}),
    33  		blockedc: make(chan struct{}),
    34  	}
    35  	uconn.quic.events = uconn.quic.eventArr[:0]
    36  	return &UQUICConn{
    37  		conn: uconn,
    38  	}
    39  }
    40  
    41  // Start starts the client or server handshake protocol.
    42  // It may produce connection events, which may be read with NextEvent.
    43  //
    44  // Start must be called at most once.
    45  func (q *UQUICConn) Start(ctx context.Context) error {
    46  	if q.conn.quic.started {
    47  		return quicError(errors.New("tls: Start called more than once"))
    48  	}
    49  	q.conn.quic.started = true
    50  	if q.conn.config.MinVersion < VersionTLS13 {
    51  		return quicError(errors.New("tls: Config MinVersion must be at least TLS 1.13"))
    52  	}
    53  	go q.conn.HandshakeContext(ctx)
    54  	if _, ok := <-q.conn.quic.blockedc; !ok {
    55  		return q.conn.handshakeErr
    56  	}
    57  	return nil
    58  }
    59  
    60  func (q *UQUICConn) ApplyPreset(p *ClientHelloSpec) error {
    61  	return q.conn.ApplyPreset(p)
    62  }
    63  
    64  // NextEvent returns the next event occurring on the connection.
    65  // It returns an event with a Kind of QUICNoEvent when no events are available.
    66  func (q *UQUICConn) NextEvent() QUICEvent {
    67  	qs := q.conn.quic
    68  	if last := qs.nextEvent - 1; last >= 0 && len(qs.events[last].Data) > 0 {
    69  		// Write over some of the previous event's data,
    70  		// to catch callers erroniously retaining it.
    71  		qs.events[last].Data[0] = 0
    72  	}
    73  	if qs.nextEvent >= len(qs.events) {
    74  		qs.events = qs.events[:0]
    75  		qs.nextEvent = 0
    76  		return QUICEvent{Kind: QUICNoEvent}
    77  	}
    78  	e := qs.events[qs.nextEvent]
    79  	qs.events[qs.nextEvent] = QUICEvent{} // zero out references to data
    80  	qs.nextEvent++
    81  	return e
    82  }
    83  
    84  // Close closes the connection and stops any in-progress handshake.
    85  func (q *UQUICConn) Close() error {
    86  	if q.conn.quic.cancel == nil {
    87  		return nil // never started
    88  	}
    89  	q.conn.quic.cancel()
    90  	for range q.conn.quic.blockedc {
    91  		// Wait for the handshake goroutine to return.
    92  	}
    93  	return q.conn.handshakeErr
    94  }
    95  
    96  // HandleData handles handshake bytes received from the peer.
    97  // It may produce connection events, which may be read with NextEvent.
    98  func (q *UQUICConn) HandleData(level QUICEncryptionLevel, data []byte) error {
    99  	c := q.conn
   100  	if c.in.level != level {
   101  		return quicError(c.in.setErrorLocked(errors.New("tls: handshake data received at wrong level")))
   102  	}
   103  	c.quic.readbuf = data
   104  	<-c.quic.signalc
   105  	_, ok := <-c.quic.blockedc
   106  	if ok {
   107  		// The handshake goroutine is waiting for more data.
   108  		return nil
   109  	}
   110  	// The handshake goroutine has exited.
   111  	c.hand.Write(c.quic.readbuf)
   112  	c.quic.readbuf = nil
   113  	for q.conn.hand.Len() >= 4 && q.conn.handshakeErr == nil {
   114  		b := q.conn.hand.Bytes()
   115  		n := int(b[1])<<16 | int(b[2])<<8 | int(b[3])
   116  		if 4+n < len(b) {
   117  			return nil
   118  		}
   119  		if err := q.conn.handlePostHandshakeMessage(); err != nil {
   120  			return quicError(err)
   121  		}
   122  	}
   123  	if q.conn.handshakeErr != nil {
   124  		return quicError(q.conn.handshakeErr)
   125  	}
   126  	return nil
   127  }
   128  
   129  // SendSessionTicket sends a session ticket to the client.
   130  // It produces connection events, which may be read with NextEvent.
   131  // Currently, it can only be called once.
   132  func (q *UQUICConn) SendSessionTicket(earlyData bool) error {
   133  	c := q.conn
   134  	if !c.isHandshakeComplete.Load() {
   135  		return quicError(errors.New("tls: SendSessionTicket called before handshake completed"))
   136  	}
   137  	if c.isClient {
   138  		return quicError(errors.New("tls: SendSessionTicket called on the client"))
   139  	}
   140  	if q.sessionTicketSent {
   141  		return quicError(errors.New("tls: SendSessionTicket called multiple times"))
   142  	}
   143  	q.sessionTicketSent = true
   144  	return quicError(c.sendSessionTicket(earlyData))
   145  }
   146  
   147  // ConnectionState returns basic TLS details about the connection.
   148  func (q *UQUICConn) ConnectionState() ConnectionState {
   149  	return q.conn.ConnectionState()
   150  }
   151  
   152  // SetTransportParameters sets the transport parameters to send to the peer.
   153  //
   154  // Server connections may delay setting the transport parameters until after
   155  // receiving the client's transport parameters. See QUICTransportParametersRequired.
   156  func (q *UQUICConn) SetTransportParameters(params []byte) {
   157  	if params == nil {
   158  		params = []byte{}
   159  	}
   160  	q.conn.quic.transportParams = params // this won't be used for building ClientHello when using a preset
   161  
   162  	// // instead, we set the transport parameters hold by the ClientHello
   163  	// for _, ext := range q.conn.Extensions {
   164  	// 	if qtp, ok := ext.(*QUICTransportParametersExtension); ok {
   165  	// 		qtp.TransportParametersExtData = params
   166  	// 	}
   167  	// }
   168  
   169  	if q.conn.quic.started {
   170  		<-q.conn.quic.signalc
   171  		<-q.conn.quic.blockedc
   172  	}
   173  }
   174  
   175  func (uc *UConn) QUICSetReadSecret(level QUICEncryptionLevel, suite uint16, secret []byte) {
   176  	uc.quic.events = append(uc.quic.events, QUICEvent{
   177  		Kind:  QUICSetReadSecret,
   178  		Level: level,
   179  		Suite: suite,
   180  		Data:  secret,
   181  	})
   182  }
   183  
   184  func (uc *UConn) QUICSetWriteSecret(level QUICEncryptionLevel, suite uint16, secret []byte) {
   185  	uc.quic.events = append(uc.quic.events, QUICEvent{
   186  		Kind:  QUICSetWriteSecret,
   187  		Level: level,
   188  		Suite: suite,
   189  		Data:  secret,
   190  	})
   191  }
   192  
   193  func (uc *UConn) QUICGetTransportParameters() ([]byte, error) {
   194  	if uc.quic.transportParams == nil {
   195  		uc.quic.events = append(uc.quic.events, QUICEvent{
   196  			Kind: QUICTransportParametersRequired,
   197  		})
   198  	}
   199  	for uc.quic.transportParams == nil {
   200  		if err := uc.quicWaitForSignal(); err != nil {
   201  			return nil, err
   202  		}
   203  	}
   204  	return uc.quic.transportParams, nil
   205  }