package iroh

import (
	
	
	
	
	
	
	
	

	quic 
	
	
	
)

// Side reports whether a [Conn] was dialed locally or accepted from a peer.
type Side int

const (
	// SideClient is a connection this endpoint dialed.
	SideClient Side = iota
	// SideServer is a connection this endpoint accepted.
	SideServer
)

func ( Side) () string {
	switch  {
	case SideClient:
		return "client"
	case SideServer:
		return "server"
	default:
		return "unknown"
	}
}

// Stream is a bidirectional stream.
type Stream struct {
	s *quic.Stream
}

// SendStream is the send half of a unidirectional stream.
type SendStream struct {
	s *quic.SendStream
}

// ReceiveStream is the receive half of a unidirectional stream.
type ReceiveStream struct {
	s *quic.ReceiveStream
}

// Read reads data from s.
func ( *Stream) ( []byte) (int, error) { return .s.Read() }

// Write writes data to s.
func ( *Stream) ( []byte) (int, error) { return .s.Write() }

// ReadFrom implements [io.ReaderFrom]. See [SendStream.ReadFrom].
func ( *Stream) ( io.Reader) (int64, error) { return .s.ReadFrom() }

// Writev writes the buffers in order as one write episode.
// See [SendStream.Writev].
func ( *Stream) ( *net.Buffers) (int64, error) { return .s.Writev() }

// Close closes the send side of s.
func ( *Stream) () error { return .s.Close() }

// SetDeadline sets the read and write deadlines for s.
func ( *Stream) ( time.Time) error { return .s.SetDeadline() }

// SetReadDeadline sets the read deadline for s.
func ( *Stream) ( time.Time) error { return .s.SetReadDeadline() }

// SetWriteDeadline sets the write deadline for s.
func ( *Stream) ( time.Time) error { return .s.SetWriteDeadline() }

// CancelRead aborts receiving on s with code.
func ( *Stream) ( uint64) { .s.CancelRead(quic.StreamErrorCode()) }

// CancelWrite aborts sending on s with code.
func ( *Stream) ( uint64) { .s.CancelWrite(quic.StreamErrorCode()) }

// Context is cancelled when s is closed.
func ( *Stream) () context.Context { return .s.Context() }

// Write writes data to s.
func ( *SendStream) ( []byte) (int, error) { return .s.Write() }

// ReadFrom implements [io.ReaderFrom]. It reads from r until EOF or error,
// writing to the stream in buffer-sized chunks under a single lock
// acquisition per chunk; [io.Copy] and every caller built on it picks this
// up with no signature change. Data is copied into stream-owned storage
// before each chunk write returns, so r's buffer is never retained.
func ( *SendStream) ( io.Reader) (int64, error) { return .s.ReadFrom() }

// Writev writes the buffers in order as one write episode, amortizing the
// per-call lock and bookkeeping across the vector. It returns the total
// number of bytes written and advances bufs to reflect exactly what was
// consumed, including a partially written element, so the caller can resume
// after a short write. The stream copies data into owned storage before
// Writev returns; the caller may reuse the underlying slices immediately.
// The delivered byte stream is identical to the equivalent sequence of
// Write calls; per-vector atomicity is not promised.
//
// To send a [net.Buffers], call Writev directly: net.Buffers implements
// [io.WriterTo], which io.Copy prefers over io.ReaderFrom, so
// io.Copy(stream, &bufs) degrades to one Write call per element and never
// batches.
//
// Vectored submission measured at least as fast as the equivalent
// sequence of Write calls at every tested batch depth.
func ( *SendStream) ( *net.Buffers) (int64, error) { return .s.Writev() }

// Close closes s.
func ( *SendStream) () error { return .s.Close() }

// SetWriteDeadline sets the write deadline for s.
func ( *SendStream) ( time.Time) error { return .s.SetWriteDeadline() }

