1package quic 2 3import ( 4 "bytes" 5 "context" 6 "crypto/rand" 7 "crypto/tls" 8 "errors" 9 "fmt" 10 "net" 11 "sync" 12 "sync/atomic" 13 "time" 14 15 "github.com/lucas-clemente/quic-go/internal/handshake" 16 "github.com/lucas-clemente/quic-go/internal/protocol" 17 "github.com/lucas-clemente/quic-go/internal/qerr" 18 "github.com/lucas-clemente/quic-go/internal/utils" 19 "github.com/lucas-clemente/quic-go/internal/wire" 20 "github.com/lucas-clemente/quic-go/logging" 21) 22 23// packetHandler handles packets 24type packetHandler interface { 25 handlePacket(*receivedPacket) 26 shutdown() 27 destroy(error) 28 getPerspective() protocol.Perspective 29} 30 31type unknownPacketHandler interface { 32 handlePacket(*receivedPacket) 33 setCloseError(error) 34} 35 36type packetHandlerManager interface { 37 AddWithConnID(protocol.ConnectionID, protocol.ConnectionID, func() packetHandler) bool 38 Destroy() error 39 sessionRunner 40 SetServer(unknownPacketHandler) 41 CloseServer() 42} 43 44type quicSession interface { 45 EarlySession 46 earlySessionReady() <-chan struct{} 47 handlePacket(*receivedPacket) 48 GetVersion() protocol.VersionNumber 49 getPerspective() protocol.Perspective 50 run() error 51 destroy(error) 52 shutdown() 53} 54 55// A Listener of QUIC 56type baseServer struct { 57 mutex sync.Mutex 58 59 acceptEarlySessions bool 60 61 tlsConf *tls.Config 62 config *Config 63 64 conn connection 65 // If the server is started with ListenAddr, we create a packet conn. 66 // If it is started with Listen, we take a packet conn as a parameter. 67 createdPacketConn bool 68 69 tokenGenerator *handshake.TokenGenerator 70 71 sessionHandler packetHandlerManager 72 73 receivedPackets chan *receivedPacket 74 75 // set as a member, so they can be set in the tests 76 newSession func( 77 sendConn, 78 sessionRunner, 79 protocol.ConnectionID, /* original dest connection ID */ 80 *protocol.ConnectionID, /* retry src connection ID */ 81 protocol.ConnectionID, /* client dest connection ID */ 82 protocol.ConnectionID, /* destination connection ID */ 83 protocol.ConnectionID, /* source connection ID */ 84 protocol.StatelessResetToken, 85 *Config, 86 *tls.Config, 87 *handshake.TokenGenerator, 88 bool, /* enable 0-RTT */ 89 logging.ConnectionTracer, 90 uint64, 91 utils.Logger, 92 protocol.VersionNumber, 93 ) quicSession 94 95 serverError error 96 errorChan chan struct{} 97 closed bool 98 running chan struct{} // closed as soon as run() returns 99 100 sessionQueue chan quicSession 101 sessionQueueLen int32 // to be used as an atomic 102 103 logger utils.Logger 104} 105 106var ( 107 _ Listener = &baseServer{} 108 _ unknownPacketHandler = &baseServer{} 109) 110 111type earlyServer struct{ *baseServer } 112 113var _ EarlyListener = &earlyServer{} 114 115func (s *earlyServer) Accept(ctx context.Context) (EarlySession, error) { 116 return s.baseServer.accept(ctx) 117} 118 119// ListenAddr creates a QUIC server listening on a given address. 120// The tls.Config must not be nil and must contain a certificate configuration. 121// The quic.Config may be nil, in that case the default values will be used. 122func ListenAddr(addr string, tlsConf *tls.Config, config *Config) (Listener, error) { 123 return listenAddr(addr, tlsConf, config, false) 124} 125 126// ListenAddrEarly works like ListenAddr, but it returns sessions before the handshake completes. 127func ListenAddrEarly(addr string, tlsConf *tls.Config, config *Config) (EarlyListener, error) { 128 s, err := listenAddr(addr, tlsConf, config, true) 129 if err != nil { 130 return nil, err 131 } 132 return &earlyServer{s}, nil 133} 134 135func listenAddr(addr string, tlsConf *tls.Config, config *Config, acceptEarly bool) (*baseServer, error) { 136 udpAddr, err := net.ResolveUDPAddr("udp", addr) 137 if err != nil { 138 return nil, err 139 } 140 conn, err := net.ListenUDP("udp", udpAddr) 141 if err != nil { 142 return nil, err 143 } 144 serv, err := listen(conn, tlsConf, config, acceptEarly) 145 if err != nil { 146 return nil, err 147 } 148 serv.createdPacketConn = true 149 return serv, nil 150} 151 152// Listen listens for QUIC connections on a given net.PacketConn. If the 153// PacketConn satisfies the OOBCapablePacketConn interface (as a net.UDPConn 154// does), ECN and packet info support will be enabled. In this case, ReadMsgUDP 155// and WriteMsgUDP will be used instead of ReadFrom and WriteTo to read/write 156// packets. A single net.PacketConn only be used for a single call to Listen. 157// The PacketConn can be used for simultaneous calls to Dial. QUIC connection 158// IDs are used for demultiplexing the different connections. The tls.Config 159// must not be nil and must contain a certificate configuration. The 160// tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites. Furthermore, 161// it must define an application control (using NextProtos). The quic.Config may 162// be nil, in that case the default values will be used. 163func Listen(conn net.PacketConn, tlsConf *tls.Config, config *Config) (Listener, error) { 164 return listen(conn, tlsConf, config, false) 165} 166 167// ListenEarly works like Listen, but it returns sessions before the handshake completes. 168func ListenEarly(conn net.PacketConn, tlsConf *tls.Config, config *Config) (EarlyListener, error) { 169 s, err := listen(conn, tlsConf, config, true) 170 if err != nil { 171 return nil, err 172 } 173 return &earlyServer{s}, nil 174} 175 176func listen(conn net.PacketConn, tlsConf *tls.Config, config *Config, acceptEarly bool) (*baseServer, error) { 177 if tlsConf == nil { 178 return nil, errors.New("quic: tls.Config not set") 179 } 180 if err := validateConfig(config); err != nil { 181 return nil, err 182 } 183 config = populateServerConfig(config) 184 for _, v := range config.Versions { 185 if !protocol.IsValidVersion(v) { 186 return nil, fmt.Errorf("%s is not a valid QUIC version", v) 187 } 188 } 189 190 sessionHandler, err := getMultiplexer().AddConn(conn, config.ConnectionIDLength, config.StatelessResetKey, config.Tracer) 191 if err != nil { 192 return nil, err 193 } 194 tokenGenerator, err := handshake.NewTokenGenerator(rand.Reader) 195 if err != nil { 196 return nil, err 197 } 198 c, err := wrapConn(conn) 199 if err != nil { 200 return nil, err 201 } 202 s := &baseServer{ 203 conn: c, 204 tlsConf: tlsConf, 205 config: config, 206 tokenGenerator: tokenGenerator, 207 sessionHandler: sessionHandler, 208 sessionQueue: make(chan quicSession), 209 errorChan: make(chan struct{}), 210 running: make(chan struct{}), 211 receivedPackets: make(chan *receivedPacket, protocol.MaxServerUnprocessedPackets), 212 newSession: newSession, 213 logger: utils.DefaultLogger.WithPrefix("server"), 214 acceptEarlySessions: acceptEarly, 215 } 216 go s.run() 217 sessionHandler.SetServer(s) 218 s.logger.Debugf("Listening for %s connections on %s", conn.LocalAddr().Network(), conn.LocalAddr().String()) 219 return s, nil 220} 221 222func (s *baseServer) run() { 223 defer close(s.running) 224 for { 225 select { 226 case <-s.errorChan: 227 return 228 default: 229 } 230 select { 231 case <-s.errorChan: 232 return 233 case p := <-s.receivedPackets: 234 if bufferStillInUse := s.handlePacketImpl(p); !bufferStillInUse { 235 p.buffer.Release() 236 } 237 } 238 } 239} 240 241var defaultAcceptToken = func(clientAddr net.Addr, token *Token) bool { 242 if token == nil { 243 return false 244 } 245 validity := protocol.TokenValidity 246 if token.IsRetryToken { 247 validity = protocol.RetryTokenValidity 248 } 249 if time.Now().After(token.SentTime.Add(validity)) { 250 return false 251 } 252 var sourceAddr string 253 if udpAddr, ok := clientAddr.(*net.UDPAddr); ok { 254 sourceAddr = udpAddr.IP.String() 255 } else { 256 sourceAddr = clientAddr.String() 257 } 258 return sourceAddr == token.RemoteAddr 259} 260 261// Accept returns sessions that already completed the handshake. 262// It is only valid if acceptEarlySessions is false. 263func (s *baseServer) Accept(ctx context.Context) (Session, error) { 264 return s.accept(ctx) 265} 266 267func (s *baseServer) accept(ctx context.Context) (quicSession, error) { 268 select { 269 case <-ctx.Done(): 270 return nil, ctx.Err() 271 case sess := <-s.sessionQueue: 272 atomic.AddInt32(&s.sessionQueueLen, -1) 273 return sess, nil 274 case <-s.errorChan: 275 return nil, s.serverError 276 } 277} 278 279// Close the server 280func (s *baseServer) Close() error { 281 s.mutex.Lock() 282 if s.closed { 283 s.mutex.Unlock() 284 return nil 285 } 286 if s.serverError == nil { 287 s.serverError = errors.New("server closed") 288 } 289 // If the server was started with ListenAddr, we created the packet conn. 290 // We need to close it in order to make the go routine reading from that conn return. 291 createdPacketConn := s.createdPacketConn 292 s.closed = true 293 close(s.errorChan) 294 s.mutex.Unlock() 295 296 <-s.running 297 s.sessionHandler.CloseServer() 298 if createdPacketConn { 299 return s.sessionHandler.Destroy() 300 } 301 return nil 302} 303 304func (s *baseServer) setCloseError(e error) { 305 s.mutex.Lock() 306 defer s.mutex.Unlock() 307 if s.closed { 308 return 309 } 310 s.closed = true 311 s.serverError = e 312 close(s.errorChan) 313} 314 315// Addr returns the server's network address 316func (s *baseServer) Addr() net.Addr { 317 return s.conn.LocalAddr() 318} 319 320func (s *baseServer) handlePacket(p *receivedPacket) { 321 select { 322 case s.receivedPackets <- p: 323 default: 324 s.logger.Debugf("Dropping packet from %s (%d bytes). Server receive queue full.", p.remoteAddr, p.Size()) 325 if s.config.Tracer != nil { 326 s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropDOSPrevention) 327 } 328 } 329} 330 331func (s *baseServer) handlePacketImpl(p *receivedPacket) bool /* is the buffer still in use? */ { 332 if wire.IsVersionNegotiationPacket(p.data) { 333 s.logger.Debugf("Dropping Version Negotiation packet.") 334 if s.config.Tracer != nil { 335 s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeVersionNegotiation, p.Size(), logging.PacketDropUnexpectedPacket) 336 } 337 return false 338 } 339 // If we're creating a new session, the packet will be passed to the session. 340 // The header will then be parsed again. 341 hdr, _, _, err := wire.ParsePacket(p.data, s.config.ConnectionIDLength) 342 if err != nil && err != wire.ErrUnsupportedVersion { 343 if s.config.Tracer != nil { 344 s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError) 345 } 346 s.logger.Debugf("Error parsing packet: %s", err) 347 return false 348 } 349 // Short header packets should never end up here in the first place 350 if !hdr.IsLongHeader { 351 panic(fmt.Sprintf("misrouted packet: %#v", hdr)) 352 } 353 if hdr.Type == protocol.PacketTypeInitial && p.Size() < protocol.MinInitialPacketSize { 354 s.logger.Debugf("Dropping a packet that is too small to be a valid Initial (%d bytes)", p.Size()) 355 if s.config.Tracer != nil { 356 s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropUnexpectedPacket) 357 } 358 return false 359 } 360 // send a Version Negotiation Packet if the client is speaking a different protocol version 361 if !protocol.IsSupportedVersion(s.config.Versions, hdr.Version) { 362 if p.Size() < protocol.MinUnknownVersionPacketSize { 363 s.logger.Debugf("Dropping a packet with an unknown version that is too small (%d bytes)", p.Size()) 364 if s.config.Tracer != nil { 365 s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket) 366 } 367 return false 368 } 369 if !s.config.DisableVersionNegotiationPackets { 370 go s.sendVersionNegotiationPacket(p, hdr) 371 } 372 return false 373 } 374 if hdr.IsLongHeader && hdr.Type != protocol.PacketTypeInitial { 375 // Drop long header packets. 376 // There's little point in sending a Stateless Reset, since the client 377 // might not have received the token yet. 378 s.logger.Debugf("Dropping long header packet of type %s (%d bytes)", hdr.Type, len(p.data)) 379 if s.config.Tracer != nil { 380 s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeFromHeader(hdr), p.Size(), logging.PacketDropUnexpectedPacket) 381 } 382 return false 383 } 384 385 s.logger.Debugf("<- Received Initial packet.") 386 387 if err := s.handleInitialImpl(p, hdr); err != nil { 388 s.logger.Errorf("Error occurred handling initial packet: %s", err) 389 } 390 // Don't put the packet buffer back. 391 // handleInitialImpl deals with the buffer. 392 return true 393} 394 395func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) error { 396 if len(hdr.Token) == 0 && hdr.DestConnectionID.Len() < protocol.MinConnectionIDLenInitial { 397 p.buffer.Release() 398 if s.config.Tracer != nil { 399 s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropUnexpectedPacket) 400 } 401 return errors.New("too short connection ID") 402 } 403 404 var ( 405 token *Token 406 retrySrcConnID *protocol.ConnectionID 407 ) 408 origDestConnID := hdr.DestConnectionID 409 if len(hdr.Token) > 0 { 410 c, err := s.tokenGenerator.DecodeToken(hdr.Token) 411 if err == nil { 412 token = &Token{ 413 IsRetryToken: c.IsRetryToken, 414 RemoteAddr: c.RemoteAddr, 415 SentTime: c.SentTime, 416 } 417 if token.IsRetryToken { 418 origDestConnID = c.OriginalDestConnectionID 419 retrySrcConnID = &c.RetrySrcConnectionID 420 } 421 } 422 } 423 if !s.config.AcceptToken(p.remoteAddr, token) { 424 go func() { 425 defer p.buffer.Release() 426 if token != nil && token.IsRetryToken { 427 if err := s.maybeSendInvalidToken(p, hdr); err != nil { 428 s.logger.Debugf("Error sending INVALID_TOKEN error: %s", err) 429 } 430 return 431 } 432 if err := s.sendRetry(p.remoteAddr, hdr, p.info); err != nil { 433 s.logger.Debugf("Error sending Retry: %s", err) 434 } 435 }() 436 return nil 437 } 438 439 if queueLen := atomic.LoadInt32(&s.sessionQueueLen); queueLen >= protocol.MaxAcceptQueueSize { 440 s.logger.Debugf("Rejecting new connection. Server currently busy. Accept queue length: %d (max %d)", queueLen, protocol.MaxAcceptQueueSize) 441 go func() { 442 defer p.buffer.Release() 443 if err := s.sendConnectionRefused(p.remoteAddr, hdr, p.info); err != nil { 444 s.logger.Debugf("Error rejecting connection: %s", err) 445 } 446 }() 447 return nil 448 } 449 450 connID, err := protocol.GenerateConnectionID(s.config.ConnectionIDLength) 451 if err != nil { 452 return err 453 } 454 s.logger.Debugf("Changing connection ID to %s.", connID) 455 var sess quicSession 456 tracingID := nextSessionTracingID() 457 if added := s.sessionHandler.AddWithConnID(hdr.DestConnectionID, connID, func() packetHandler { 458 var tracer logging.ConnectionTracer 459 if s.config.Tracer != nil { 460 // Use the same connection ID that is passed to the client's GetLogWriter callback. 461 connID := hdr.DestConnectionID 462 if origDestConnID.Len() > 0 { 463 connID = origDestConnID 464 } 465 tracer = s.config.Tracer.TracerForConnection( 466 context.WithValue(context.Background(), SessionTracingKey, tracingID), 467 protocol.PerspectiveServer, 468 connID, 469 ) 470 } 471 sess = s.newSession( 472 newSendConn(s.conn, p.remoteAddr, p.info), 473 s.sessionHandler, 474 origDestConnID, 475 retrySrcConnID, 476 hdr.DestConnectionID, 477 hdr.SrcConnectionID, 478 connID, 479 s.sessionHandler.GetStatelessResetToken(connID), 480 s.config, 481 s.tlsConf, 482 s.tokenGenerator, 483 s.acceptEarlySessions, 484 tracer, 485 tracingID, 486 s.logger, 487 hdr.Version, 488 ) 489 sess.handlePacket(p) 490 return sess 491 }); !added { 492 return nil 493 } 494 go sess.run() 495 go s.handleNewSession(sess) 496 if sess == nil { 497 p.buffer.Release() 498 return nil 499 } 500 return nil 501} 502 503func (s *baseServer) handleNewSession(sess quicSession) { 504 sessCtx := sess.Context() 505 if s.acceptEarlySessions { 506 // wait until the early session is ready (or the handshake fails) 507 select { 508 case <-sess.earlySessionReady(): 509 case <-sessCtx.Done(): 510 return 511 } 512 } else { 513 // wait until the handshake is complete (or fails) 514 select { 515 case <-sess.HandshakeComplete().Done(): 516 case <-sessCtx.Done(): 517 return 518 } 519 } 520 521 atomic.AddInt32(&s.sessionQueueLen, 1) 522 select { 523 case s.sessionQueue <- sess: 524 // blocks until the session is accepted 525 case <-sessCtx.Done(): 526 atomic.AddInt32(&s.sessionQueueLen, -1) 527 // don't pass sessions that were already closed to Accept() 528 } 529} 530 531func (s *baseServer) sendRetry(remoteAddr net.Addr, hdr *wire.Header, info *packetInfo) error { 532 // Log the Initial packet now. 533 // If no Retry is sent, the packet will be logged by the session. 534 (&wire.ExtendedHeader{Header: *hdr}).Log(s.logger) 535 srcConnID, err := protocol.GenerateConnectionID(s.config.ConnectionIDLength) 536 if err != nil { 537 return err 538 } 539 token, err := s.tokenGenerator.NewRetryToken(remoteAddr, hdr.DestConnectionID, srcConnID) 540 if err != nil { 541 return err 542 } 543 replyHdr := &wire.ExtendedHeader{} 544 replyHdr.IsLongHeader = true 545 replyHdr.Type = protocol.PacketTypeRetry 546 replyHdr.Version = hdr.Version 547 replyHdr.SrcConnectionID = srcConnID 548 replyHdr.DestConnectionID = hdr.SrcConnectionID 549 replyHdr.Token = token 550 if s.logger.Debug() { 551 s.logger.Debugf("Changing connection ID to %s.", srcConnID) 552 s.logger.Debugf("-> Sending Retry") 553 replyHdr.Log(s.logger) 554 } 555 556 packetBuffer := getPacketBuffer() 557 defer packetBuffer.Release() 558 buf := bytes.NewBuffer(packetBuffer.Data) 559 if err := replyHdr.Write(buf, hdr.Version); err != nil { 560 return err 561 } 562 // append the Retry integrity tag 563 tag := handshake.GetRetryIntegrityTag(buf.Bytes(), hdr.DestConnectionID, hdr.Version) 564 buf.Write(tag[:]) 565 if s.config.Tracer != nil { 566 s.config.Tracer.SentPacket(remoteAddr, &replyHdr.Header, protocol.ByteCount(buf.Len()), nil) 567 } 568 _, err = s.conn.WritePacket(buf.Bytes(), remoteAddr, info.OOB()) 569 return err 570} 571 572func (s *baseServer) maybeSendInvalidToken(p *receivedPacket, hdr *wire.Header) error { 573 // Only send INVALID_TOKEN if we can unprotect the packet. 574 // This makes sure that we won't send it for packets that were corrupted. 575 sealer, opener := handshake.NewInitialAEAD(hdr.DestConnectionID, protocol.PerspectiveServer, hdr.Version) 576 data := p.data[:hdr.ParsedLen()+hdr.Length] 577 extHdr, err := unpackHeader(opener, hdr, data, hdr.Version) 578 if err != nil { 579 if s.config.Tracer != nil { 580 s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropHeaderParseError) 581 } 582 // don't return the error here. Just drop the packet. 583 return nil 584 } 585 hdrLen := extHdr.ParsedLen() 586 if _, err := opener.Open(data[hdrLen:hdrLen], data[hdrLen:], extHdr.PacketNumber, data[:hdrLen]); err != nil { 587 // don't return the error here. Just drop the packet. 588 if s.config.Tracer != nil { 589 s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropPayloadDecryptError) 590 } 591 return nil 592 } 593 if s.logger.Debug() { 594 s.logger.Debugf("Client sent an invalid retry token. Sending INVALID_TOKEN to %s.", p.remoteAddr) 595 } 596 return s.sendError(p.remoteAddr, hdr, sealer, qerr.InvalidToken, p.info) 597} 598 599func (s *baseServer) sendConnectionRefused(remoteAddr net.Addr, hdr *wire.Header, info *packetInfo) error { 600 sealer, _ := handshake.NewInitialAEAD(hdr.DestConnectionID, protocol.PerspectiveServer, hdr.Version) 601 return s.sendError(remoteAddr, hdr, sealer, qerr.ConnectionRefused, info) 602} 603 604// sendError sends the error as a response to the packet received with header hdr 605func (s *baseServer) sendError(remoteAddr net.Addr, hdr *wire.Header, sealer handshake.LongHeaderSealer, errorCode qerr.TransportErrorCode, info *packetInfo) error { 606 packetBuffer := getPacketBuffer() 607 defer packetBuffer.Release() 608 buf := bytes.NewBuffer(packetBuffer.Data) 609 610 ccf := &wire.ConnectionCloseFrame{ErrorCode: uint64(errorCode)} 611 612 replyHdr := &wire.ExtendedHeader{} 613 replyHdr.IsLongHeader = true 614 replyHdr.Type = protocol.PacketTypeInitial 615 replyHdr.Version = hdr.Version 616 replyHdr.SrcConnectionID = hdr.DestConnectionID 617 replyHdr.DestConnectionID = hdr.SrcConnectionID 618 replyHdr.PacketNumberLen = protocol.PacketNumberLen4 619 replyHdr.Length = 4 /* packet number len */ + ccf.Length(hdr.Version) + protocol.ByteCount(sealer.Overhead()) 620 if err := replyHdr.Write(buf, hdr.Version); err != nil { 621 return err 622 } 623 payloadOffset := buf.Len() 624 625 if err := ccf.Write(buf, hdr.Version); err != nil { 626 return err 627 } 628 629 raw := buf.Bytes() 630 _ = sealer.Seal(raw[payloadOffset:payloadOffset], raw[payloadOffset:], replyHdr.PacketNumber, raw[:payloadOffset]) 631 raw = raw[0 : buf.Len()+sealer.Overhead()] 632 633 pnOffset := payloadOffset - int(replyHdr.PacketNumberLen) 634 sealer.EncryptHeader( 635 raw[pnOffset+4:pnOffset+4+16], 636 &raw[0], 637 raw[pnOffset:payloadOffset], 638 ) 639 640 replyHdr.Log(s.logger) 641 wire.LogFrame(s.logger, ccf, true) 642 if s.config.Tracer != nil { 643 s.config.Tracer.SentPacket(remoteAddr, &replyHdr.Header, protocol.ByteCount(len(raw)), []logging.Frame{ccf}) 644 } 645 _, err := s.conn.WritePacket(raw, remoteAddr, info.OOB()) 646 return err 647} 648 649func (s *baseServer) sendVersionNegotiationPacket(p *receivedPacket, hdr *wire.Header) { 650 s.logger.Debugf("Client offered version %s, sending Version Negotiation", hdr.Version) 651 data, err := wire.ComposeVersionNegotiation(hdr.SrcConnectionID, hdr.DestConnectionID, s.config.Versions) 652 if err != nil { 653 s.logger.Debugf("Error composing Version Negotiation: %s", err) 654 return 655 } 656 if s.config.Tracer != nil { 657 s.config.Tracer.SentPacket( 658 p.remoteAddr, 659 &wire.Header{ 660 IsLongHeader: true, 661 DestConnectionID: hdr.SrcConnectionID, 662 SrcConnectionID: hdr.DestConnectionID, 663 }, 664 protocol.ByteCount(len(data)), 665 nil, 666 ) 667 } 668 if _, err := s.conn.WritePacket(data, p.remoteAddr, p.info.OOB()); err != nil { 669 s.logger.Debugf("Error sending Version Negotiation: %s", err) 670 } 671} 672