package quic

import (
	
	
	
	
	
	
	
	

	
	
	
	
)

// ErrNATTraversalNotNegotiated is returned by n0 QUIC NAT traversal operations
// when the n0_nat_traversal extension has not been negotiated.
var ErrNATTraversalNotNegotiated = errors.New("quic: n0 nat traversal not negotiated")

// ErrNATTraversalNotEnoughAddresses is returned when QNT is negotiated but a
// traversal round cannot start because either the local candidate set or the
// peer's ADD_ADDRESS set is empty.
var ErrNATTraversalNotEnoughAddresses = errors.New("quic: not enough nat traversal addresses")

// ErrNATTraversalTooManyAddresses is returned when a QNT address set is full.
var ErrNATTraversalTooManyAddresses = errors.New("quic: too many nat traversal addresses")

// NATTraversalCandidate is a local address the application believes is worth
// advertising to the peer for n0 QUIC NAT traversal. qng owns address-family
// canonicalization before any address is put on the wire.
type NATTraversalCandidate struct {
	Addr netip.AddrPort
}

type qntLocalState struct {
	mu                     sync.Mutex
	remoteOnce             sync.Once
	local                  []qntLocalAddress
	nextLocalAddressSeqNo  uint64
	nextRemoteAddressSeqNo uint64
	remote                 *qntRemoteAddressState
	round                  uint64
	pendingReachOut        []*wire.ReachOutFrame
	pendingProbes          []netip.AddrPort
	sentProbes             map[[8]byte]netip.AddrPort
	probeAttempts          map[netip.AddrPort]uint8
	validatedProbes        []netip.AddrPort
	retryAttempt           uint8
	nextRetry              monotime.Time
	// remoteReady is closed once the remote candidate set first becomes
	// non-empty; created lazily under mu.
	remoteReady     chan struct{}
	remoteReadyDone bool
}

func ( *qntLocalState) () chan struct{} {
	if .remoteReady == nil {
		.remoteReady = make(chan struct{})
	}
	return .remoteReady
}

func ( *qntLocalState) () {
	if .remoteReadyDone {
		return
	}
	.remoteReadyDone = true
	close(.remoteReadyChLocked())
}

type qntLocalAddress struct {
	addr netip.AddrPort
	seq  uint64
}

const qntMaxProbeAttempts = 9

const qntSyntheticRemoteSeqBase = 1 << 62

// AddNATTraversalAddress adds a local QNT candidate address.
func ( *Conn) ( netip.AddrPort) error {
	if !.qntAPINegotiated() {
		return ErrNATTraversalNotNegotiated
	}
	 = canonicalNATTraversalAddr()
	if !.IsValid() {
		return nil
	}
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	if slices.ContainsFunc(.local, func( qntLocalAddress) bool {
		return .addr == 
	}) {
		return nil
	}
	if len(.local) >= .qntLocalAddressLimit() {
		return ErrNATTraversalTooManyAddresses
	}
	 := .nextLocalAddressSeqNo
	.nextLocalAddressSeqNo++
	.local = append(.local, qntLocalAddress{addr: , seq: })
	if .perspective == protocol.PerspectiveServer {
		.queueLocalAddAddressFrame(, )
	}
	return nil
}

// RemoveNATTraversalAddress removes a local QNT candidate address.
func ( *Conn) ( netip.AddrPort) error {
	if !.qntAPINegotiated() {
		return ErrNATTraversalNotNegotiated
	}
	 = canonicalNATTraversalAddr()
	if !.IsValid() {
		return nil
	}
	 := .qntLocalState()
	.mu.Lock()
	 := slices.IndexFunc(.local, func( qntLocalAddress) bool {
		return .addr == 
	})
	if  < 0 {
		.mu.Unlock()
		return nil
	}
	 := .local[].seq
	.local = slices.Delete(.local, , +1)
	.mu.Unlock()
	if .perspective == protocol.PerspectiveServer {
		.queueLocalRemoveAddressFrame()
	}
	return nil
}

