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