// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

package dtls

import (
	
	
	
	
	
	
	
	

	
	
	
	
	
	
	
	
	
	
	
)

const (
	initialTickerInterval = time.Second
	cookieLength          = 20
	sessionLength         = 32
	defaultNamedCurve     = elliptic.X25519
	inboundBufferSize     = 8192
	// Default replay protection window is specified by RFC 6347 Section 4.1.2.6
	defaultReplayProtectionWindow = 64
	// maxAppDataPacketQueueSize is the maximum number of app data packets we will
	// enqueue before the handshake is completed
	maxAppDataPacketQueueSize = 100
)

func invalidKeyingLabels() map[string]bool {
	return map[string]bool{
		"client finished": true,
		"server finished": true,
		"master secret":   true,
		"key expansion":   true,
	}
}

// Conn represents a DTLS connection
type Conn struct {
	lock           sync.RWMutex     // Internal lock (must not be public)
	nextConn       connctx.ConnCtx  // Embedded Conn, typically a udpconn we read/write from
	fragmentBuffer *fragmentBuffer  // out-of-order and missing fragment handling
	handshakeCache *handshakeCache  // caching of handshake messages for verifyData generation
	decrypted      chan interface{} // Decrypted Application Data or error, pull by calling `Read`

	state State // Internal state

	maximumTransmissionUnit int

	handshakeCompletedSuccessfully atomic.Value

	encryptedPackets [][]byte

	connectionClosedByUser bool
	closeLock              sync.Mutex
	closed                 *closer.Closer
	handshakeLoopsFinished sync.WaitGroup

	readDeadline  *deadline.Deadline
	writeDeadline *deadline.Deadline

	log logging.LeveledLogger

	reading               chan struct{}
	handshakeRecv         chan chan struct{}
	cancelHandshaker      func()
	cancelHandshakeReader func()

	fsm *handshakeFSM

	replayProtectionWindow uint
}

func createConn( net.Conn,  *Config,  bool) (*Conn, error) {
	 := validateConfig()
	if  != nil {
		return nil, 
	}

	if  == nil {
		return nil, errNilNextConn
	}

	 := .LoggerFactory
	if  == nil {
		 = logging.NewDefaultLoggerFactory()
	}

	 := .NewLogger("dtls")

	 := .MTU
	if  <= 0 {
		 = defaultMTU
	}

	 := .ReplayProtectionWindow
	if  <= 0 {
		 = defaultReplayProtectionWindow
	}

	 := &Conn{
		nextConn:                connctx.New(),
		fragmentBuffer:          newFragmentBuffer(),
		handshakeCache:          newHandshakeCache(),
		maximumTransmissionUnit: ,

		decrypted: make(chan interface{}, 1),
		log:       ,

		readDeadline:  deadline.New(),
		writeDeadline: deadline.New(),

		reading:          make(chan struct{}, 1),
		handshakeRecv:    make(chan chan struct{}),
		closed:           closer.NewCloser(),
		cancelHandshaker: func() {},

		replayProtectionWindow: uint(),

		state: State{
			isClient: ,
		},
	}

	.setRemoteEpoch(0)
	.setLocalEpoch(0)
	return , nil
}

