// 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 aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows

package net

import (
	
	
	
)

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

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

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

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

func ( *UDPConn) ( []byte,  *UDPAddr) (int, *UDPAddr, error) {
	var  int
	var  error
	switch .fd.family {
	case syscall.AF_INET:
		var  syscall.SockaddrInet4
		,  = .fd.readFromInet4(, &)
		if  == nil {
			 := .Addr // copy from.Addr; ip escapes, so this line allocates 4 bytes
			* = UDPAddr{IP: [:], Port: .Port}
		}
	case syscall.AF_INET6:
		var  syscall.SockaddrInet6
		,  = .fd.readFromInet6(, &)
		if  == nil {
			 := .Addr // copy from.Addr; ip escapes, so this line allocates 16 bytes
			* = UDPAddr{IP: [:], Port: .Port, Zone: zoneCache.name(int(.ZoneId))}
		}
	}
	if  != nil {
		// No sockaddr, so don't return UDPAddr.
		 = nil
	}
	return , , 
}

func ( *UDPConn) ( []byte) ( int,  netip.AddrPort,  error) {
	var  netip.Addr
	var  int
	switch .fd.family {
	case syscall.AF_INET:
		var  syscall.SockaddrInet4
		,  = .fd.readFromInet4(, &)
		if  == nil {
			 = netip.AddrFrom4(.Addr)
			 = .Port
		}
	case syscall.AF_INET6:
		var  syscall.SockaddrInet6
		,  = .fd.readFromInet6(, &)
		if  == nil {
			 = netip.AddrFrom16(.Addr).WithZone(zoneCache.name(int(.ZoneId)))
			 = .Port
		}
	}
	if  == nil {
		 = netip.AddrPortFrom(, uint16())
	}
	return , , 
}

func ( *UDPConn) (,  []byte) (, ,  int,  netip.AddrPort,  error) {
	switch .fd.family {
	case syscall.AF_INET:
		var  syscall.SockaddrInet4
		, , ,  = .fd.readMsgInet4(, , 0, &)
		 := netip.AddrFrom4(.Addr)
		 = netip.AddrPortFrom(, uint16(.Port))
	case syscall.AF_INET6:
		var  syscall.SockaddrInet6
		, , ,  = .fd.readMsgInet6(, , 0, &)
		 := netip.AddrFrom16(.Addr).WithZone(zoneCache.name(int(.ZoneId)))
		 = netip.AddrPortFrom(, uint16(.Port))
	}
	return
}

func ( *UDPConn) ( []byte,  *UDPAddr) (int, error) {
	if .fd.isConnected {
		return 0, ErrWriteToConnected
	}
	if  == nil {
		return 0, errMissingAddress
	}

	switch .fd.family {
	case syscall.AF_INET:
		,  := ipToSockaddrInet4(.IP, .Port)
		if  != nil {
			return 0, 
		}
		return .fd.writeToInet4(, &)
	case syscall.AF_INET6:
		,  := ipToSockaddrInet6(.IP, .Port, .Zone)
		if  != nil {
			return 0, 
		}
		return .fd.writeToInet6(, &)
	default:
		return 0, &AddrError{Err: "invalid address family", Addr: .IP.String()}
	}
}

func ( *UDPConn) ( []byte,  netip.AddrPort) (int, error) {
	if .fd.isConnected {
		return 0, ErrWriteToConnected
	}
	if !.IsValid() {
		return 0, errMissingAddress
	}

	switch .fd.family {
	case syscall.AF_INET:
		,  := addrPortToSockaddrInet4()
		if  != nil {
			return 0, 
		}
		return .fd.writeToInet4(, &)
	case syscall.AF_INET6:
		,  := addrPortToSockaddrInet6()
		if  != nil {
			return 0, 
		}
		return .fd.writeToInet6(, &)
	default:
		return 0, &AddrError{Err: "invalid address family", Addr: .Addr().String()}
	}
}

func ( *UDPConn) (,  []byte,  *UDPAddr) (,  int,  error) {
	if .fd.isConnected &&  != nil {
		return 0, 0, ErrWriteToConnected
	}
	if !.fd.isConnected &&  == nil {
		return 0, 0, errMissingAddress
	}
	,  := .sockaddr(.fd.family)
	if  != nil {
		return 0, 0, 
	}
	return .fd.writeMsg(, , )
}

func ( *UDPConn) (,  []byte,  netip.AddrPort) (,  int,  error) {
	if .fd.isConnected && .IsValid() {
		return 0, 0, ErrWriteToConnected
	}
	if !.fd.isConnected && !.IsValid() {
		return 0, 0, errMissingAddress
	}

	switch .fd.family {
	case syscall.AF_INET:
		,  := addrPortToSockaddrInet4()
		if  != nil {
			return 0, 0, 
		}
		return .fd.writeMsgInet4(, , &)
	case syscall.AF_INET6:
		,  := addrPortToSockaddrInet6()
		if  != nil {
			return 0, 0, 
		}
		return .fd.writeMsgInet6(, , &)
	default:
		return 0, 0, &AddrError{Err: "invalid address family", Addr: .Addr().String()}
	}
}

func ( *sysDialer) ( context.Context, ,  *UDPAddr) (*UDPConn, error) {
	,  := internetSocket(, .network, , , syscall.SOCK_DGRAM, 0, "dial", .Dialer.Control)
	if  != nil {
		return nil, 
	}
	return newUDPConn(), nil
}

func ( *sysListener) ( context.Context,  *UDPAddr) (*UDPConn, error) {
	,  := internetSocket(, .network, , nil, syscall.SOCK_DGRAM, 0, "listen", .ListenConfig.Control)
	if  != nil {
		return nil, 
	}
	return newUDPConn(), nil
}

func ( *sysListener) ( context.Context,  *Interface,  *UDPAddr) (*UDPConn, error) {
	,  := internetSocket(, .network, , nil, syscall.SOCK_DGRAM, 0, "listen", .ListenConfig.Control)
	if  != nil {
		return nil, 
	}
	 := newUDPConn()
	if  := .IP.To4();  != nil {
		if  := listenIPv4MulticastUDP(, , );  != nil {
			.Close()
			return nil, 
		}
	} else {
		if  := listenIPv6MulticastUDP(, , .IP);  != nil {
			.Close()
			return nil, 
		}
	}
	return , nil
}

func listenIPv4MulticastUDP( *UDPConn,  *Interface,  IP) error {
	if  != nil {
		if  := setIPv4MulticastInterface(.fd, );  != nil {
			return 
		}
	}
	if  := setIPv4MulticastLoopback(.fd, false);  != nil {
		return 
	}
	if  := joinIPv4Group(.fd, , );  != nil {
		return 
	}
	return nil
}

func listenIPv6MulticastUDP( *UDPConn,  *Interface,  IP) error {
	if  != nil {
		if  := setIPv6MulticastInterface(.fd, );  != nil {
			return 
		}
	}
	if  := setIPv6MulticastLoopback(.fd, false);  != nil {
		return 
	}
	if  := joinIPv6Group(.fd, , );  != nil {
		return 
	}
	return nil
}