// CancelWrite aborts sending on s with code.
func ( *SendStream) ( uint64) { .s.CancelWrite(quic.StreamErrorCode()) }

// Context is cancelled when s is closed.
func ( *SendStream) () context.Context { return .s.Context() }

// Read reads data from s.
func ( *ReceiveStream) ( []byte) (int, error) { return .s.Read() }

// SetReadDeadline sets the read deadline for s.
func ( *ReceiveStream) ( time.Time) error { return .s.SetReadDeadline() }

// CancelRead aborts receiving on s with code.
func ( *ReceiveStream) ( uint64) { .s.CancelRead(quic.StreamErrorCode()) }

// Conn is an established connection to a remote iroh endpoint. The peer's
// identity is authenticated by the RFC 7250 handshake and available via
// [Conn.RemoteID].
type Conn struct {
	qc       *quic.Conn
	remoteID key.EndpointID
	alpn     string
	side     Side
	stableID uint64

	// resolveOnce lazily populates remoteID and alpn from the completed
	// handshake for a 0-RTT accept conn, whose verified identity is not known
	// until the handshake finishes. It is nil for conns whose identity is set at
	// construction. RemoteID and ALPN call resolveIdentity before reading.
	resolveOnce sync.Once
	resolve     func() (key.EndpointID, string)

	pathState *socket.RemoteStateActor
	pathConn  *connAdapter
}

// resolveIdentity populates remoteID and alpn from the completed handshake the
// first time it is called, for a 0-RTT accept conn. It is a no-op for conns
// whose identity was set at construction.
func ( *Conn) () {
	if .resolve == nil {
		return
	}
	.resolveOnce.Do(func() {
		.remoteID, .alpn = .resolve()
	})
}

// ConnStats is a snapshot of connection statistics.
type ConnStats struct {
	// MinRTT is the minimum RTT observed on the active path.
	MinRTT time.Duration
	// LatestRTT is the most recent RTT sample observed on the active path.
	LatestRTT time.Duration
	// SmoothedRTT is an exponentially weighted moving average of RTT samples.
	SmoothedRTT time.Duration
	// MeanDeviation estimates variation in RTT samples.
	MeanDeviation time.Duration

	// BytesSent is the number of bytes sent on the underlying connection,
	// including retransmissions.
	BytesSent uint64
	// PacketsSent is the number of packets sent on the underlying connection,
	// including packets later declared lost.
	PacketsSent uint64
	// BytesReceived is the number of bytes received on the underlying
	// connection, including duplicate stream data.
	BytesReceived uint64
	// PacketsReceived is the number of packets received on the underlying
	// connection, including packets that were not processable.
	PacketsReceived uint64
	// BytesLost is the number of bytes declared lost on the underlying
	// connection. It may decrease if packets declared lost are later received.
	BytesLost uint64
	// PacketsLost is the number of packets declared lost on the underlying
	// connection. It may decrease if packets declared lost are later received.
	PacketsLost uint64
}

