// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build unix || js || wasip1 || windows

package net

import (
	
	
	
	
)

func sockaddrToTCP( syscall.Sockaddr) Addr {
	switch sa := .(type) {
	case *syscall.SockaddrInet4:
		return &TCPAddr{IP: .Addr[0:], Port: .Port}
	case *syscall.SockaddrInet6:
		return &TCPAddr{IP: .Addr[0:], Port: .Port, Zone: zoneCache.name(int(.ZoneId))}
	}
	return nil
}

func ( *TCPAddr) () int {
	if  == nil || len(.IP) <= IPv4len {
		return syscall.AF_INET
	}
	if .IP.To4() != nil {
		return syscall.AF_INET
	}
	return syscall.AF_INET6
}

func ( *TCPAddr) ( int) (syscall.Sockaddr, error) {
	if  == nil {
		return nil, nil
	}
	return ipToSockaddr(, .IP, .Port, .Zone)
}

func ( *TCPAddr) ( string) sockaddr {
	return &TCPAddr{loopbackIP(), .Port, .Zone}
}

func ( *TCPConn) ( io.Reader) (int64, error) {
	if , ,  := spliceFrom(.fd, );  {
		return , 
	}
	if , ,  := sendFile(.fd, );  {
		return , 
	}
	return genericReadFrom(, )
}

func ( *TCPConn) ( io.Writer) (int64, error) {
	if , ,  := spliceTo(, .fd);  {
		return , 
	}
	return genericWriteTo(, )
}

func ( *sysDialer) ( context.Context, ,  *TCPAddr) (*TCPConn, error) {
	if  := .testHookDialTCP;  != nil {
		return (, .network, , )
	}
	if  := testHookDialTCP;  != nil {
		return (, .network, , )
	}
	return .doDialTCP(, , )
}

func ( *sysDialer) ( context.Context, ,  *TCPAddr) (*TCPConn, error) {
	return .doDialTCPProto(, , , 0)
}

func ( *sysDialer) ( context.Context, ,  *TCPAddr,  int) (*TCPConn, error) {
	 := .Dialer.ControlContext
	if  == nil && .Dialer.Control != nil {
		 = func( context.Context, ,  string,  syscall.RawConn) error {
			return .Dialer.Control(, , )
		}
	}
	,  := internetSocket(, .network, , , syscall.SOCK_STREAM, , "dial", )

	// TCP has a rarely used mechanism called a 'simultaneous connection' in
	// which Dial("tcp", addr1, addr2) run on the machine at addr1 can
	// connect to a simultaneous Dial("tcp", addr2, addr1) run on the machine
	// at addr2, without either machine executing Listen. If laddr == nil,
	// it means we want the kernel to pick an appropriate originating local
	// address. Some Linux kernels cycle blindly through a fixed range of
	// local ports, regardless of destination port. If a kernel happens to
	// pick local port 50001 as the source for a Dial("tcp", "", "localhost:50001"),
	// then the Dial will succeed, having simultaneously connected to itself.
	// This can only happen when we are letting the kernel pick a port (laddr == nil)
	// and when there is no listener for the destination address.
	// It's hard to argue this is anything other than a kernel bug. If we
	// see this happen, rather than expose the buggy effect to users, we
	// close the fd and try again. If it happens twice more, we relent and
	// use the result. See also:
	//	https://golang.org/issue/2690
	//	https://stackoverflow.com/questions/4949858/
	//
	// The opposite can also happen: if we ask the kernel to pick an appropriate
	// originating local address, sometimes it picks one that is already in use.
	// So if the error is EADDRNOTAVAIL, we have to try again too, just for
	// a different reason.
	//
	// The kernel socket code is no doubt enjoying watching us squirm.
	for  := 0;  < 2 && ( == nil || .Port == 0) && (selfConnect(, ) || spuriousENOTAVAIL()); ++ {
		if  == nil {
			.Close()
		}
		,  = internetSocket(, .network, , , syscall.SOCK_STREAM, , "dial", )
	}

	if  != nil {
		return nil, 
	}
	return newTCPConn(, .Dialer.KeepAlive, testHookSetKeepAlive), nil
}

func selfConnect( *netFD,  error) bool {
	// If the connect failed, we clearly didn't connect to ourselves.
	if  != nil {
		return false
	}

	// The socket constructor can return an fd with raddr nil under certain
	// unknown conditions. The errors in the calls there to Getpeername
	// are discarded, but we can't catch the problem there because those
	// calls are sometimes legally erroneous with a "socket not connected".
	// Since this code (selfConnect) is already trying to work around
	// a problem, we make sure if this happens we recognize trouble and
	// ask the DialTCP routine to try again.
	// TODO: try to understand what's really going on.
	if .laddr == nil || .raddr == nil {
		return true
	}
	 := .laddr.(*TCPAddr)
	 := .raddr.(*TCPAddr)
	return .Port == .Port && .IP.Equal(.IP)
}

func spuriousENOTAVAIL( error) bool {
	if ,  := .(*OpError);  {
		 = .Err
	}
	if ,  := .(*os.SyscallError);  {
		 = .Err
	}
	return  == syscall.EADDRNOTAVAIL
}

func ( *TCPListener) () bool { return  != nil && .fd != nil }

func ( *TCPListener) () (*TCPConn, error) {
	,  := .fd.accept()
	if  != nil {
		return nil, 
	}
	return newTCPConn(, .lc.KeepAlive, nil), nil
}

func ( *TCPListener) () error {
	return .fd.Close()
}

func ( *TCPListener) () (*os.File, error) {
	,  := .fd.dup()
	if  != nil {
		return nil, 
	}
	return , nil
}

func ( *sysListener) ( context.Context,  *TCPAddr) (*TCPListener, error) {
	return .listenTCPProto(, , 0)
}

func ( *sysListener) ( context.Context,  *TCPAddr,  int) (*TCPListener, error) {
	var  func( context.Context, ,  string,  syscall.RawConn) error
	if .ListenConfig.Control != nil {
		 = func( context.Context, ,  string,  syscall.RawConn) error {
			return .ListenConfig.Control(, , )
		}
	}
	,  := internetSocket(, .network, , nil, syscall.SOCK_STREAM, , "listen", )
	if  != nil {
		return nil, 
	}
	return &TCPListener{fd: , lc: .ListenConfig}, nil
}