// AddRemoteNATTraversalAddress adds a remote QNT candidate learned from an
// authenticated address source outside the peer's ADD_ADDRESS frames, such as a
// dialed endpoint ticket.
func ( *Conn) ( netip.AddrPort) error {
	if !.qntAPINegotiated() {
		return ErrNATTraversalNotNegotiated
	}
	 = canonicalNATTraversalAddr()
	if !.IsValid() {
		return nil
	}
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	for ,  := range .remote.addresses() {
		if  ==  {
			return nil
		}
	}
	if len(.remote.addrs) >= .remote.max {
		return ErrNATTraversalTooManyAddresses
	}
	if .nextRemoteAddressSeqNo == 0 {
		.nextRemoteAddressSeqNo = qntSyntheticRemoteSeqBase
	}
	 := .nextRemoteAddressSeqNo
	.nextRemoteAddressSeqNo++
	.remote.addrs[] = 
	.signalRemoteReadyLocked()
	return nil
}

// InitiateNATTraversalRound starts one client-side QNT round. qng queues
// REACH_OUT frames, owns NAT probe retry scheduling, matches PATH_RESPONSE
// frames, and opens validated four-tuples as multipath paths. The returned
// addresses are informational; qng, not socket, owns probing.
func ( *Conn) ( context.Context) ([]netip.AddrPort, error) {
	if !.qntAPINegotiated() {
		return nil, ErrNATTraversalNotNegotiated
	}
	 := .qntLocalState()
	.mu.Lock()
	 := .remote.addresses()
	if len(.local) == 0 || len() == 0 {
		.mu.Unlock()
		return nil, ErrNATTraversalNotEnoughAddresses
	}
	.round++
	.pendingReachOut = .pendingReachOut[:0]
	.pendingProbes = append(.pendingProbes[:0], ...)
	clear(.sentProbes)
	.retryAttempt = 0
	.nextRetry = 0
	.probeAttempts = make(map[netip.AddrPort]uint8, len())
	for ,  := range  {
		.probeAttempts[] = qntMaxProbeAttempts - 1
	}
	for ,  := range .local {
		.pendingReachOut = append(.pendingReachOut, &wire.ReachOutFrame{
			Round: .round,
			Addr:  .addr.Addr(),
			Port:  .addr.Port(),
		})
	}
	.mu.Unlock()
	.qntQueuePendingReachOutFrames()
	return , nil
}

// NATTraversalRemoteAddrsReady returns a channel closed once this connection
// first knows a remote NAT traversal candidate (peer ADD_ADDRESS frame or
// [Conn.AddRemoteNATTraversalAddress]) — the earliest moment a QNT round can
// start. It never closes when no candidate ever arrives, e.g. on the server
// side of QNT, which receives no ADD_ADDRESS.
func ( *Conn) () <-chan struct{} {
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	return .remoteReadyChLocked()
}

// NATTraversalAddresses returns the remote ADD_ADDRESS set known to qng.
func ( *Conn) () ([]netip.AddrPort, error) {
	if !.qntAPINegotiated() {
		return nil, ErrNATTraversalNotNegotiated
	}
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	return .remote.addresses(), nil
}

func ( *Conn) () *qntLocalState {
	.qnt.remoteOnce.Do(func() {
		.qnt.remote = newQNTRemoteAddressState(.qntRemoteAddressLimit())
	})
	return &.qnt
}

func ( *Conn) () []netip.AddrPort {
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	 := make([]netip.AddrPort, 0, len(.local))
	for ,  := range .local {
		 = append(, .addr)
	}
	return 
}

func ( *Conn) () []*wire.ReachOutFrame {
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	return cloneReachOutFrames(.pendingReachOut)
}

func ( *Conn) () bool {
	if .framer == nil {
		return false
	}
	 := .qntLocalState()
	.mu.Lock()
	 := cloneReachOutFrames(.pendingReachOut)
	.pendingReachOut = .pendingReachOut[:0]
	.mu.Unlock()
	for ,  := range  {
		if  != nil {
			.queueControlFrame()
		}
	}
	return len() > 0
}

func ( *Conn) () []netip.AddrPort {
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	return slices.Clone(.pendingProbes)
}

