package tcp

import (
	
	
	
	
	
	
	
	

	
	
	
	
	

	logging 
	ma 
	mafmt 
	manet 
)

const defaultConnectTimeout = 5 * time.Second

var log = logging.Logger("tcp-tpt")

const keepAlivePeriod = 30 * time.Second

type canKeepAlive interface {
	SetKeepAlive(bool) error
	SetKeepAlivePeriod(time.Duration) error
}

var _ canKeepAlive = &net.TCPConn{}

// Deprecated: Use tcpreuse.ReuseportIsAvailable
var ReuseportIsAvailable = tcpreuse.ReuseportIsAvailable

func tryKeepAlive( net.Conn,  bool) {
	,  := .(canKeepAlive)
	if ! {
		log.Errorf("can't set TCP keepalives. net.Conn of type %T doesn't support SetKeepAlive", )
		return
	}
	if  := .SetKeepAlive();  != nil {
		// Sometimes we seem to get "invalid argument" results from this function on Darwin.
		// This might be due to a closed connection, but I can't reproduce that on Linux.
		//
		// But there's nothing we can do about invalid arguments, so we'll drop this to a
		// debug.
		if errors.Is(, os.ErrInvalid) || errors.Is(, syscall.EINVAL) {
			log.Debugw("failed to enable TCP keepalive", "error", )
		} else {
			log.Errorw("failed to enable TCP keepalive", "error", )
		}
		return
	}

	if runtime.GOOS != "openbsd" {
		if  := .SetKeepAlivePeriod(keepAlivePeriod);  != nil {
			log.Errorw("failed set keepalive period", "error", )
		}
	}
}

// try to set linger on the connection, if possible.
func tryLinger( net.Conn,  int) {
	type  interface {
		(int) error
	}

	if ,  := .();  {
		_ = .()
	}
}

type tcpGatedMaListener struct {
	transport.GatedMaListener
	sec int
}

func ( *tcpGatedMaListener) () (manet.Conn, network.ConnManagementScope, error) {
	, ,  := .GatedMaListener.Accept()
	if  != nil {
		if  != nil {
			log.Errorf("BUG: got non-nil scope but also an error: %s", )
			.Done()
		}
		return nil, nil, 
	}
	tryLinger(, .sec)
	tryKeepAlive(, true)
	return , , nil
}

type Option func(*TcpTransport) error

func () Option {
	return func( *TcpTransport) error {
		.disableReuseport = true
		return nil
	}
}

func ( time.Duration) Option {
	return func( *TcpTransport) error {
		.connectTimeout = 
		return nil
	}
}

func () Option {
	return func( *TcpTransport) error {
		.enableMetrics = true
		return nil
	}
}

// WithDialerForAddr sets a custom dialer for the given address.
// If set, it will be the *ONLY* dialer used.
func ( DialerForAddr) Option {
	return func( *TcpTransport) error {
		.overrideDialerForAddr = 
		return nil
	}
}

type ContextDialer interface {
	DialContext(ctx context.Context, network, address string) (net.Conn, error)
}

// DialerForAddr is a function that returns a dialer for a given address.
// Implementations must return either a ContextDialer or an error. It is
// invalid to return nil, nil.
type DialerForAddr func(raddr ma.Multiaddr) (ContextDialer, error)

// TcpTransport is the TCP transport.
type TcpTransport struct {
	// Connection upgrader for upgrading insecure stream connections to
	// secure multiplex connections.
	upgrader transport.Upgrader

	// optional custom dialer to use for dialing. If set, it will be the *ONLY* dialer
	// used. The transport will not attempt to reuse the listen port to
	// dial or the shared TCP transport for dialing.
	overrideDialerForAddr DialerForAddr

	disableReuseport bool // Explicitly disable reuseport.
	enableMetrics    bool

	// share and demultiplex TCP listeners across multiple transports
	sharedTcp *tcpreuse.ConnMgr

	// TCP connect timeout
	connectTimeout time.Duration

	rcmgr network.ResourceManager

	reuse reuseport.Transport

	metricsCollector *aggregatingCollector
}

var _ transport.Transport = &TcpTransport{}
var _ transport.DialUpdater = &TcpTransport{}

// NewTCPTransport creates a tcp transport object that tracks dialers and listeners
// created.
func ( transport.Upgrader,  network.ResourceManager,  *tcpreuse.ConnMgr,  ...Option) (*TcpTransport, error) {
	if  == nil {
		 = &network.NullResourceManager{}
	}
	 := &TcpTransport{
		upgrader:       ,
		connectTimeout: defaultConnectTimeout, // can be set by using the WithConnectionTimeout option
		rcmgr:          ,
		sharedTcp:      ,
	}
	for ,  := range  {
		if  := ();  != nil {
			return nil, 
		}
	}
	return , nil
}

var dialMatcher = mafmt.And(mafmt.IP, mafmt.Base(ma.P_TCP))

