// 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

package net

import (
	
	
	
	
	
)

const (
	readSyscallName     = "read"
	readFromSyscallName = "recvfrom"
	readMsgSyscallName  = "recvmsg"
	writeSyscallName    = "write"
	writeToSyscallName  = "sendto"
	writeMsgSyscallName = "sendmsg"
)

func newFD(, ,  int,  string) (*netFD, error) {
	 := &netFD{
		pfd: poll.FD{
			Sysfd:         ,
			IsStream:       == syscall.SOCK_STREAM,
			ZeroReadIsEOF:  != syscall.SOCK_DGRAM &&  != syscall.SOCK_RAW,
		},
		family: ,
		sotype: ,
		net:    ,
	}
	return , nil
}

func ( *netFD) () error {
	return .pfd.Init(.net, true)
}

func ( *netFD) () string {
	var ,  string
	if .laddr != nil {
		 = .laddr.String()
	}
	if .raddr != nil {
		 = .raddr.String()
	}
	return .net + ":" +  + "->" + 
}

func ( *netFD) ( context.Context, ,  syscall.Sockaddr) ( syscall.Sockaddr,  error) {
	// Do not need to call fd.writeLock here,
	// because fd is not yet accessible to user,
	// so no concurrent operations are possible.
	switch  := connectFunc(.pfd.Sysfd, );  {
	case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
	case nil, syscall.EISCONN:
		select {
		case <-.Done():
			return nil, mapErr(.Err())
		default:
		}
		if  := .pfd.Init(.net, true);  != nil {
			return nil, 
		}
		runtime.KeepAlive()
		return nil, nil
	case syscall.EINVAL:
		// On Solaris and illumos we can see EINVAL if the socket has
		// already been accepted and closed by the server.  Treat this
		// as a successful connection--writes to the socket will see
		// EOF.  For details and a test case in C see
		// https://golang.org/issue/6828.
		if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" {
			return nil, nil
		}
		fallthrough
	default:
		return nil, os.NewSyscallError("connect", )
	}
	if  := .pfd.Init(.net, true);  != nil {
		return nil, 
	}
	if ,  := .Deadline();  {
		.pfd.SetWriteDeadline()
		defer .pfd.SetWriteDeadline(noDeadline)
	}

	// Start the "interrupter" goroutine, if this context might be canceled.
	//
	// The interrupter goroutine waits for the context to be done and
	// interrupts the dial (by altering the fd's write deadline, which
	// wakes up waitWrite).
	 := .Done()
	if  != nil {
		// Wait for the interrupter goroutine to exit before returning
		// from connect.
		 := make(chan struct{})
		 := make(chan error)
		defer func() {
			close()
			if  := <-;  != nil &&  == nil {
				// The interrupter goroutine called SetWriteDeadline,
				// but the connect code below had returned from
				// waitWrite already and did a successful connect (ret
				// == nil). Because we've now poisoned the connection
				// by making it unwritable, don't return a successful
				// dial. This was issue 16523.
				 = mapErr()
				.Close() // prevent a leak
			}
		}()
		go func() {
			select {
			case <-:
				// Force the runtime's poller to immediately give up
				// waiting for writability, unblocking waitWrite
				// below.
				.pfd.SetWriteDeadline(aLongTimeAgo)
				testHookCanceledDial()
				 <- .Err()
			case <-:
				 <- nil
			}
		}()
	}

	for {
		// Performing multiple connect system calls on a
		// non-blocking socket under Unix variants does not
		// necessarily result in earlier errors being
		// returned. Instead, once runtime-integrated network
		// poller tells us that the socket is ready, get the
		// SO_ERROR socket option to see if the connection
		// succeeded or failed. See issue 7474 for further
		// details.
		if  := .pfd.WaitWrite();  != nil {
			select {
			case <-:
				return nil, mapErr(.Err())
			default:
			}
			return nil, 
		}
		,  := getsockoptIntFunc(.pfd.Sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
		if  != nil {
			return nil, os.NewSyscallError("getsockopt", )
		}
		switch  := syscall.Errno();  {
		case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
		case syscall.EISCONN:
			return nil, nil
		case syscall.Errno(0):
			// The runtime poller can wake us up spuriously;
			// see issues 14548 and 19289. Check that we are
			// really connected; if not, wait again.
			if ,  := syscall.Getpeername(.pfd.Sysfd);  == nil {
				return , nil
			}
		default:
			return nil, os.NewSyscallError("connect", )
		}
		runtime.KeepAlive()
	}
}

func ( *netFD) () ( *netFD,  error) {
	, , ,  := .pfd.Accept()
	if  != nil {
		if  != "" {
			 = wrapSyscallError(, )
		}
		return nil, 
	}

	if ,  = newFD(, .family, .sotype, .net);  != nil {
		poll.CloseFunc()
		return nil, 
	}
	if  = .init();  != nil {
		.Close()
		return nil, 
	}
	,  := syscall.Getsockname(.pfd.Sysfd)
	.setAddr(.addrFunc()(), .addrFunc()())
	return , nil
}

// Defined in os package.
func newUnixFile( int,  string) *os.File

func ( *netFD) () ( *os.File,  error) {
	, ,  := .pfd.Dup()
	if  != nil {
		if  != "" {
			 = os.NewSyscallError(, )
		}
		return nil, 
	}

	return newUnixFile(, .name()), nil
}