github.com/tumi8/quic-go@v0.37.4-tum/transport.go (about) 1 package quic 2 3 import ( 4 "context" 5 "crypto/rand" 6 "crypto/tls" 7 "errors" 8 "net" 9 "sync" 10 "time" 11 12 "github.com/tumi8/quic-go/noninternal/wire" 13 14 "github.com/tumi8/quic-go/noninternal/protocol" 15 "github.com/tumi8/quic-go/noninternal/utils" 16 "github.com/tumi8/quic-go/logging" 17 ) 18 19 // The Transport is the central point to manage incoming and outgoing QUIC connections. 20 // QUIC demultiplexes connections based on their QUIC Connection IDs, not based on the 4-tuple. 21 // This means that a single UDP socket can be used for listening for incoming connections, as well as 22 // for dialing an arbitrary number of outgoing connections. 23 // A Transport handles a single net.PacketConn, and offers a range of configuration options 24 // compared to the simple helper functions like Listen and Dial that this package provides. 25 type Transport struct { 26 // A single net.PacketConn can only be handled by one Transport. 27 // Bad things will happen if passed to multiple Transports. 28 // 29 // A number of optimizations will be enabled if the connections implements the OOBCapablePacketConn interface, 30 // as a *net.UDPConn does. 31 // 1. It enables the Don't Fragment (DF) bit on the IP header. 32 // This is required to run DPLPMTUD (Path MTU Discovery, RFC 8899). 33 // 2. It enables reading of the ECN bits from the IP header. 34 // This allows the remote node to speed up its loss detection and recovery. 35 // 3. It uses batched syscalls (recvmmsg) to more efficiently receive packets from the socket. 36 // 4. It uses Generic Segmentation Offload (GSO) to efficiently send batches of packets (on Linux). 37 // 38 // After passing the connection to the Transport, it's invalid to call ReadFrom or WriteTo on the connection. 39 Conn net.PacketConn 40 41 // The length of the connection ID in bytes. 42 // It can be 0, or any value between 4 and 18. 43 // If unset, a 4 byte connection ID will be used. 44 ConnectionIDLength int 45 46 // Use for generating new connection IDs. 47 // This allows the application to control of the connection IDs used, 48 // which allows routing / load balancing based on connection IDs. 49 // All Connection IDs returned by the ConnectionIDGenerator MUST 50 // have the same length. 51 ConnectionIDGenerator ConnectionIDGenerator 52 53 // The StatelessResetKey is used to generate stateless reset tokens. 54 // If no key is configured, sending of stateless resets is disabled. 55 // It is highly recommended to configure a stateless reset key, as stateless resets 56 // allow the peer to quickly recover from crashes and reboots of this node. 57 // See section 10.3 of RFC 9000 for details. 58 StatelessResetKey *StatelessResetKey 59 60 // A Tracer traces events that don't belong to a single QUIC connection. 61 Tracer logging.Tracer 62 63 handlerMap packetHandlerManager 64 65 mutex sync.Mutex 66 initOnce sync.Once 67 initErr error 68 69 // Set in init. 70 // If no ConnectionIDGenerator is set, this is the ConnectionIDLength. 71 connIDLen int 72 // Set in init. 73 // If no ConnectionIDGenerator is set, this is set to a default. 74 connIDGenerator ConnectionIDGenerator 75 76 server unknownPacketHandler 77 78 conn rawConn 79 80 closeQueue chan closePacket 81 statelessResetQueue chan receivedPacket 82 83 listening chan struct{} // is closed when listen returns 84 closed bool 85 createdConn bool 86 isSingleUse bool // was created for a single server or client, i.e. by calling quic.Listen or quic.Dial 87 88 logger utils.Logger 89 } 90 91 // Listen starts listening for incoming QUIC connections. 92 // There can only be a single listener on any net.PacketConn. 93 // Listen may only be called again after the current Listener was closed. 94 func (t *Transport) Listen(tlsConf *tls.Config, conf *Config) (*Listener, error) { 95 if tlsConf == nil { 96 return nil, errors.New("quic: tls.Config not set") 97 } 98 if err := validateConfig(conf); err != nil { 99 return nil, err 100 } 101 102 t.mutex.Lock() 103 defer t.mutex.Unlock() 104 105 if t.server != nil { 106 return nil, errListenerAlreadySet 107 } 108 conf = populateServerConfig(conf) 109 if err := t.init(false); err != nil { 110 return nil, err 111 } 112 s, err := newServer(t.conn, t.handlerMap, t.connIDGenerator, tlsConf, conf, t.Tracer, t.closeServer, false) 113 if err != nil { 114 return nil, err 115 } 116 t.server = s 117 return &Listener{baseServer: s}, nil 118 } 119 120 // ListenEarly starts listening for incoming QUIC connections. 121 // There can only be a single listener on any net.PacketConn. 122 // Listen may only be called again after the current Listener was closed. 123 func (t *Transport) ListenEarly(tlsConf *tls.Config, conf *Config) (*EarlyListener, error) { 124 if tlsConf == nil { 125 return nil, errors.New("quic: tls.Config not set") 126 } 127 if err := validateConfig(conf); err != nil { 128 return nil, err 129 } 130 131 t.mutex.Lock() 132 defer t.mutex.Unlock() 133 134 if t.server != nil { 135 return nil, errListenerAlreadySet 136 } 137 conf = populateServerConfig(conf) 138 if err := t.init(false); err != nil { 139 return nil, err 140 } 141 s, err := newServer(t.conn, t.handlerMap, t.connIDGenerator, tlsConf, conf, t.Tracer, t.closeServer, true) 142 if err != nil { 143 return nil, err 144 } 145 t.server = s 146 return &EarlyListener{baseServer: s}, nil 147 } 148 149 // Dial dials a new connection to a remote host (not using 0-RTT). 150 func (t *Transport) Dial(ctx context.Context, addr net.Addr, tlsConf *tls.Config, conf *Config) (Connection, error) { 151 if err := validateConfig(conf); err != nil { 152 return nil, err 153 } 154 conf = populateConfig(conf) 155 if err := t.init(t.isSingleUse); err != nil { 156 return nil, err 157 } 158 var onClose func() 159 if t.isSingleUse { 160 onClose = func() { t.Close() } 161 } 162 tlsConf = tlsConf.Clone() 163 tlsConf.MinVersion = tls.VersionTLS13 164 return dial(ctx, newSendConn(t.conn, addr), t.connIDGenerator, t.handlerMap, tlsConf, conf, onClose, false) 165 } 166 167 // DialEarly dials a new connection, attempting to use 0-RTT if possible. 168 func (t *Transport) DialEarly(ctx context.Context, addr net.Addr, tlsConf *tls.Config, conf *Config) (EarlyConnection, error) { 169 if err := validateConfig(conf); err != nil { 170 return nil, err 171 } 172 conf = populateConfig(conf) 173 if err := t.init(t.isSingleUse); err != nil { 174 return nil, err 175 } 176 var onClose func() 177 if t.isSingleUse { 178 onClose = func() { t.Close() } 179 } 180 tlsConf = tlsConf.Clone() 181 tlsConf.MinVersion = tls.VersionTLS13 182 return dial(ctx, newSendConn(t.conn, addr), t.connIDGenerator, t.handlerMap, tlsConf, conf, onClose, true) 183 } 184 185 func (t *Transport) init(allowZeroLengthConnIDs bool) error { 186 t.initOnce.Do(func() { 187 var conn rawConn 188 if c, ok := t.Conn.(rawConn); ok { 189 conn = c 190 } else { 191 var err error 192 conn, err = wrapConn(t.Conn) 193 if err != nil { 194 t.initErr = err 195 return 196 } 197 } 198 t.conn = conn 199 200 t.logger = utils.DefaultLogger // TODO: make this configurable 201 t.conn = conn 202 t.handlerMap = newPacketHandlerMap(t.StatelessResetKey, t.enqueueClosePacket, t.logger) 203 t.listening = make(chan struct{}) 204 205 t.closeQueue = make(chan closePacket, 4) 206 t.statelessResetQueue = make(chan receivedPacket, 4) 207 208 if t.ConnectionIDGenerator != nil { 209 t.connIDGenerator = t.ConnectionIDGenerator 210 t.connIDLen = t.ConnectionIDGenerator.ConnectionIDLen() 211 } else { 212 connIDLen := t.ConnectionIDLength 213 if t.ConnectionIDLength == 0 && !allowZeroLengthConnIDs { 214 connIDLen = protocol.DefaultConnectionIDLength 215 } 216 t.connIDLen = connIDLen 217 t.connIDGenerator = &protocol.DefaultConnectionIDGenerator{ConnLen: t.connIDLen} 218 } 219 220 getMultiplexer().AddConn(t.Conn) 221 go t.listen(conn) 222 go t.runSendQueue() 223 }) 224 return t.initErr 225 } 226 227 // WriteTo sends a packet on the underlying connection. 228 func (t *Transport) WriteTo(b []byte, addr net.Addr) (int, error) { 229 if err := t.init(false); err != nil { 230 return 0, err 231 } 232 return t.conn.WritePacket(b, uint16(len(b)), addr, nil) 233 } 234 235 func (t *Transport) enqueueClosePacket(p closePacket) { 236 select { 237 case t.closeQueue <- p: 238 default: 239 // Oops, we're backlogged. 240 // Just drop the packet, sending CONNECTION_CLOSE copies is best effort anyway. 241 } 242 } 243 244 func (t *Transport) runSendQueue() { 245 for { 246 select { 247 case <-t.listening: 248 return 249 case p := <-t.closeQueue: 250 t.conn.WritePacket(p.payload, uint16(len(p.payload)), p.addr, p.info.OOB()) 251 case p := <-t.statelessResetQueue: 252 t.sendStatelessReset(p) 253 } 254 } 255 } 256 257 // Close closes the underlying connection and waits until listen has returned. 258 // It is invalid to start new listeners or connections after that. 259 func (t *Transport) Close() error { 260 t.close(errors.New("closing")) 261 if t.createdConn { 262 if err := t.Conn.Close(); err != nil { 263 return err 264 } 265 } else if t.conn != nil { 266 t.conn.SetReadDeadline(time.Now()) 267 defer func() { t.conn.SetReadDeadline(time.Time{}) }() 268 } 269 if t.listening != nil { 270 <-t.listening // wait until listening returns 271 } 272 return nil 273 } 274 275 func (t *Transport) closeServer() { 276 t.handlerMap.CloseServer() 277 t.mutex.Lock() 278 t.server = nil 279 if t.isSingleUse { 280 t.closed = true 281 } 282 t.mutex.Unlock() 283 if t.createdConn { 284 t.Conn.Close() 285 } 286 if t.isSingleUse { 287 t.conn.SetReadDeadline(time.Now()) 288 defer func() { t.conn.SetReadDeadline(time.Time{}) }() 289 <-t.listening // wait until listening returns 290 } 291 } 292 293 func (t *Transport) close(e error) { 294 t.mutex.Lock() 295 defer t.mutex.Unlock() 296 if t.closed { 297 return 298 } 299 300 if t.handlerMap != nil { 301 t.handlerMap.Close(e) 302 } 303 if t.server != nil { 304 t.server.setCloseError(e) 305 } 306 t.closed = true 307 } 308 309 // only print warnings about the UDP receive buffer size once 310 var setBufferWarningOnce sync.Once 311 312 func (t *Transport) listen(conn rawConn) { 313 defer close(t.listening) 314 defer getMultiplexer().RemoveConn(t.Conn) 315 316 for { 317 p, err := conn.ReadPacket() 318 //nolint:staticcheck // SA1019 ignore this! 319 // TODO: This code is used to ignore wsa errors on Windows. 320 // Since net.Error.Temporary is deprecated as of Go 1.18, we should find a better solution. 321 // See https://github.com/tumi8/quic-go/issues/1737 for details. 322 if nerr, ok := err.(net.Error); ok && nerr.Temporary() { 323 t.mutex.Lock() 324 closed := t.closed 325 t.mutex.Unlock() 326 if closed { 327 return 328 } 329 t.logger.Debugf("Temporary error reading from conn: %w", err) 330 continue 331 } 332 if err != nil { 333 // Windows returns an error when receiving a UDP datagram that doesn't fit into the provided buffer. 334 if isRecvMsgSizeErr(err) { 335 continue 336 } 337 t.close(err) 338 return 339 } 340 t.handlePacket(p) 341 } 342 } 343 344 func (t *Transport) handlePacket(p receivedPacket) { 345 connID, err := wire.ParseConnectionID(p.data, t.connIDLen) 346 if err != nil { 347 t.logger.Debugf("error parsing connection ID on packet from %s: %s", p.remoteAddr, err) 348 if t.Tracer != nil { 349 t.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError) 350 } 351 p.buffer.MaybeRelease() 352 return 353 } 354 355 if isStatelessReset := t.maybeHandleStatelessReset(p.data); isStatelessReset { 356 return 357 } 358 if handler, ok := t.handlerMap.Get(connID); ok { 359 handler.handlePacket(p) 360 return 361 } 362 if !wire.IsLongHeaderPacket(p.data[0]) { 363 t.maybeSendStatelessReset(p) 364 return 365 } 366 367 t.mutex.Lock() 368 defer t.mutex.Unlock() 369 if t.server == nil { // no server set 370 t.logger.Debugf("received a packet with an unexpected connection ID %s", connID) 371 return 372 } 373 t.server.handlePacket(p) 374 } 375 376 func (t *Transport) maybeSendStatelessReset(p receivedPacket) { 377 if t.StatelessResetKey == nil { 378 p.buffer.Release() 379 return 380 } 381 382 // Don't send a stateless reset in response to very small packets. 383 // This includes packets that could be stateless resets. 384 if len(p.data) <= protocol.MinStatelessResetSize { 385 p.buffer.Release() 386 return 387 } 388 389 select { 390 case t.statelessResetQueue <- p: 391 default: 392 // it's fine to not send a stateless reset when we're busy 393 p.buffer.Release() 394 } 395 } 396 397 func (t *Transport) sendStatelessReset(p receivedPacket) { 398 defer p.buffer.Release() 399 400 connID, err := wire.ParseConnectionID(p.data, t.connIDLen) 401 if err != nil { 402 t.logger.Errorf("error parsing connection ID on packet from %s: %s", p.remoteAddr, err) 403 return 404 } 405 token := t.handlerMap.GetStatelessResetToken(connID) 406 t.logger.Debugf("Sending stateless reset to %s (connection ID: %s). Token: %#x", p.remoteAddr, connID, token) 407 data := make([]byte, protocol.MinStatelessResetSize-16, protocol.MinStatelessResetSize) 408 rand.Read(data) 409 data[0] = (data[0] & 0x7f) | 0x40 410 data = append(data, token[:]...) 411 if _, err := t.conn.WritePacket(data, uint16(len(data)), p.remoteAddr, p.info.OOB()); err != nil { 412 t.logger.Debugf("Error sending Stateless Reset to %s: %s", p.remoteAddr, err) 413 } 414 } 415 416 func (t *Transport) maybeHandleStatelessReset(data []byte) bool { 417 // stateless resets are always short header packets 418 if wire.IsLongHeaderPacket(data[0]) { 419 return false 420 } 421 if len(data) < 17 /* type byte + 16 bytes for the reset token */ { 422 return false 423 } 424 425 token := *(*protocol.StatelessResetToken)(data[len(data)-16:]) 426 if conn, ok := t.handlerMap.GetByResetToken(token); ok { 427 t.logger.Debugf("Received a stateless reset with token %#x. Closing connection.", token) 428 go conn.destroy(&StatelessResetError{Token: token}) 429 return true 430 } 431 return false 432 }