func (e *endpoint) prepareForWrite(to *tcpip.FullAddress) (retry bool, err *tcpip.Error) {
	switch e.state {
	case StateInitial:
	case StateConnected:
		return false, nil
	case StateBound:
		if to == nil {
			return false, tcpip.ErrDestinationRequired
		}
		return false, nil
	default:
		return false, tcpip.ErrInvalidEndpointState
	}
	e.mu.RUnlock()
	defer e.mu.RLock()
	e.mu.Lock()
	defer e.mu.Unlock()
	if e.state != StateInitial {
		return true, nil
	}
	if err := e.bindLocked(tcpip.FullAddress{}); err != nil {
		return false, err
	}
	return true, nil
}
prepareForWrite prepares the endpoint for sending data. In particular, it
binds it if it's still in the initial state. To do so, it must first
reacquire the mutex in exclusive mode.

Returns true for retry if preparation should be retried.
package udp
import (
	"sync"
	"github.com/google/netstack/tcpip"
	"github.com/google/netstack/tcpip/buffer"
	"github.com/google/netstack/tcpip/header"
	"github.com/google/netstack/tcpip/iptables"
	"github.com/google/netstack/tcpip/stack"
	"github.com/google/netstack/waiter"
)
type udpPacket struct {
	udpPacketEntry
	senderAddress tcpip.FullAddress
	data          buffer.VectorisedView
	timestamp     int64
}
type EndpointState uint32
const (
	StateInitial EndpointState = iota
	StateBound
	StateConnected
	StateClosed
)
func (s EndpointState) String() string {
	switch s {
	case StateInitial:
		return "INITIAL"
	case StateBound:
		return "BOUND"
	case StateConnected:
		return "CONNECTING"
	case StateClosed:
		return "CLOSED"
	default:
		return "UNKNOWN"
	}
}
type endpoint struct {
	stack.TransportEndpointInfo
	stack       *stack.Stack
	waiterQueue *waiter.Queue
	uniqueID    uint64
	rcvMu         sync.Mutex
	rcvReady      bool
	rcvList       udpPacketList
	rcvBufSizeMax int
	rcvBufSize    int
	rcvClosed     bool
	mu             sync.RWMutex
	sndBufSize     int
	state          EndpointState
	route          stack.Route
	dstPort        uint16
	v6only         bool
	ttl            uint8
	multicastTTL   uint8
	multicastAddr  tcpip.Address
	multicastNICID tcpip.NICID
	multicastLoop  bool
	reusePort      bool
	bindToDevice   tcpip.NICID
	broadcast      bool
	sendTOS uint8
	shutdownFlags tcpip.ShutdownFlags
	multicastMemberships []multicastMembership
	effectiveNetProtos []tcpip.NetworkProtocolNumber
	stats tcpip.TransportEndpointStats
}
type multicastMembership struct {
	nicID         tcpip.NICID
	multicastAddr tcpip.Address
}
func newEndpoint(s *stack.Stack, netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) *endpoint {
	return &endpoint{
		stack: s,
		TransportEndpointInfo: stack.TransportEndpointInfo{
			NetProto:   netProto,
			TransProto: header.UDPProtocolNumber,
		},
		waiterQueue: waiterQueue,
		multicastTTL:  1,
		multicastLoop: true,
		rcvBufSizeMax: 32 * 1024,
		sndBufSize:    32 * 1024,
		state:         StateInitial,
		uniqueID:      s.UniqueID(),
	}
}
func (e *endpoint) UniqueID() uint64 {
	return e.uniqueID
}
func (e *endpoint) Close() {
	e.mu.Lock()
	e.shutdownFlags = tcpip.ShutdownRead | tcpip.ShutdownWrite
	switch e.state {
	case StateBound, StateConnected:
		e.stack.UnregisterTransportEndpoint(e.RegisterNICID, e.effectiveNetProtos, ProtocolNumber, e.ID, e, e.bindToDevice)
		e.stack.ReleasePort(e.effectiveNetProtos, ProtocolNumber, e.ID.LocalAddress, e.ID.LocalPort, e.bindToDevice)
	}
	for _, mem := range e.multicastMemberships {
		e.stack.LeaveGroup(e.NetProto, mem.nicID, mem.multicastAddr)
	}
	e.multicastMemberships = nil
	e.rcvMu.Lock()
	e.rcvClosed = true
	e.rcvBufSize = 0
	for !e.rcvList.Empty() {
		p := e.rcvList.Front()
		e.rcvList.Remove(p)
	}
	e.rcvMu.Unlock()
	e.route.Release()
	e.state = StateClosed
	e.mu.Unlock()
	e.waiterQueue.Notify(waiter.EventHUp | waiter.EventErr | waiter.EventIn | waiter.EventOut)
}
func (e *endpoint) ModerateRecvBuf(copied int) {}
func (e *endpoint) IPTables() (iptables.IPTables, error) {
	return e.stack.IPTables(), nil
}
func (e *endpoint) Read(addr *tcpip.FullAddress) (buffer.View, tcpip.ControlMessages, *tcpip.Error) {
	e.rcvMu.Lock()
	if e.rcvList.Empty() {
		err := tcpip.ErrWouldBlock
		if e.rcvClosed {
			e.stats.ReadErrors.ReadClosed.Increment()
			err = tcpip.ErrClosedForReceive
		}
		e.rcvMu.Unlock()
		return buffer.View{}, tcpip.ControlMessages{}, err
	}
	p := e.rcvList.Front()
	e.rcvList.Remove(p)
	e.rcvBufSize -= p.data.Size()
	e.rcvMu.Unlock()
	if addr != nil {
		*addr = p.senderAddress
	}
	return p.data.ToView(), tcpip.ControlMessages{HasTimestamp: true, Timestamp: p.timestamp}, nil
}