func handshakeConn( context.Context,  *Conn,  *Config,  bool,  *State) (*Conn, error) {
	if  == nil {
		return nil, errNilNextConn
	}

	,  := parseCipherSuites(.CipherSuites, .CustomCipherSuites, .includeCertificateSuites(), .PSK != nil)
	if  != nil {
		return nil, 
	}

	,  := signaturehash.ParseSignatureSchemes(.SignatureSchemes, .InsecureHashes)
	if  != nil {
		return nil, 
	}

	 := initialTickerInterval
	if .FlightInterval != 0 {
		 = .FlightInterval
	}

	 := .ServerName
	// Do not allow the use of an IP address literal as an SNI value.
	// See RFC 6066, Section 3.
	if net.ParseIP() != nil {
		 = ""
	}

	 := .EllipticCurves
	if len() == 0 {
		 = defaultCurves
	}

	 := &handshakeConfig{
		localPSKCallback:            .PSK,
		localPSKIdentityHint:        .PSKIdentityHint,
		localCipherSuites:           ,
		localSignatureSchemes:       ,
		extendedMasterSecret:        .ExtendedMasterSecret,
		localSRTPProtectionProfiles: .SRTPProtectionProfiles,
		serverName:                  ,
		supportedProtocols:          .SupportedProtocols,
		clientAuth:                  .ClientAuth,
		localCertificates:           .Certificates,
		insecureSkipVerify:          .InsecureSkipVerify,
		verifyPeerCertificate:       .VerifyPeerCertificate,
		verifyConnection:            .VerifyConnection,
		rootCAs:                     .RootCAs,
		clientCAs:                   .ClientCAs,
		customCipherSuites:          .CustomCipherSuites,
		retransmitInterval:          ,
		log:                         .log,
		initialEpoch:                0,
		keyLogWriter:                .KeyLogWriter,
		sessionStore:                .SessionStore,
		ellipticCurves:              ,
		localGetCertificate:         .GetCertificate,
		localGetClientCertificate:   .GetClientCertificate,
		insecureSkipHelloVerify:     .InsecureSkipVerifyHello,
	}

	// rfc5246#section-7.4.3
	// In addition, the hash and signature algorithms MUST be compatible
	// with the key in the server's end-entity certificate.
	if ! {
		,  := .getCertificate(&ClientHelloInfo{})
		if  != nil && !errors.Is(, errNoCertificates) {
			return nil, 
		}
		.localCipherSuites = filterCipherSuitesForCertificate(, )
	}

	var  flightVal
	var  handshakeState

	if  != nil {
		if .state.isClient {
			 = flight5
		} else {
			 = flight6
		}
		 = handshakeFinished

		.state = *
	} else {
		if .state.isClient {
			 = flight1
		} else {
			 = flight0
		}
		 = handshakePreparing
	}
	// Do handshake
	if  := .handshake(, , , );  != nil {
		return nil, 
	}

	.log.Trace("Handshake Completed")

	return , nil
}

// Dial connects to the given network address and establishes a DTLS connection on top.
// Connection handshake will timeout using ConnectContextMaker in the Config.
// If you want to specify the timeout duration, use DialWithContext() instead.
func ( string,  *net.UDPAddr,  *Config) (*Conn, error) {
	,  := .connectContextMaker()
	defer ()

	return DialWithContext(, , , )
}

// Client establishes a DTLS connection over an existing connection.
// Connection handshake will timeout using ConnectContextMaker in the Config.
// If you want to specify the timeout duration, use ClientWithContext() instead.
func ( net.Conn,  *Config) (*Conn, error) {
	,  := .connectContextMaker()
	defer ()

	return ClientWithContext(, , )
}

// Server listens for incoming DTLS connections.
// Connection handshake will timeout using ConnectContextMaker in the Config.
// If you want to specify the timeout duration, use ServerWithContext() instead.
func ( net.Conn,  *Config) (*Conn, error) {
	,  := .connectContextMaker()
	defer ()

	return ServerWithContext(, , )
}

// DialWithContext connects to the given network address and establishes a DTLS connection on top.
func ( context.Context,  string,  *net.UDPAddr,  *Config) (*Conn, error) {
	,  := net.DialUDP(, nil, )
	if  != nil {
		return nil, 
	}
	return ClientWithContext(, , )
}

// ClientWithContext establishes a DTLS connection over an existing connection.
func ( context.Context,  net.Conn,  *Config) (*Conn, error) {
	switch {
	case  == nil:
		return nil, errNoConfigProvided
	case .PSK != nil && .PSKIdentityHint == nil:
		return nil, errPSKAndIdentityMustBeSetForClient
	}

	,  := createConn(, , true)
	if  != nil {
		return nil, 
	}

	return handshakeConn(, , , true, nil)
}