// PathInfo is a snapshot of one currently open network path for a connection.
type PathInfo struct {
	// ID is the QUIC multipath PathID when known. The initial path has ID 0.
	ID uint32
	// Validated reports whether the path can carry non-probing application data.
	Validated bool
	// Addr is the path's transport address, when HasAddr is true.
	Addr netaddr.TransportAddr
	// HasAddr reports whether Addr is known.
	HasAddr bool
	// RTT is the path's smoothed round-trip time, when HasRTT is true.
	RTT time.Duration
	// HasRTT reports whether RTT was observed for this path.
	HasRTT bool
	// BytesInFlight is the path's current application-data bytes in flight,
	// when HasBytesInFlight is true.
	BytesInFlight uint64
	// HasBytesInFlight reports whether BytesInFlight was observed for this path.
	HasBytesInFlight bool
	// BytesSent is the cumulative 1-RTT/0-RTT application-data packet bytes
	// sent on this path, when HasBytesSent is true. It excludes
	// Initial/Handshake packets and UDP framing overhead.
	BytesSent uint64
	// HasBytesSent reports whether BytesSent was observed for this path.
	HasBytesSent bool
	// BytesReceived is the cumulative 1-RTT/0-RTT application-data packet bytes
	// received on this path, when HasBytesReceived is true. It excludes
	// Initial/Handshake packets and UDP framing overhead.
	BytesReceived uint64
	// HasBytesReceived reports whether BytesReceived was observed for this path.
	HasBytesReceived bool
	// CongestionWindow is the path's current congestion window, when
	// HasCongestionWindow is true.
	CongestionWindow uint64
	// HasCongestionWindow reports whether CongestionWindow was observed for this
	// path.
	HasCongestionWindow bool
	// LostPackets is the number of application-data packets declared lost on
	// this path, when HasLoss is true.
	LostPackets uint64
	// LostBytes is the number of application-data bytes declared lost on this
	// path, when HasLoss is true.
	LostBytes uint64
	// HasLoss reports whether LostPackets and LostBytes were observed for this
	// path.
	HasLoss bool
	// Selected reports whether this path is currently selected for application
	// data transmission.
	Selected bool
	// Relayed reports whether this path uses a relay server.
	Relayed bool
}

func newConn( *quic.Conn,  key.EndpointID,  string,  Side,  uint64) (*Conn, error) {
	return &Conn{qc: , remoteID: , alpn: , side: , stableID: }, nil
}

// Incoming is an incoming connection attempt accepted by an [Endpoint]. Call
// Accept to continue the handshake, or Refuse/Ignore to close it.
type Incoming struct {
	ep     *Endpoint
	qc     *quic.Conn
	remote net.Addr
}

// Accept accepts the incoming connection and returns an [Accepting] handle.
func ( *Incoming) () (*Accepting, error) {
	if  == nil || .qc == nil {
		return nil, errors.New("iroh: nil incoming connection")
	}
	return &Accepting{ep: .ep, qc: .qc}, nil
}

// Refuse closes the incoming connection.
func ( *Incoming) () {
	if  != nil && .qc != nil {
		.qc.CloseWithError(0, "refused")
	}
}

// Ignore closes the incoming connection without waiting for completion.
func ( *Incoming) () {
	if  != nil && .qc != nil {
		.qc.CloseWithError(0, "")
	}
}

// RemoteAddr returns the transport address of the incoming connection.
func ( *Incoming) () net.Addr {
	if  == nil {
		return nil
	}
	if .remote != nil {
		return .remote
	}
	if .qc == nil {
		return nil
	}
	return .qc.RemoteAddr()
}

// RemoteAddrValidated reports whether qng has validated the remote address.
func ( *Incoming) () bool {
	if  == nil {
		return false
	}
	if .qc == nil {
		return false
	}
	return .qc.RemoteAddrValidated()
}

// LocalAddr returns the local transport address the incoming connection used.
func ( *Incoming) () net.Addr {
	if  == nil || .qc == nil {
		return nil
	}
	return .qc.LocalAddr()
}

// Accepting is an accepted incoming connection whose handshake may still be in
// progress. Call Connection to wait for the verified [Conn].
type Accepting struct {
	ep *Endpoint
	qc *quic.Conn
}

// ALPN waits for the handshake to complete and returns the negotiated ALPN.
func ( *Accepting) ( context.Context) (string, error) {
	if  == nil || .qc == nil {
		return "", errors.New("iroh: nil accepting connection")
	}
	select {
	case <-.qc.HandshakeComplete():
		return .qc.ConnectionState().TLS.NegotiatedProtocol, nil
	default:
	}
	select {
	case <-.qc.HandshakeComplete():
	case <-.qc.Context().Done():
		// HandshakeComplete only closes on success; unblock when the
		// connection attempt dies before finishing its handshake.
		return "", fmt.Errorf("%w: %w", ErrConnClosedDuringHandshake, context.Cause(.qc.Context()))
	case <-.Done():
		.qc.CloseWithError(0, "")
		return "", .Err()
	}
	return .qc.ConnectionState().TLS.NegotiatedProtocol, nil
}