func qntProbeUDPAddr( netip.AddrPort) *net.UDPAddr {
	 = canonicalNATTraversalAddr()
	if !validQNTProbeAddr() {
		return nil
	}
	return net.UDPAddrFromAddrPort()
}

func validQNTProbeAddr( netip.AddrPort) bool {
	 = canonicalNATTraversalAddr()
	return .IsValid() && .Port() != 0
}

func ( *Conn) ( [8]byte,  netip.AddrPort) {
	 = canonicalNATTraversalAddr()
	if !.IsValid() {
		return
	}
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	if .sentProbes == nil {
		.sentProbes = make(map[[8]byte]netip.AddrPort)
	}
	.sentProbes[] = 
}

func ( *Conn) () (netip.AddrPort, ackhandler.Frame, bool, error) {
	 := .qntLocalState()
	.mu.Lock()
	 := len(.pendingProbes) == 0
	.mu.Unlock()
	if  {
		return netip.AddrPort{}, ackhandler.Frame{}, false, nil
	}

	var  [8]byte
	if ,  := rand.Read([:]);  != nil {
		return netip.AddrPort{}, ackhandler.Frame{}, false, 
	}
	, ,  := .qntPopPendingProbe()
	if ! {
		return netip.AddrPort{}, ackhandler.Frame{}, false, nil
	}
	return , ackhandler.Frame{Frame: }, true, nil
}

func ( *Conn) ( [8]byte) (netip.AddrPort, *wire.PathChallengeFrame, bool) {
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	if len(.pendingProbes) == 0 {
		return netip.AddrPort{}, nil, false
	}
	 := .pendingProbes[0]
	.pendingProbes = .pendingProbes[1:]
	 = canonicalNATTraversalAddr()
	if !.IsValid() || .Port() == 0 {
		return netip.AddrPort{}, nil, false
	}
	if .sentProbes == nil {
		.sentProbes = make(map[[8]byte]netip.AddrPort)
	}
	.sentProbes[] = 
	return , &wire.PathChallengeFrame{Data: }, true
}

func ( *Conn) ( protocol.ConnectionID,  protocol.Version) (netip.AddrPort, shortHeaderPacket, *packetBuffer, bool, error) {
	if .packer == nil {
		return netip.AddrPort{}, shortHeaderPacket{}, nil, false, nil
	}
	, , ,  := .qntNextProbeFrame()
	if  != nil || ! {
		return netip.AddrPort{}, shortHeaderPacket{}, nil, false, 
	}
	if qntProbeUDPAddr() == nil {
		return netip.AddrPort{}, shortHeaderPacket{}, nil, false, nil
	}
	, ,  := .packer.PackPathProbePacket(, []ackhandler.Frame{}, )
	if  != nil {
		return netip.AddrPort{}, shortHeaderPacket{}, nil, false, 
	}
	return , , , true, nil
}

func ( *Conn) ( *wire.PathResponseFrame,  netip.AddrPort) (netip.AddrPort, bool) {
	if  == nil {
		return netip.AddrPort{}, false
	}
	 = canonicalNATTraversalAddr()
	if !.IsValid() {
		return netip.AddrPort{}, false
	}
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	,  := .sentProbes[.Data]
	if ! ||  !=  {
		return netip.AddrPort{}, false
	}
	delete(.sentProbes, .Data)
	delete(.probeAttempts, )
	.pendingProbes = slices.DeleteFunc(.pendingProbes, func( netip.AddrPort) bool {
		return  == 
	})
	if !qntHasRetryableProbeLocked() {
		.nextRetry = 0
	}
	return , true
}

func ( *Conn) ( netip.AddrPort) bool {
	 = canonicalNATTraversalAddr()
	if !.IsValid() || .Port() == 0 {
		return false
	}
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	if slices.Contains(.validatedProbes, ) {
		return false
	}
	.validatedProbes = append(.validatedProbes, )
	return true
}