// ServerWithContext listens for incoming DTLS connections.
func ( context.Context,  net.Conn,  *Config) (*Conn, error) {
	if  == nil {
		return nil, errNoConfigProvided
	}
	,  := createConn(, , false)
	if  != nil {
		return nil, 
	}
	return handshakeConn(, , , false, nil)
}

// Read reads data from the connection.
func ( *Conn) ( []byte) ( int,  error) {
	if !.isHandshakeCompletedSuccessfully() {
		return 0, errHandshakeInProgress
	}

	select {
	case <-.readDeadline.Done():
		return 0, errDeadlineExceeded
	default:
	}

	for {
		select {
		case <-.readDeadline.Done():
			return 0, errDeadlineExceeded
		case ,  := <-.decrypted:
			if ! {
				return 0, io.EOF
			}
			switch val := .(type) {
			case ([]byte):
				if len() < len() {
					return 0, errBufferTooSmall
				}
				copy(, )
				return len(), nil
			case (error):
				return 0, 
			}
		}
	}
}

// Write writes len(p) bytes from p to the DTLS connection
func ( *Conn) ( []byte) (int, error) {
	if .isConnectionClosed() {
		return 0, ErrConnClosed
	}

	select {
	case <-.writeDeadline.Done():
		return 0, errDeadlineExceeded
	default:
	}

	if !.isHandshakeCompletedSuccessfully() {
		return 0, errHandshakeInProgress
	}

	return len(), .writePackets(.writeDeadline, []*packet{
		{
			record: &recordlayer.RecordLayer{
				Header: recordlayer.Header{
					Epoch:   .state.getLocalEpoch(),
					Version: protocol.Version1_2,
				},
				Content: &protocol.ApplicationData{
					Data: ,
				},
			},
			shouldEncrypt: true,
		},
	})
}

// Close closes the connection.
func ( *Conn) () error {
	 := .close(true) //nolint:contextcheck
	.handshakeLoopsFinished.Wait()
	return 
}

// ConnectionState returns basic DTLS details about the connection.
// Note that this replaced the `Export` function of v1.
func ( *Conn) () State {
	.lock.RLock()
	defer .lock.RUnlock()
	return *.state.clone()
}

// SelectedSRTPProtectionProfile returns the selected SRTPProtectionProfile
func ( *Conn) () (SRTPProtectionProfile, bool) {
	 := .state.getSRTPProtectionProfile()
	if  == 0 {
		return 0, false
	}

	return , true
}

func ( *Conn) ( context.Context,  []*packet) error {
	.lock.Lock()
	defer .lock.Unlock()

	var  [][]byte

	for ,  := range  {
		if ,  := .record.Content.(*handshake.Handshake);  {
			,  := .record.Marshal()
			if  != nil {
				return 
			}

			.log.Tracef("[handshake:%v] -> %s (epoch: %d, seq: %d)",
				srvCliStr(.state.isClient), .Header.Type.String(),
				.record.Header.Epoch, .Header.MessageSequence)
			.handshakeCache.push([recordlayer.HeaderSize:], .record.Header.Epoch, .Header.MessageSequence, .Header.Type, .state.isClient)

			,  := .processHandshakePacket(, )
			if  != nil {
				return 
			}
			 = append(, ...)
		} else {
			,  := .processPacket()
			if  != nil {
				return 
			}
			 = append(, )
		}
	}
	if len() == 0 {
		return nil
	}
	 := .compactRawPackets()

	for ,  := range  {
		if ,  := .nextConn.WriteContext(, );  != nil {
			return netError()
		}
	}

	return nil
}

func ( *Conn) ( [][]byte) [][]byte {
	// avoid a useless copy in the common case
	if len() == 1 {
		return 
	}

	 := make([][]byte, 0)
	 := make([]byte, 0)

	for ,  := range  {
		if len() > 0 && len()+len() >= .maximumTransmissionUnit {
			 = append(, )
			 = []byte{}
		}
		 = append(, ...)
	}

	 = append(, )

	return 
}