// RemoteAddr returns the transport address of the connection.
func ( *Accepting) () net.Addr {
	if  == nil || .qc == nil {
		return nil
	}
	return .qc.RemoteAddr()
}

// Connection waits for the handshake, verifies the peer id, registers the
// connection with the endpoint, runs handshake hooks, and returns an
// established [Conn].
func ( *Accepting) ( context.Context) (*Conn, error) {
	if  == nil || .qc == nil {
		return nil, errors.New("iroh: nil accepting connection")
	}
	return .ep.finishAccepting(, .qc)
}

// RemoteID returns the verified endpoint id of the peer. For a connection
// obtained from [Accepting.Into0RTT] it is the zero id until the handshake
// completes; wait on [Conn.HandshakeComplete] before relying on it.
func ( *Conn) () key.EndpointID {
	.resolveIdentity()
	return .remoteID
}

// ALPN returns the negotiated ALPN protocol. For a connection obtained from
// [Accepting.Into0RTT] it is empty until the handshake completes.
func ( *Conn) () string {
	.resolveIdentity()
	return .alpn
}

// Side reports whether this connection was dialed or accepted.
func ( *Conn) () Side { return .side }

// StableID returns an endpoint-local identifier for this connection. It is
// fixed for the connection lifetime, even when the transport path changes.
func ( *Conn) () uint64 { return .stableID }

// Stats returns a snapshot of connection statistics.
func ( *Conn) () ConnStats {
	return connStats(.qc.ConnectionStats())
}

func connStats( quic.ConnectionStats) ConnStats {
	return ConnStats{
		MinRTT:          .MinRTT,
		LatestRTT:       .LatestRTT,
		SmoothedRTT:     .SmoothedRTT,
		MeanDeviation:   .MeanDeviation,
		BytesSent:       .BytesSent,
		PacketsSent:     .PacketsSent,
		BytesReceived:   .BytesReceived,
		PacketsReceived: .PacketsReceived,
		BytesLost:       .BytesLost,
		PacketsLost:     .PacketsLost,
	}
}

// Paths returns a snapshot of the connection's currently open network paths.
func ( *Conn) () []PathInfo {
	.resolveIdentity()
	if .pathState != nil && .pathConn != nil {
		return pathInfosFromSocket(.pathState.PathInfos(.pathConn))
	}
	return pathInfosFromSocket((&connAdapter{qc: .qc}).Paths())
}

// WatchPaths returns a stream of path snapshots for this connection.
//
// The first value is the current snapshot. Later values are sent when the
// endpoint observes a path change for the peer. The stream ends when ctx is
// done, the connection closes, or path observation is unavailable.
func ( *Conn) ( context.Context) (<-chan []PathInfo, error) {
	.resolveIdentity()
	if .pathState == nil || .pathConn == nil {
		return nil, errors.New("iroh: path observation not available")
	}
	,  := .pathState.PathEvents()
	 := make(chan []PathInfo, 1)
	go func() {
		defer ()
		defer close()
		 := func() bool {
			 := .Paths()
			select {
			case  <- :
				return true
			case <-.Done():
				return false
			case <-.Context().Done():
				return false
			}
		}
		if !() {
			return
		}
		for {
			select {
			case <-.Done():
				return
			case <-.Context().Done():
				return
			case ,  := <-:
				if ! {
					return
				}
				if !() {
					return
				}
			}
		}
	}()
	return , nil
}