func ( *Conn) ( netip.AddrPort) bool {
	 = canonicalNATTraversalAddr()
	if !.IsValid() {
		return false
	}
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	if slices.Contains(.pendingProbes, ) || slices.Contains(.validatedProbes, ) {
		return true
	}
	for ,  := range .sentProbes {
		if  ==  {
			return true
		}
	}
	if .multipathOut != nil {
		for ,  := range .multipathOut.paths {
			if .qntRoute ==  {
				return true
			}
		}
	}
	return false
}

func ( *Conn) () (netip.AddrPort, bool) {
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	if len(.validatedProbes) == 0 {
		return netip.AddrPort{}, false
	}
	 := .validatedProbes[0]
	.validatedProbes = .validatedProbes[1:]
	return , true
}

func ( *Conn) () (netip.AddrPort, bool) {
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	if len(.validatedProbes) == 0 {
		return netip.AddrPort{}, false
	}
	return .validatedProbes[0], true
}

func ( *Conn) ( uint64,  netip.AddrPort) {
	if .framer == nil {
		return
	}
	.queueControlFrame(&wire.AddAddressFrame{
		SeqNo: ,
		Addr:  .Addr(),
		Port:  .Port(),
	})
}

func ( *Conn) ( uint64) {
	if .framer == nil {
		return
	}
	.queueControlFrame(&wire.RemoveAddressFrame{SeqNo: })
}

func cloneReachOutFrames( []*wire.ReachOutFrame) []*wire.ReachOutFrame {
	 := make([]*wire.ReachOutFrame, len())
	for ,  := range  {
		if  == nil {
			continue
		}
		 := *
		[] = &
	}
	return 
}

func ( *Conn) ( netip.AddrPort) error {
	 = canonicalNATTraversalAddr()
	if !.IsValid() {
		return nil
	}
	return .addRemoteNATTraversalAddressFrame(&wire.AddAddressFrame{
		SeqNo: 0,
		Addr:  .Addr(),
		Port:  .Port(),
	})
}

func ( *Conn) ( *wire.AddAddressFrame) error {
	return .addRemoteNATTraversalAddressFrame()
}

func ( *Conn) ( *wire.ReachOutFrame) error {
	if !.qntAPINegotiated() {
		return ErrNATTraversalNotNegotiated
	}
	if  == nil {
		return nil
	}
	return .qntQueueReachOutProbe()
}

func ( *Conn) ( *wire.ReachOutFrame) error {
	 := canonicalAddrPort(.Addr, .Port)
	if !.IsValid() || .Port() == 0 {
		return nil
	}
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	if .Round < .round {
		return nil
	}
	if .Round > .round {
		.round = .Round
		.pendingProbes = .pendingProbes[:0]
		clear(.sentProbes)
		clear(.probeAttempts)
		.validatedProbes = .validatedProbes[:0]
		.retryAttempt = 0
		.nextRetry = 0
	}
	if qntHasProbeLocked(, ) {
		return nil
	}
	if qntProbeCountLocked() >= int(.qntRemoteAddressLimit()) {
		return ErrNATTraversalTooManyAddresses
	}
	.pendingProbes = append(.pendingProbes, )
	if .probeAttempts == nil {
		.probeAttempts = make(map[netip.AddrPort]uint8)
	}
	.probeAttempts[] = qntMaxProbeAttempts - 1
	return nil
}

func ( *Conn) () bool {
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	var  bool
	for ,  := range .probeAttempts {
		if !qntCanRetryProbeLocked(, , ) {
			continue
		}
		.probeAttempts[] =  - 1
		.pendingProbes = append(.pendingProbes, )
		 = true
	}
	if  {
		.retryAttempt++
		.nextRetry = 0
	}
	return 
}

func ( *Conn) ( monotime.Time,  time.Duration) (monotime.Time, bool) {
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	if !qntHasRetryableProbeLocked() {
		.nextRetry = 0
		return 0, false
	}
	 := qntRetryDelay(.retryAttempt, )
	if  <= 0 {
		.nextRetry = 0
		return 0, false
	}
	.nextRetry = .Add()
	return .nextRetry, true
}

func ( *Conn) () monotime.Time {
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	return .nextRetry
}

