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

package net

import (
	
	
	
	
)

// socket returns a network file descriptor that is ready for
// asynchronous I/O using the network poller.
func socket( context.Context,  string, , ,  int,  bool, ,  sockaddr,  func(context.Context, string, string, syscall.RawConn) error) ( *netFD,  error) {
	,  := sysSocket(, , )
	if  != nil {
		return nil, 
	}
	if  = setDefaultSockopts(, , , );  != nil {
		poll.CloseFunc()
		return nil, 
	}
	if ,  = newFD(, , , );  != nil {
		poll.CloseFunc()
		return nil, 
	}

	// This function makes a network file descriptor for the
	// following applications:
	//
	// - An endpoint holder that opens a passive stream
	//   connection, known as a stream listener
	//
	// - An endpoint holder that opens a destination-unspecific
	//   datagram connection, known as a datagram listener
	//
	// - An endpoint holder that opens an active stream or a
	//   destination-specific datagram connection, known as a
	//   dialer
	//
	// - An endpoint holder that opens the other connection, such
	//   as talking to the protocol stack inside the kernel
	//
	// For stream and datagram listeners, they will only require
	// named sockets, so we can assume that it's just a request
	// from stream or datagram listeners when laddr is not nil but
	// raddr is nil. Otherwise we assume it's just for dialers or
	// the other connection holders.

	if  != nil &&  == nil {
		switch  {
		case syscall.SOCK_STREAM, syscall.SOCK_SEQPACKET:
			if  := .listenStream(, , listenerBacklog(), );  != nil {
				.Close()
				return nil, 
			}
			return , nil
		case syscall.SOCK_DGRAM:
			if  := .listenDatagram(, , );  != nil {
				.Close()
				return nil, 
			}
			return , nil
		}
	}
	if  := .dial(, , , );  != nil {
		.Close()
		return nil, 
	}
	return , nil
}

func ( *netFD) () string {
	switch .net {
	case "unix", "unixgram", "unixpacket":
		return .net
	}
	switch .net[len(.net)-1] {
	case '4', '6':
		return .net
	}
	if .family == syscall.AF_INET {
		return .net + "4"
	}
	return .net + "6"
}

func ( *netFD) ( context.Context, ,  sockaddr,  func(context.Context, string, string, syscall.RawConn) error) error {
	var  *rawConn
	if  != nil {
		 = newRawConn()
		var  string
		if  != nil {
			 = .String()
		} else if  != nil {
			 = .String()
		}
		if  := (, .ctrlNetwork(), , );  != nil {
			return 
		}
	}

	var  syscall.Sockaddr
	var  error
	if  != nil {
		if ,  = .sockaddr(.family);  != nil {
			return 
		} else if  != nil {
			if  = syscall.Bind(.pfd.Sysfd, );  != nil {
				return os.NewSyscallError("bind", )
			}
		}
	}
	var  syscall.Sockaddr  // remote address from the user
	var  syscall.Sockaddr // remote address we actually connected to
	if  != nil {
		if ,  = .sockaddr(.family);  != nil {
			return 
		}
		if ,  = .connect(, , );  != nil {
			return 
		}
		.isConnected = true
	} else {
		if  := .init();  != nil {
			return 
		}
	}
	// Record the local and remote addresses from the actual socket.
	// Get the local address by calling Getsockname.
	// For the remote address, use
	// 1) the one returned by the connect method, if any; or
	// 2) the one from Getpeername, if it succeeds; or
	// 3) the one passed to us as the raddr parameter.
	, _ = syscall.Getsockname(.pfd.Sysfd)
	if  != nil {
		.setAddr(.addrFunc()(), .addrFunc()())
	} else if , _ = syscall.Getpeername(.pfd.Sysfd);  != nil {
		.setAddr(.addrFunc()(), .addrFunc()())
	} else {
		.setAddr(.addrFunc()(), )
	}
	return nil
}

func ( *netFD) ( context.Context,  sockaddr,  int,  func(context.Context, string, string, syscall.RawConn) error) error {
	var  error
	if  = setDefaultListenerSockopts(.pfd.Sysfd);  != nil {
		return 
	}
	var  syscall.Sockaddr
	if ,  = .sockaddr(.family);  != nil {
		return 
	}

	if  != nil {
		 := newRawConn()
		if  := (, .ctrlNetwork(), .String(), );  != nil {
			return 
		}
	}

	if  = syscall.Bind(.pfd.Sysfd, );  != nil {
		return os.NewSyscallError("bind", )
	}
	if  = listenFunc(.pfd.Sysfd, );  != nil {
		return os.NewSyscallError("listen", )
	}
	if  = .init();  != nil {
		return 
	}
	, _ = syscall.Getsockname(.pfd.Sysfd)
	.setAddr(.addrFunc()(), nil)
	return nil
}

func ( *netFD) ( context.Context,  sockaddr,  func(context.Context, string, string, syscall.RawConn) error) error {
	switch addr := .(type) {
	case *UDPAddr:
		// We provide a socket that listens to a wildcard
		// address with reusable UDP port when the given laddr
		// is an appropriate UDP multicast address prefix.
		// This makes it possible for a single UDP listener to
		// join multiple different group addresses, for
		// multiple UDP listeners that listen on the same UDP
		// port to join the same group address.
		if .IP != nil && .IP.IsMulticast() {
			if  := setDefaultMulticastSockopts(.pfd.Sysfd);  != nil {
				return 
			}
			 := *
			switch .family {
			case syscall.AF_INET:
				.IP = IPv4zero
			case syscall.AF_INET6:
				.IP = IPv6unspecified
			}
			 = &
		}
	}
	var  error
	var  syscall.Sockaddr
	if ,  = .sockaddr(.family);  != nil {
		return 
	}

	if  != nil {
		 := newRawConn()
		if  := (, .ctrlNetwork(), .String(), );  != nil {
			return 
		}
	}
	if  = syscall.Bind(.pfd.Sysfd, );  != nil {
		return os.NewSyscallError("bind", )
	}
	if  = .init();  != nil {
		return 
	}
	, _ = syscall.Getsockname(.pfd.Sysfd)
	.setAddr(.addrFunc()(), nil)
	return nil
}