func ( *Conn) ( *packet) ([]byte, error) {
	 := .record.Header.Epoch
	for len(.state.localSequenceNumber) <= int() {
		.state.localSequenceNumber = append(.state.localSequenceNumber, uint64(0))
	}
	 := atomic.AddUint64(&.state.localSequenceNumber[], 1) - 1
	if  > recordlayer.MaxSequenceNumber {
		// RFC 6347 Section 4.1.0
		// The implementation must either abandon an association or rehandshake
		// prior to allowing the sequence number to wrap.
		return nil, errSequenceNumberOverflow
	}
	.record.Header.SequenceNumber = 

	,  := .record.Marshal()
	if  != nil {
		return nil, 
	}

	if .shouldEncrypt {
		var  error
		,  = .state.cipherSuite.Encrypt(.record, )
		if  != nil {
			return nil, 
		}
	}

	return , nil
}

func ( *Conn) ( *packet,  *handshake.Handshake) ([][]byte, error) {
	 := make([][]byte, 0)

	,  := .fragmentHandshake()
	if  != nil {
		return nil, 
	}
	 := .record.Header.Epoch
	for len(.state.localSequenceNumber) <= int() {
		.state.localSequenceNumber = append(.state.localSequenceNumber, uint64(0))
	}

	for ,  := range  {
		 := atomic.AddUint64(&.state.localSequenceNumber[], 1) - 1
		if  > recordlayer.MaxSequenceNumber {
			return nil, errSequenceNumberOverflow
		}

		 := &recordlayer.Header{
			Version:        .record.Header.Version,
			ContentType:    .record.Header.ContentType,
			ContentLen:     uint16(len()),
			Epoch:          .record.Header.Epoch,
			SequenceNumber: ,
		}

		,  := .Marshal()
		if  != nil {
			return nil, 
		}

		.record.Header = *

		 = append(, ...)
		if .shouldEncrypt {
			var  error
			,  = .state.cipherSuite.Encrypt(.record, )
			if  != nil {
				return nil, 
			}
		}

		 = append(, )
	}

	return , nil
}

func ( *Conn) ( *handshake.Handshake) ([][]byte, error) {
	,  := .Message.Marshal()
	if  != nil {
		return nil, 
	}

	 := make([][]byte, 0)

	 := splitBytes(, .maximumTransmissionUnit)
	if len() == 0 {
		 = [][]byte{
			{},
		}
	}

	 := 0
	for ,  := range  {
		 := len()

		 := &handshake.Header{
			Type:            .Header.Type,
			Length:          .Header.Length,
			MessageSequence: .Header.MessageSequence,
			FragmentOffset:  uint32(),
			FragmentLength:  uint32(),
		}

		 += 

		,  := .Marshal()
		if  != nil {
			return nil, 
		}

		 = append(, ...)
		 = append(, )
	}

	return , nil
}

var poolReadBuffer = sync.Pool{ //nolint:gochecknoglobals
	New: func() interface{} {
		 := make([]byte, inboundBufferSize)
		return &
	},
}

func ( *Conn) ( context.Context) error {
	,  := poolReadBuffer.Get().(*[]byte)
	if ! {
		return errFailedToAccessPoolReadBuffer
	}
	defer poolReadBuffer.Put()

	 := *
	,  := .nextConn.ReadContext(, )
	if  != nil {
		return netError()
	}

	,  := recordlayer.UnpackDatagram([:])
	if  != nil {
		return 
	}

	var  bool
	for ,  := range  {
		, ,  := .handleIncomingPacket(, , true)
		if  != nil {
			if  := .notify(, .Level, .Description);  != nil {
				if  == nil {
					 = 
				}
			}
		}
		if  {
			 = true
		}

		if  != nil {
			return 
		}
	}
	if  {
		 := make(chan struct{})
		select {
		case .handshakeRecv <- :
			// If the other party may retransmit the flight,
			// we should respond even if it not a new message.
			<-
		case <-.fsm.Done():
		}
	}
	return nil
}