func ( *Conn) ( monotime.Time) bool {
	 := .qntNextRetryDeadline()
	if .IsZero() || .Before() {
		return false
	}
	if !.qntQueueProbeRetries() {
		.qntClearNextRetry()
		return false
	}
	return true
}

func ( *Conn) () {
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	.nextRetry = 0
}

func ( *Conn) () uint8 {
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	return .retryAttempt
}

func qntHasRetryableProbeLocked( *qntLocalState) bool {
	for ,  := range .probeAttempts {
		if qntCanRetryProbeLocked(, , ) {
			return true
		}
	}
	return false
}

func qntCanRetryProbeLocked( *qntLocalState,  netip.AddrPort,  uint8) bool {
	return  > 0 &&
		!slices.Contains(.pendingProbes, ) &&
		!slices.Contains(.validatedProbes, ) &&
		qntHasSentProbeLocked(, )
}

func qntHasSentProbeLocked( *qntLocalState,  netip.AddrPort) bool {
	for ,  := range .sentProbes {
		if  ==  {
			return true
		}
	}
	return false
}

func qntHasProbeLocked( *qntLocalState,  netip.AddrPort) bool {
	if slices.Contains(.pendingProbes, ) || slices.Contains(.validatedProbes, ) {
		return true
	}
	for ,  := range .sentProbes {
		if  ==  {
			return true
		}
	}
	return false
}

// qntKnownCandidate reports whether source is a QNT candidate this connection
// is aware of: a probe target (pending/sent/validated) or an advertised remote
// address. A PATH_CHALLENGE arriving from such an address is a QNT probe on a
// new candidate 4-tuple and must be answered on that same 4-tuple so the peer
// can validate it, independent of RFC 9000 migration perspective rules.
func ( *Conn) ( netip.AddrPort) bool {
	 = canonicalNATTraversalAddr()
	if !.IsValid() {
		return false
	}
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	if qntHasProbeLocked(, ) {
		return true
	}
	for ,  := range .remote.addresses() {
		if  ==  {
			return true
		}
	}
	return false
}

func qntProbeCountLocked( *qntLocalState) int {
	 := len(.pendingProbes) + len(.validatedProbes)
	for ,  := range .sentProbes {
		if !slices.Contains(.pendingProbes, ) && !slices.Contains(.validatedProbes, ) {
			++
		}
	}
	return 
}

func ( *Conn) ( *wire.RemoveAddressFrame) error {
	return .removeRemoteNATTraversalAddressFrame()
}

func ( *Conn) ( *wire.AddAddressFrame) error {
	if !.qntAPINegotiated() {
		return ErrNATTraversalNotNegotiated
	}
	if  == nil {
		return nil
	}
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	, ,  := .remote.add()
	if  == nil && len(.remote.addresses()) > 0 {
		.signalRemoteReadyLocked()
	}
	return 
}

func ( *Conn) ( *wire.RemoveAddressFrame) error {
	if !.qntAPINegotiated() {
		return ErrNATTraversalNotNegotiated
	}
	if  == nil {
		return nil
	}
	 := .qntLocalState()
	.mu.Lock()
	defer .mu.Unlock()
	.remote.remove()
	return nil
}

func canonicalNATTraversalAddr( netip.AddrPort) netip.AddrPort {
	if !.IsValid() {
		return netip.AddrPort{}
	}
	return netip.AddrPortFrom(.Addr().Unmap(), .Port())
}

func ( *Conn) () bool {
	if  == nil || .config == nil {
		return false
	}
	return .qntNegotiated()
}

func ( *Conn) () uint8 {
	if  == nil || .config == nil {
		return 0
	}
	if  := maxRemoteNATTraversalAddressesParam(.config.MaxRemoteNATTraversalAddresses);  != nil {
		return *
	}
	return 0
}

func ( *Conn) () int {
	if  == nil {
		return 0
	}
	 := .peerParams.Load()
	if  == nil || .MaxRemoteNATTraversalAddresses == nil {
		return 0
	}
	return int(*.MaxRemoteNATTraversalAddresses)
}