func pathInfosFromSocket( []socket.PathInfo) []PathInfo {
	if len() == 0 {
		return nil
	}
	 := make([]PathInfo, 0, len())
	for ,  := range  {
		 := PathInfo{
			ID:                  .ID,
			Validated:           .Validated,
			HasAddr:             .HasAddr,
			RTT:                 .RTT,
			HasRTT:              .HasRTT,
			BytesInFlight:       .BytesInFlight,
			HasBytesInFlight:    .HasBytesInFlight,
			BytesSent:           .BytesSent,
			HasBytesSent:        .HasBytesSent,
			BytesReceived:       .BytesReceived,
			HasBytesReceived:    .HasBytesReceived,
			CongestionWindow:    .CongestionWindow,
			HasCongestionWindow: .HasCongestionWindow,
			LostPackets:         .LostPackets,
			LostBytes:           .LostBytes,
			HasLoss:             .HasLoss,
			Selected:            .Selected,
		}
		if .HasAddr {
			.Addr, .Relayed = transportAddrFromSocket(.Addr)
		}
		 = append(, )
	}
	return 
}

func transportAddrFromSocket( socket.Addr) (netaddr.TransportAddr, bool) {
	switch .Kind() {
	case socket.AddrIP:
		,  := .IP()
		return netaddr.IPAddr{Addr: }, false
	case socket.AddrRelay:
		, ,  := .Relay()
		return netaddr.RelayAddr{URL: }, true
	case socket.AddrCustom:
		,  := .Custom()
		return , false
	default:
		return nil, false
	}
}

// OpenStreamSync opens a new bidirectional stream, blocking until the peer's
// flow control permits it or ctx is done.
func ( *Conn) ( context.Context) (*Stream, error) {
	,  := .qc.OpenStreamSync()
	if  != nil {
		return nil, 
	}
	return &Stream{s: }, nil
}

// OpenStreamConn opens a bidirectional stream and returns it as a [net.Conn].
func ( *Conn) ( context.Context) (net.Conn, error) {
	,  := .OpenStreamSync()
	if  != nil {
		return nil, 
	}
	return streamConn{
		Stream:   ,
		local:    .LocalAddr(),
		remote:   .RemoteAddr(),
		remoteID: .RemoteID(),
		used0RTT: .Used0RTT(),
	}, nil
}

// AcceptStream accepts the next bidirectional stream opened by the peer.
func ( *Conn) ( context.Context) (*Stream, error) {
	,  := .qc.AcceptStream()
	if  != nil {
		return nil, 
	}
	return &Stream{s: }, nil
}

// AcceptStreamConn accepts the next bidirectional stream and returns it as a
// [net.Conn].
func ( *Conn) ( context.Context) (net.Conn, error) {
	,  := .AcceptStream()
	if  != nil {
		return nil, 
	}
	return streamConn{
		Stream:   ,
		local:    .LocalAddr(),
		remote:   .RemoteAddr(),
		remoteID: .RemoteID(),
		used0RTT: .Used0RTT(),
	}, nil
}

// OpenUniStreamSync opens a new unidirectional (send) stream.
func ( *Conn) ( context.Context) (*SendStream, error) {
	,  := .qc.OpenUniStreamSync()
	if  != nil {
		return nil, 
	}
	return &SendStream{s: }, nil
}

// AcceptUniStream accepts the next unidirectional stream opened by the peer.
func ( *Conn) ( context.Context) (*ReceiveStream, error) {
	,  := .qc.AcceptUniStream()
	if  != nil {
		return nil, 
	}
	return &ReceiveStream{s: }, nil
}

// SendDatagram sends an unreliable datagram.
func ( *Conn) ( []byte) error { return .qc.SendDatagram() }

// MaxDatagramSize returns the largest payload that can currently be passed to
// [Conn.SendDatagram]. The size may change over the connection lifetime as the
// path MTU estimate changes. The ok result is false if datagrams were not
// negotiated.
func ( *Conn) () ( int,  bool) {
	,  := .qc.MaxDatagramSize()
	return int(), 
}

// ReadDatagram receives the next unreliable datagram.
func ( *Conn) ( context.Context) ([]byte, error) {
	return .qc.ReceiveDatagram()
}