func ( *Conn) ( context.Context) error {
	 := .encryptedPackets
	.encryptedPackets = nil

	for ,  := range  {
		, ,  := .handleIncomingPacket(, , false) // don't re-enqueue
		if  != nil {
			if  := .notify(, .Level, .Description);  != nil {
				if  == nil {
					 = 
				}
			}
		}
		var  *alertError
		if errors.As(, &) {
			if .IsFatalOrCloseNotify() {
				return 
			}
		} else if  != nil {
			return 
		}
	}
	return nil
}

func ( *Conn) ( []byte) bool {
	if len(.encryptedPackets) < maxAppDataPacketQueueSize {
		.encryptedPackets = append(.encryptedPackets, )
		return true
	}
	return false
}

func ( *Conn) ( context.Context,  []byte,  bool) (bool, *alert.Alert, error) { //nolint:gocognit
	 := &recordlayer.Header{}
	if  := .Unmarshal();  != nil {
		// Decode error must be silently discarded
		// [RFC6347 Section-4.1.2.7]
		.log.Debugf("discarded broken packet: %v", )
		return false, nil, nil
	}
	// Validate epoch
	 := .state.getRemoteEpoch()
	if .Epoch >  {
		if .Epoch > +1 {
			.log.Debugf("discarded future packet (epoch: %d, seq: %d)",
				.Epoch, .SequenceNumber,
			)
			return false, nil, nil
		}
		if  {
			if  := .enqueueEncryptedPackets();  {
				.log.Debug("received packet of next epoch, queuing packet")
			}
		}
		return false, nil, nil
	}

	// Anti-replay protection
	for len(.state.replayDetector) <= int(.Epoch) {
		.state.replayDetector = append(.state.replayDetector,
			replaydetector.New(.replayProtectionWindow, recordlayer.MaxSequenceNumber),
		)
	}
	,  := .state.replayDetector[int(.Epoch)].Check(.SequenceNumber)
	if ! {
		.log.Debugf("discarded duplicated packet (epoch: %d, seq: %d)",
			.Epoch, .SequenceNumber,
		)
		return false, nil, nil
	}

	// Decrypt
	if .Epoch != 0 {
		if .state.cipherSuite == nil || !.state.cipherSuite.IsInitialized() {
			if  {
				if  := .enqueueEncryptedPackets();  {
					.log.Debug("handshake not finished, queuing packet")
				}
			}
			return false, nil, nil
		}

		var  error
		,  = .state.cipherSuite.Decrypt()
		if  != nil {
			.log.Debugf("%s: decrypt failed: %s", srvCliStr(.state.isClient), )
			return false, nil, nil
		}
	}

	,  := .fragmentBuffer.push(append([]byte{}, ...))
	if  != nil {
		// Decode error must be silently discarded
		// [RFC6347 Section-4.1.2.7]
		.log.Debugf("defragment failed: %s", )
		return false, nil, nil
	} else if  {
		()
		for ,  := .fragmentBuffer.pop();  != nil; ,  = .fragmentBuffer.pop() {
			 := &handshake.Header{}
			if  := .Unmarshal();  != nil {
				.log.Debugf("%s: handshake parse failed: %s", srvCliStr(.state.isClient), )
				continue
			}
			.handshakeCache.push(, , .MessageSequence, .Type, !.state.isClient)
		}

		return true, nil, nil
	}

	 := &recordlayer.RecordLayer{}
	if  := .Unmarshal();  != nil {
		return false, &alert.Alert{Level: alert.Fatal, Description: alert.DecodeError}, 
	}

	switch content := .Content.(type) {
	case *alert.Alert:
		.log.Tracef("%s: <- %s", srvCliStr(.state.isClient), .String())
		var  *alert.Alert
		if .Description == alert.CloseNotify {
			// Respond with a close_notify [RFC5246 Section 7.2.1]
			 = &alert.Alert{Level: alert.Warning, Description: alert.CloseNotify}
		}
		()
		return false, , &alertError{}
	case *protocol.ChangeCipherSpec:
		if .state.cipherSuite == nil || !.state.cipherSuite.IsInitialized() {
			if  {
				if  := .enqueueEncryptedPackets();  {
					.log.Debugf("CipherSuite not initialized, queuing packet")
				}
			}
			return false, nil, nil
		}

		 := .Epoch + 1
		.log.Tracef("%s: <- ChangeCipherSpec (epoch: %d)", srvCliStr(.state.isClient), )

		if .state.getRemoteEpoch()+1 ==  {
			.setRemoteEpoch()
			()
		}
	case *protocol.ApplicationData:
		if .Epoch == 0 {
			return false, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, errApplicationDataEpochZero
		}

		()

		select {
		case .decrypted <- .Data:
		case <-.closed.Done():
		case <-.Done():
		}

	default:
		return false, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, fmt.Errorf("%w: %d", errUnhandledContextType, .ContentType())
	}
	return false, nil, nil
}