// CanDial returns true if this transport believes it can dial the given
// multiaddr.
func ( *TcpTransport) ( ma.Multiaddr) bool {
	return dialMatcher.Matches()
}

func ( *TcpTransport) ( context.Context,  ma.Multiaddr) (manet.Conn, error) {
	// get the net.Dial friendly arguments from the remote addr
	, ,  := manet.DialArgs()
	if  != nil {
		return nil, 
	}
	,  := .overrideDialerForAddr()
	if  != nil {
		return nil, 
	}
	if  == nil {
		return nil, fmt.Errorf("dialer for address %s is nil", )
	}

	// ok, Dial!
	var  net.Conn
	switch  {
	case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6", "unix":
		,  = .DialContext(, , )
		if  != nil {
			return nil, 
		}
	default:
		return nil, fmt.Errorf("unrecognized network: %s", )
	}

	return manet.WrapNetConn()
}

func ( *TcpTransport) ( context.Context,  ma.Multiaddr) (manet.Conn, error) {
	// Apply the deadline iff applicable
	if .connectTimeout > 0 {
		var  context.CancelFunc
		,  = context.WithTimeout(, .connectTimeout)
		defer ()
	}

	if .overrideDialerForAddr != nil {
		return .customDial(, )
	}

	if .sharedTcp != nil {
		return .sharedTcp.DialContext(, )
	}

	if .UseReuseport() {
		return .reuse.DialContext(, )
	}
	var  manet.Dialer
	return .DialContext(, )
}

// Dial dials the peer at the remote address.
func ( *TcpTransport) ( context.Context,  ma.Multiaddr,  peer.ID) (transport.CapableConn, error) {
	return .DialWithUpdates(, , , nil)
}

func ( *TcpTransport) ( context.Context,  ma.Multiaddr,  peer.ID,  chan<- transport.DialUpdate) (transport.CapableConn, error) {
	,  := .rcmgr.OpenConnection(network.DirOutbound, true, )
	if  != nil {
		log.Debugw("resource manager blocked outgoing connection", "peer", , "addr", , "error", )
		return nil, 
	}

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

func ( *TcpTransport) ( context.Context,  ma.Multiaddr,  peer.ID,  network.ConnManagementScope,  chan<- transport.DialUpdate) (transport.CapableConn, error) {
	if  := .SetPeer();  != nil {
		log.Debugw("resource manager blocked outgoing connection for peer", "peer", , "addr", , "error", )
		return nil, 
	}
	,  := .maDial(, )
	if  != nil {
		return nil, 
	}
	// Set linger to 0 so we never get stuck in the TIME-WAIT state. When
	// linger is 0, connections are _reset_ instead of closed with a FIN.
	// This means we can immediately reuse the 5-tuple and reconnect.
	tryLinger(, 0)
	tryKeepAlive(, true)
	 := 
	if .enableMetrics {
		var  error
		,  = newTracingConn(, .metricsCollector, true)
		if  != nil {
			return nil, 
		}
	}
	if  != nil {
		select {
		case  <- transport.DialUpdate{Kind: transport.UpdateKindHandshakeProgressed, Addr: }:
		default:
			// It is better to skip the update than to delay upgrading the connection
		}
	}
	 := network.DirOutbound
	if , ,  := network.GetSimultaneousConnect();  && ! {
		 = network.DirInbound
	}
	return .upgrader.Upgrade(, , , , , )
}

// UseReuseport returns true if reuseport is enabled and available.
func ( *TcpTransport) () bool {
	return !.disableReuseport && tcpreuse.ReuseportIsAvailable()
}

func ( *TcpTransport) ( ma.Multiaddr) (manet.Listener, error) {
	if .UseReuseport() {
		return .reuse.Listen()
	}
	return manet.Listen()
}

// Listen listens on the given multiaddr.
func ( *TcpTransport) ( ma.Multiaddr) (transport.Listener, error) {
	var  transport.GatedMaListener
	var  error
	if .sharedTcp != nil {
		,  = .sharedTcp.DemultiplexedListen(, tcpreuse.DemultiplexedConnType_MultistreamSelect)
		if  != nil {
			return nil, 
		}
	} else {
		,  := .unsharedMAListen()
		if  != nil {
			return nil, 
		}
		 = .upgrader.GateMaListener()
	}

	// Always wrap the listener with tcpGatedMaListener to apply TCP-specific configurations
	 := &tcpGatedMaListener{, 0}

	if .enableMetrics {
		// Wrap with tracing listener if metrics are enabled
		return .upgrader.UpgradeGatedMaListener(, newTracingListener(, .metricsCollector)), nil
	}

	// Regular path without metrics
	return .upgrader.UpgradeGatedMaListener(, ), nil
}

// Protocols returns the list of terminal protocols this transport can dial.
func ( *TcpTransport) () []int {
	return []int{ma.P_TCP}
}

// Proxy always returns false for the TCP transport.
func ( *TcpTransport) () bool {
	return false
}

func ( *TcpTransport) () string {
	return "TCP"
}