// Used0RTT reports whether the connection's early data was sent as 0-RTT and
// accepted by the peer. On the dialing side it is meaningful only after the
// handshake completes (see [Conn.HandshakeComplete]); a value of false means the
// peer rejected 0-RTT and any early data must be resent. It is always false for
// accepted connections that did not resume a prior session.
func ( *Conn) () bool { return .qc.ConnectionState().Used0RTT }

// MultipathNegotiated reports whether both endpoints negotiated the QUIC
// multipath extension on this connection.
func ( *Conn) () bool {
	return .qc.ConnectionState().MultipathNegotiated
}

// KeyExchangeGroup returns the TLS named group negotiated for this connection.
// It is empty until the handshake completes.
func ( *Conn) () string {
	return .qc.ConnectionState().TLS.CurveID.String()
}

// HandshakeComplete returns a channel closed when the TLS handshake finishes.
// For a 0-RTT dial, [Endpoint.Connect] may return before this fires; waiting on
// it and then checking [Conn.Used0RTT] tells whether the 0-RTT attempt was
// accepted or fell back to a full handshake.
func ( *Conn) () <-chan struct{} { return .qc.HandshakeComplete() }

// Context returns a context that is cancelled when the connection is closed.
func ( *Conn) () context.Context { return .qc.Context() }

// LocalAddr returns the local transport address, if known.
func ( *Conn) () net.Addr { return .qc.LocalAddr() }

// RemoteAddr returns the remote transport address, if known.
func ( *Conn) () net.Addr { return .qc.RemoteAddr() }

// CloseWithError closes the connection with an application error code and
// reason.
func ( *Conn) ( uint64,  string) error {
	return .qc.CloseWithError(quic.ApplicationErrorCode(), )
}

// Close closes the connection with application error code 0 and an empty
// reason. Use [Conn.CloseWithError] to send an application-specific close code.
func ( *Conn) () error {
	return .CloseWithError(0, "")
}

type streamConn struct {
	*Stream
	local    net.Addr
	remote   net.Addr
	remoteID key.EndpointID
	used0RTT bool
}

func ( streamConn) () net.Addr { return .local }

func ( streamConn) () net.Addr { return .remote }

// RemoteID returns the verified endpoint id of the peer that owns the stream.
func ( streamConn) () key.EndpointID { return .remoteID }

// Used0RTT reports whether the parent connection used accepted 0-RTT early
// data. Replay safety is application-specific.
func ( streamConn) () bool { return .used0RTT }

// Close closes both stream directions. [Stream.Close] closes only the send
// direction; CancelRead closes the receive direction, allowing QUIC to retire
// the stream and replenish stream credit. An already-canceled send direction
// is treated as closed.
func ( streamConn) () error {
	 := .Stream.Close()
	.Stream.CancelRead(0)
	if  != nil {
		var  *quic.StreamError
		if errors.As(context.Cause(.Stream.Context()), &) {
			return nil
		}
	}
	return 
}

// connAdapter adapts a qng *quic.Conn to the socket package's
// [socket.Connection] interface so the per-remote state actor can track its
// liveness, RTT, and path without the socket package importing iroh.
type connAdapter struct {
	qc   *quic.Conn
	addr socket.Addr
}

// newConnAdapter wraps qc for the per-remote actor. addr is the connection's
// transport path, classified by the endpoint (a real IP for a direct path, a
// relay address for a relay path).
func newConnAdapter( *quic.Conn,  socket.Addr) *connAdapter {
	return &connAdapter{qc: , addr: }
}

// SmoothedRTT returns the connection's active-path smoothed RTT. qng negotiates
// multipath, but this adapter still exposes the connection-level active-path RTT
// until per-PathID RTT is surfaced.
func ( *connAdapter) () time.Duration { return .qc.ConnectionStats().SmoothedRTT }

// Done is closed when the connection closes.
func ( *connAdapter) () <-chan struct{} { return .qc.Context().Done() }