func ( *Conn) () <-chan chan struct{} {
	return .handshakeRecv
}

func ( *Conn) ( context.Context,  alert.Level,  alert.Description) error {
	if  == alert.Fatal && len(.state.SessionID) > 0 {
		// According to the RFC, we need to delete the stored session.
		// https://datatracker.ietf.org/doc/html/rfc5246#section-7.2
		if  := .fsm.cfg.sessionStore;  != nil {
			.log.Tracef("clean invalid session: %s", .state.SessionID)
			if  := .Del(.sessionKey());  != nil {
				return 
			}
		}
	}
	return .writePackets(, []*packet{
		{
			record: &recordlayer.RecordLayer{
				Header: recordlayer.Header{
					Epoch:   .state.getLocalEpoch(),
					Version: protocol.Version1_2,
				},
				Content: &alert.Alert{
					Level:       ,
					Description: ,
				},
			},
			shouldEncrypt: .isHandshakeCompletedSuccessfully(),
		},
	})
}

func ( *Conn) () {
	.handshakeCompletedSuccessfully.Store(struct{ bool }{true})
}

func ( *Conn) () bool {
	,  := .handshakeCompletedSuccessfully.Load().(struct{ bool })
	return .
}

func ( *Conn) ( context.Context,  *handshakeConfig,  flightVal,  handshakeState) error { //nolint:gocognit
	.fsm = newHandshakeFSM(&.state, .handshakeCache, , )

	 := make(chan struct{})
	,  := context.WithCancel(context.Background())
	.cancelHandshakeReader = 
	.onFlightState = func( flightVal,  handshakeState) {
		if  == handshakeFinished && !.isHandshakeCompletedSuccessfully() {
			.setHandshakeCompletedSuccessfully()
			close()
		}
	}

	,  := context.WithCancel(context.Background())
	.cancelHandshaker = 

	 := make(chan error, 1)

	.handshakeLoopsFinished.Add(2)

	// Handshake routine should be live until close.
	// The other party may request retransmission of the last flight to cope with packet drop.
	go func() {
		defer .handshakeLoopsFinished.Done()
		 := .fsm.Run(, , )
		if !errors.Is(, context.Canceled) {
			select {
			case  <- :
			default:
			}
		}
	}()
	go func() {
		defer func() {
			// Escaping read loop.
			// It's safe to close decrypted channnel now.
			close(.decrypted)

			// Force stop handshaker when the underlying connection is closed.
			()
		}()
		defer .handshakeLoopsFinished.Done()
		for {
			if  := .readAndBuffer();  != nil {
				var  *alertError
				if errors.As(, &) {
					if !.IsFatalOrCloseNotify() {
						if .isHandshakeCompletedSuccessfully() {
							// Pass the error to Read()
							select {
							case .decrypted <- :
							case <-.closed.Done():
							case <-.Done():
							}
						}
						continue // non-fatal alert must not stop read loop
					}
				} else {
					switch {
					case errors.Is(, context.DeadlineExceeded), errors.Is(, context.Canceled), errors.Is(, io.EOF), errors.Is(, net.ErrClosed):
					case errors.Is(, recordlayer.ErrInvalidPacketLength):
						// Decode error must be silently discarded
						// [RFC6347 Section-4.1.2.7]
						continue
					default:
						if .isHandshakeCompletedSuccessfully() {
							// Keep read loop and pass the read error to Read()
							select {
							case .decrypted <- :
							case <-.closed.Done():
							case <-.Done():
							}
							continue // non-fatal alert must not stop read loop
						}
					}
				}

				select {
				case  <- :
				default:
				}

				if  != nil {
					if .IsFatalOrCloseNotify() {
						_ = .close(false) //nolint:contextcheck
					}
				}
				if !.isConnectionClosed() && errors.Is(, context.Canceled) {
					.log.Trace("handshake timeouts - closing underline connection")
					_ = .close(false) //nolint:contextcheck
				}
				return
			}
		}
	}()

	select {
	case  := <-:
		()
		()
		.handshakeLoopsFinished.Wait()
		return .translateHandshakeCtxError()
	case <-.Done():
		()
		()
		.handshakeLoopsFinished.Wait()
		return .translateHandshakeCtxError(.Err())
	case <-:
		return nil
	}
}

func ( *Conn) ( error) error {
	if  == nil {
		return nil
	}
	if errors.Is(, context.Canceled) && .isHandshakeCompletedSuccessfully() {
		return nil
	}
	return &HandshakeError{Err: }
}

func ( *Conn) ( bool) error {
	.cancelHandshaker()
	.cancelHandshakeReader()

	if .isHandshakeCompletedSuccessfully() &&  {
		// Discard error from notify() to return non-error on the first user call of Close()
		// even if the underlying connection is already closed.
		_ = .notify(context.Background(), alert.Warning, alert.CloseNotify)
	}

	.closeLock.Lock()
	// Don't return ErrConnClosed at the first time of the call from user.
	 := .connectionClosedByUser
	if  {
		.connectionClosedByUser = true
	}
	 := .isConnectionClosed()
	.closed.Close()
	.closeLock.Unlock()

	if  {
		return ErrConnClosed
	}

	if  {
		return nil
	}

	return .nextConn.Close()
}

func ( *Conn) () bool {
	select {
	case <-.closed.Done():
		return true
	default:
		return false
	}
}

func ( *Conn) ( uint16) {
	.state.localEpoch.Store()
}

func ( *Conn) ( uint16) {
	.state.remoteEpoch.Store()
}

// LocalAddr implements net.Conn.LocalAddr
func ( *Conn) () net.Addr {
	return .nextConn.LocalAddr()
}

// RemoteAddr implements net.Conn.RemoteAddr
func ( *Conn) () net.Addr {
	return .nextConn.RemoteAddr()
}

func ( *Conn) () []byte {
	if .state.isClient {
		// As ServerName can be like 0.example.com, it's better to add
		// delimiter character which is not allowed to be in
		// neither address or domain name.
		return []byte(.nextConn.RemoteAddr().String() + "_" + .fsm.cfg.serverName)
	}
	return .state.SessionID
}

// SetDeadline implements net.Conn.SetDeadline
func ( *Conn) ( time.Time) error {
	.readDeadline.Set()
	return .SetWriteDeadline()
}

// SetReadDeadline implements net.Conn.SetReadDeadline
func ( *Conn) ( time.Time) error {
	.readDeadline.Set()
	// Read deadline is fully managed by this layer.
	// Don't set read deadline to underlying connection.
	return nil
}

// SetWriteDeadline implements net.Conn.SetWriteDeadline
func ( *Conn) ( time.Time) error {
	.writeDeadline.Set()
	// Write deadline is also fully managed by this layer.
	return nil
}