// RemoteAddr returns the connection's transport path address.
func ( *connAdapter) () socket.Addr { return .addr }

// MultipathNegotiated reports whether qng negotiated the QUIC multipath
// extension on this connection.
func ( *connAdapter) () bool {
	return .qc.ConnectionState().MultipathNegotiated
}

// Paths returns qng multipath path state for socket observability.
func ( *connAdapter) () []socket.PathInfo {
	 := .qc.Paths()
	if len() == 0 {
		return nil
	}
	 := make([]socket.PathInfo, len())
	for ,  := range  {
		[] = socket.PathInfo{
			ID:        uint32(.ID),
			Validated: .Validated,
		}
		if .HasRTT {
			[].RTT = .SmoothedRTT
			[].HasRTT = true
		}
		if .HasBytesInFlight {
			[].BytesInFlight = uint64(.BytesInFlight)
			[].HasBytesInFlight = true
		}
		if .HasBytesSent {
			[].BytesSent = .BytesSent
			[].HasBytesSent = true
		}
		if .HasBytesReceived {
			[].BytesReceived = .BytesReceived
			[].HasBytesReceived = true
		}
		if .HasCongestionWindow {
			[].CongestionWindow = uint64(.CongestionWindow)
			[].HasCongestionWindow = true
		}
		if .HasLoss {
			[].LostPackets = .LostPackets
			[].LostBytes = .LostBytes
			[].HasLoss = true
		}
		if .RemoteAddr.IsValid() {
			[].Addr = socket.IPAddr(.RemoteAddr)
			[].HasAddr = true
		}
	}
	return 
}

// AddNATTraversalAddress hands one local QNT candidate address to qng.
func ( *connAdapter) ( netip.AddrPort) error {
	 := .qc.AddNATTraversalAddress()
	if errors.Is(, quic.ErrNATTraversalNotNegotiated) {
		return socket.ErrExtensionNotNegotiated
	}
	return 
}

// RemoveNATTraversalAddress removes one local QNT candidate address from qng.
func ( *connAdapter) ( netip.AddrPort) error {
	 := .qc.RemoveNATTraversalAddress()
	if errors.Is(, quic.ErrNATTraversalNotNegotiated) {
		return socket.ErrExtensionNotNegotiated
	}
	return 
}

// InitiateNATTraversalRound asks qng to start one QNT round.
func ( *connAdapter) ( context.Context) ([]netip.AddrPort, error) {
	,  := .qc.InitiateNATTraversalRound()
	if errors.Is(, quic.ErrNATTraversalNotNegotiated) {
		return nil, socket.ErrExtensionNotNegotiated
	}
	return , 
}

// NATTraversalAddresses reports the remote QNT candidate set qng has learned.
func ( *connAdapter) () ([]netip.AddrPort, error) {
	,  := .qc.NATTraversalAddresses()
	if errors.Is(, quic.ErrNATTraversalNotNegotiated) {
		return nil, socket.ErrExtensionNotNegotiated
	}
	return , 
}

// AddRemoteNATTraversalAddress hands one remote QNT candidate address to qng.
func ( *connAdapter) ( netip.AddrPort) error {
	 := .qc.AddRemoteNATTraversalAddress()
	if errors.Is(, quic.ErrNATTraversalNotNegotiated) {
		return socket.ErrExtensionNotNegotiated
	}
	return 
}

// OpenPath opens and validates one qng multipath path over the connection's
// existing MagicConn socket.
func ( *connAdapter) ( context.Context) error {
	for {
		,  := .qc.OpenPath(nil)
		if  == nil {
			return .Validated()
		}
		if !errors.Is(, quic.ErrPathLimit) {
			return 
		}
		 := time.NewTimer(10 * time.Millisecond)
		select {
		case <-.C:
		case <-.Done():
			.Stop()
			return context.Cause()
		}
	}
}

var _ socket.Connection = (*connAdapter)(nil)