// Copyright 2023 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package stack

import (
	"syscall"

	"gvisor.dev/gvisor/pkg/abi/linux"
	"gvisor.dev/gvisor/pkg/abi/linux/errno"
	"gvisor.dev/gvisor/pkg/binary"
	"gvisor.dev/gvisor/pkg/context"
	"gvisor.dev/gvisor/pkg/errors/linuxerr"
	"gvisor.dev/gvisor/pkg/hostarch"
	"gvisor.dev/gvisor/pkg/marshal"
	"gvisor.dev/gvisor/pkg/marshal/primitive"
	"gvisor.dev/gvisor/pkg/safemem"
	"gvisor.dev/gvisor/pkg/sentry/arch"
	"gvisor.dev/gvisor/pkg/sentry/fsimpl/sockfs"
	"gvisor.dev/gvisor/pkg/sentry/inet"
	"gvisor.dev/gvisor/pkg/sentry/kernel"
	"gvisor.dev/gvisor/pkg/sentry/ktime"
	"gvisor.dev/gvisor/pkg/sentry/socket"
	"gvisor.dev/gvisor/pkg/sentry/socket/plugin"
	"gvisor.dev/gvisor/pkg/sentry/socket/plugin/cgo"
	"gvisor.dev/gvisor/pkg/sentry/unimpl"
	"gvisor.dev/gvisor/pkg/sentry/vfs"
	"gvisor.dev/gvisor/pkg/syserr"
	"gvisor.dev/gvisor/pkg/usermem"
	"gvisor.dev/gvisor/pkg/waiter"
)

type socketOperations struct {
	vfsfd vfs.FileDescription
	vfs.FileDescriptionDefaultImpl
	vfs.DentryMetadataFileDescriptionImpl
	vfs.LockFD
	socket.SendReceiveTimeout

	family   int
	skType   linux.SockType
	protocol int

	// fd holds the current socket fd created by plugin stack.
	fd uint32 `state:"nosave"`

	// eventInfo holds current socket fd's corresponding notification queue
	// and events status. It will be used to interact with plugin
	// network stack for event reporting.
	eventInfo plugin.EventInfo
}

var _ = socket.Socket(&socketOperations{})

const (
	sizeofSockaddr = syscall.SizeofSockaddrInet6

	// Lo IF index.
	ifLoIndex = 1
)

func newSocket(t *kernel.Task, family int, skType linux.SockType, protocol int, notifier *Notifier, fd int, flags uint32) (*vfs.FileDescription, *syserr.Error) {
	mnt := t.Kernel().SocketMount()
	d := sockfs.NewDentry(t, mnt)
	defer d.DecRef(t)

	switch skType {
	case syscall.SOCK_STREAM:
	case syscall.SOCK_DGRAM:
	default:
		return nil, syserr.ErrSocketNotSupported
	}

	wq := &waiter.Queue{}
	sop := &socketOperations{
		family:    family,
		fd:        uint32(fd),
		protocol:  protocol,
		skType:    skType,
		eventInfo: plugin.EventInfo{Wq: wq},
	}

	sop.LockFD.Init(&vfs.FileLocks{})

	vfsfd := &sop.vfsfd
	if err := vfsfd.Init(sop, linux.O_RDWR|(flags&linux.O_NONBLOCK), t.Credentials(), mnt, d, &vfs.FileDescriptionOptions{
		DenyPRead:         true,
		DenyPWrite:        true,
		UseDentryMetadata: true,
		SpecialFile:       true,
	}); err != nil {
		return nil, syserr.FromError(err)
	}

	notifier.AddFD(uint32(fd), &sop.eventInfo)

	return vfsfd, nil
}

// Bind implements socket.Socket.Bind.
func (s *socketOperations) Bind(t *kernel.Task, sockaddr []byte) *syserr.Error {
	return int2err(cgo.Bind(s.fd, sockaddr))
}

// Listen implements socket.Socket.Listen.
func (s *socketOperations) Listen(t *kernel.Task, backlog int) *syserr.Error {
	return int2err(cgo.Listen(s.fd, backlog))
}

// Accept implements socket.Socket.Accept.
func (s *socketOperations) Accept(t *kernel.Task, peerRequested bool, flags int, blocking bool) (int32, linux.SockAddr, uint32, *syserr.Error) {
	peerAddrBuf := make([]byte, sizeofSockaddr)
	peerAddrlen := uint32(len(peerAddrBuf))
	peerAddrPtr := &peerAddrBuf[0]
	peerAddrlenPtr := &peerAddrlen

	rc := cgo.Accept(s.fd, peerAddrPtr, peerAddrlenPtr)
	if blocking {
		for rc < 0 && (-rc) == errno.EAGAIN {
			if err := s.waitEvent(t, waiter.EventIn); err != nil {
				return 0, nil, 0, syserr.FromError(err)
			}

			rc = cgo.Accept(s.fd, peerAddrPtr, peerAddrlenPtr)
		}
	}

	if rc < 0 {
		return 0, nil, 0, int2err(rc)
	}

	f, err := newSocket(t, s.family, s.skType, s.protocol, stack.notifier, int(rc), uint32(flags&syscall.SOCK_NONBLOCK))
	if err != nil {
		cgo.Close(uint32(rc))
		return 0, nil, 0, err
	}
	defer f.DecRef(t)

	kfd, kerr := t.NewFDFrom(0, f, kernel.FDFlags{
		CloseOnExec: flags&syscall.SOCK_CLOEXEC != 0,
	})
	if kerr != nil {
		cgo.Close(uint32(rc))
		return 0, nil, 0, syserr.FromError(kerr)
	}

	t.Kernel().RecordSocket(f)

	if !peerRequested {
		return kfd, nil, 0, nil
	}

	peerAddr := socket.UnmarshalSockAddr(s.family, peerAddrBuf[:peerAddrlen])
	return kfd, peerAddr, peerAddrlen, nil
}

// Connect implements socket.Socket.Connect.
func (s *socketOperations) Connect(t *kernel.Task, sockaddr []byte, blocking bool) *syserr.Error {
	var ret int64

	if !blocking {
		ret = cgo.Connect(s.fd, sockaddr)

		if ret == 0 {
			return nil
		}
		return int2err(ret)
	}

	ret = cgo.Connect(s.fd, sockaddr)

	if ret == 0 {
		return nil
	} else if ret < 0 && (-ret) != errno.EINPROGRESS {
		// Return error if errno is EALREADY/EISCONN/ECONNREFUSED/EINVAL
		return int2err(ret)
	}

	if err := s.waitEvent(t, waiter.EventOut); err != nil {
		return syserr.FromError(err)
	}

	// Call connect() again after blocking to find connect's result.
	ret = cgo.Connect(s.fd, sockaddr)
	if ret == 0 {
		return nil
	} else if ret < 0 && (-ret) == errno.EISCONN {
		return nil
	}
	return int2err(ret)
}

// Shutdown implements socket.Socket.Shutdown.
func (s *socketOperations) Shutdown(t *kernel.Task, how int) *syserr.Error {
	ret := cgo.Shutdown(s.fd, how)
	return int2err(ret)
}

// GetSockOpt implements socket.Socket.GetSockOpt.
func (s *socketOperations) GetSockOpt(t *kernel.Task, level int, name int, outPtr hostarch.Addr, outLen int) (marshal.Marshallable, *syserr.Error) {
	optVal := make([]byte, outLen)
	if outLen == 0 {
		optPtr := primitive.ByteSlice(optVal)
		return &optPtr, nil
	}
	rc, outLen := cgo.Getsockopt(s.fd, level, name, optVal, outLen)
	if rc < 0 {
		return nil, int2err(rc)
	}
	optPtr := primitive.ByteSlice(optVal[:outLen])
	return &optPtr, nil
}

// SetSockOpt implements socket.Socket.SetSockOpt.
func (s *socketOperations) SetSockOpt(t *kernel.Task, level int, name int, optVal []byte) *syserr.Error {
	rc := cgo.Setsockopt(s.fd, level, name, optVal)
	return int2err(rc)
}

// State implements socket.Socket.State.
func (s *socketOperations) State() uint32 {
	optVal := make([]byte, 1)
	rc, _ := cgo.Getsockopt(s.fd, syscall.SOL_TCP, syscall.TCP_INFO, optVal, 1)
	if rc < 0 {
		return 0
	}
	return uint32(optVal[0])
}

// Type implements socket.Socket.Type.
func (s *socketOperations) Type() (family int, skType linux.SockType, protocol int) {
	return s.family, s.skType, s.protocol
}

// OnClose implements vfs.FileDescriptionImpl.OnClose.
func (s *socketOperations) OnClose(ctx context.Context) error {
	return nil
}

// EventRegister implements waiter.Waitable.EventRegister.
func (s *socketOperations) EventRegister(e *waiter.Entry) error {
	s.eventInfo.Wq.EventRegister(e)
	stack.notifier.UpdateFD(s.fd)
	return nil
}

// EventUnregister implements waiter.Waitable.EventUnregister.
func (s *socketOperations) EventUnregister(e *waiter.Entry) {
	s.eventInfo.Wq.EventUnregister(e)
	stack.notifier.UpdateFD(s.fd)
}

// Readiness implements socket.Socket.Readiness.
func (s *socketOperations) Readiness(mask waiter.EventMask) waiter.EventMask {
	var events waiter.EventMask

	evInfo := &s.eventInfo
	iomask := mask & (waiter.EventIn | waiter.EventOut)

	// Fast path condition:
	// 1. The event needed has been reported by plugin stack io-thread; or
	// 2. POLLIN or POLLOUT event has been reported by io-thread.
	// Directly report current event without invoking cgo Readiness again.
	if evInfo.Ready&iomask == iomask {
		events = evInfo.Ready & mask
		// Clear plugin stack eventInfo record after consuming IN/OUT event.
		evInfo.Ready &= ^iomask
	} else {
		events = waiter.EventMask(cgo.Readiness(s.fd, uint64(mask)))
	}

	return events
}

// Epollable implements socket.Socket.Epollable.
func (s *socketOperations) Epollable() bool {
	return true
}

// Refers to implementation in epsocket
func interfaceIoctl(ctx context.Context, io usermem.IO, cmd uint32, ifr *linux.IFReq) *syserr.Error {
	stack := inet.StackFromContext(ctx)
	if stack == nil {
		return syserr.ErrNoDevice
	}

	// SIOCGIFNAME uses ifr.ifr_ifindex rather than ifr.ifr_name to
	// identify a device.
	if cmd == syscall.SIOCGIFNAME {
		// Gets the name of the interface given the interface index
		// stored in ifr_ifindex.
		index := int32(hostarch.ByteOrder.Uint32(ifr.Data[:4]))
		if iface, ok := stack.Interfaces()[index]; ok {
			ifr.SetName(iface.Name)
			return nil
		}
		return syserr.ErrNoDevice
	}

	// Find the relevant device.
	var iface inet.Interface
	index := int32(-1)
	for i, iface := range stack.Interfaces() {
		if iface.Name == ifr.Name() {
			index = i
			break
		}
	}
	if index == -1 {
		return syserr.ErrNoDevice
	}

	switch cmd {
	case syscall.SIOCGIFINDEX:
		// Copy out the index to the data.
		hostarch.ByteOrder.PutUint32(ifr.Data[:], uint32(index))

	case syscall.SIOCGIFHWADDR:
		// Use IEEE802.3 instead of IEEE802.2 ARP type
		// so that ifconfig command can recognize it
		devType := 1
		if index == ifLoIndex {
			devType = 772 // Loopback
		}
		hostarch.ByteOrder.PutUint16(ifr.Data[:2], uint16(devType))
		n := copy(ifr.Data[2:], iface.Addr)
		for i := 2 + n; i < len(ifr.Data); i++ {
			ifr.Data[i] = 0 // Clear padding.
		}

	case syscall.SIOCGIFFLAGS:
		f := iface.Flags
		// Drop the flags that don't fit in the size that we need to
		// return. This matches Linux behavior.
		hostarch.ByteOrder.PutUint16(ifr.Data[:2], uint16(f))

	case syscall.SIOCGIFADDR:
		// Copy the IPv4 address out.
		for _, addr := range stack.InterfaceAddrs()[index] {
			// This ioctl is only compatible with AF_INET addresses.
			if addr.Family != linux.AF_INET {
				continue
			}
			copyAddrOut(ifr, &addr)
			break
		}

	case syscall.SIOCGIFMETRIC:
		// Gets the metric of the device. As per netdevice(7), this
		// always just sets ifr_metric to 0.
		hostarch.ByteOrder.PutUint32(ifr.Data[:4], 0)

	case syscall.SIOCGIFMTU:
		// Gets the MTU of the device.
		hostarch.ByteOrder.PutUint32(ifr.Data[:4], iface.MTU)

	case syscall.SIOCGIFMAP:
		// Gets the hardware parameters of the device.
		hostarch.ByteOrder.PutUint64(ifr.Data[:8], 0)
		hostarch.ByteOrder.PutUint32(ifr.Data[8:12], 0)
		ifr.Data[12] = 0

	case syscall.SIOCGIFTXQLEN:
		hostarch.ByteOrder.PutUint32(ifr.Data[:4], 1024)

	case syscall.SIOCGIFDSTADDR:
		// Gets the destination address of a point-to-point device.
		// TODO: Implement SIOCGIFDSTADDR handler.

	case syscall.SIOCGIFBRDADDR:
		// Gets the broadcast address of a device.
		// TODO: Implement SIOCGIFBRDADDR handler.

	case syscall.SIOCGIFNETMASK:
		// Gets the network mask of a device.
		for _, addr := range stack.InterfaceAddrs()[index] {
			// This ioctl is only compatible with AF_INET addresses.
			if addr.Family != linux.AF_INET {
				continue
			}
			// Populate ifr.ifr_netmask (type sockaddr).
			hostarch.ByteOrder.PutUint16(ifr.Data[0:2], uint16(addr.Family))
			hostarch.ByteOrder.PutUint16(ifr.Data[2:4], 0)
			// Netmask is expected to be returned as a big endian value.
			mask := uint32(0xffffffff << (32 - addr.PrefixLen))
			binary.BigEndian.PutUint32(ifr.Data[4:8], mask)
			break
		}

	default:
		// Not a valid call.
		return syserr.ErrInvalidArgument
	}

	return nil
}

func ifconfIoctlFromStack(ctx context.Context, io usermem.IO, ifc *linux.IFConf) error {
	// If Ptr is NULL, return the necessary buffer size via Len.
	// Otherwise, write up to Len bytes starting at Ptr containing ifreq
	// structs.
	t := ctx.(*kernel.Task)
	s := t.NetworkContext().(*Stack)
	if s == nil {
		return syserr.ErrNoDevice.ToError()
	}

	if ifc.Ptr == 0 {
		ifc.Len = int32(len(s.Interfaces())) * int32(linux.SizeOfIFReq)
		return nil
	}

	max := ifc.Len
	ifc.Len = 0
	ifaces := s.Interfaces()
	for _, idx := range s.InterfaceIDs() {
		iface := ifaces[idx]
		ifaceAddrs := s.InterfaceAddrs()[idx]
		for _, ifaceAddr := range ifaceAddrs {
			if ifaceAddr.Family != syscall.AF_INET {
				continue
			}

			// Don't write past the end of the buffer.
			if ifc.Len+int32(linux.SizeOfIFReq) > max {
				break
			}

			// Populate ifr.ifr_addr.
			ifr := linux.IFReq{}
			ifr.SetName(iface.Name)
			copyAddrOut(&ifr, &ifaceAddr)

			// Copy the ifr to userspace.
			dst := uintptr(ifc.Ptr) + uintptr(ifc.Len)
			ifc.Len += int32(linux.SizeOfIFReq)
			if _, err := ifr.CopyOut(t, hostarch.Addr(dst)); err != nil {
				return err
			}
		}
	}
	return nil
}

// Ioctl implements socket.Socket.Ioctl.
func (s *socketOperations) Ioctl(ctx context.Context, io usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) {
	cmd := uint32(args[1].Int())
	arg := args[2].Pointer()

	var buf []byte
	switch cmd {
	case syscall.SIOCGIFADDR,
		syscall.SIOCGIFBRDADDR,
		syscall.SIOCGIFDSTADDR,
		syscall.SIOCGIFFLAGS,
		syscall.SIOCGIFHWADDR,
		syscall.SIOCGIFINDEX,
		syscall.SIOCGIFMAP,
		syscall.SIOCGIFMETRIC,
		syscall.SIOCGIFMTU,
		syscall.SIOCGIFNAME,
		syscall.SIOCGIFNETMASK,
		syscall.SIOCGIFTXQLEN:
		var ifr linux.IFReq
		ifrBuf := ctx.(*kernel.Task).CopyScratchBuffer(linux.SizeOfIFReq)
		if _, err := io.CopyIn(ctx, args[2].Pointer(), ifrBuf, usermem.IOOpts{}); err != nil {
			return 0, err
		}
		// Decode ifr from ifrBuf
		// Note that these code may need to be modified if linux.IFReq struct changes
		copy(ifr.IFName[0:linux.IFNAMSIZ], ifrBuf[0:linux.IFNAMSIZ])
		copy(ifr.Data[0:linux.SizeOfIFReq-linux.IFNAMSIZ], ifrBuf[linux.IFNAMSIZ:linux.SizeOfIFReq])
		if err := interfaceIoctl(ctx, io, cmd, &ifr); err != nil {
			return 0, err.ToError()
		}
		copy(ifrBuf[0:linux.IFNAMSIZ], ifr.IFName[0:linux.IFNAMSIZ])
		copy(ifrBuf[linux.IFNAMSIZ:linux.SizeOfIFReq], ifr.Data[0:linux.SizeOfIFReq-linux.IFNAMSIZ])
		_, err := io.CopyOut(ctx, arg, ifrBuf, usermem.IOOpts{})
		return 0, err

	case syscall.SIOCGIFCONF:
		// SIOCGIFCONF has slightly different behavior than the others, in that it
		// will need to populate the array of ifreqs.
		var ifc linux.IFConf
		ifcBuf := ctx.(*kernel.Task).CopyScratchBuffer(linux.SizeOfIFConf)
		if _, err := io.CopyIn(ctx, arg, ifcBuf, usermem.IOOpts{}); err != nil {
			return 0, err
		}
		// Decode ifc from ifcBuf
		// Note that these code may need to be modified if linux.IFconf struct changes
		ifc.Len = int32(hostarch.ByteOrder.Uint32(ifcBuf[0:4]))
		ifc.Ptr = hostarch.ByteOrder.Uint64(ifcBuf[8:])

		if err := ifconfIoctlFromStack(ctx, io, &ifc); err != nil {
			return 0, err
		}
		hostarch.ByteOrder.PutUint32(ifcBuf[0:4], uint32(ifc.Len))
		hostarch.ByteOrder.PutUint64(ifcBuf[8:], ifc.Ptr)
		_, err := io.CopyOut(ctx, arg, ifcBuf, usermem.IOOpts{})

		return 0, err
	case linux.SIOCGIFMEM, linux.SIOCGIFPFLAGS, linux.SIOCGMIIPHY, linux.SIOCGMIIREG:
		unimpl.EmitUnimplementedEvent(ctx, sysno)
		return 0, linuxerr.ENOTTY
	case syscall.TIOCINQ, syscall.TIOCOUTQ:
		buf = make([]byte, 4)
	case syscall.SIOCGSTAMP:
		buf = make([]byte, 16)
	default:
		return 0, linuxerr.ENOTTY
	}

	rc := cgo.Ioctl(s.fd, cmd, buf)
	if rc < 0 {
		_, err := translateReturn(int64(rc))
		return 0, err
	}

	_, err := io.CopyOut(ctx, arg, buf, usermem.IOOpts{})

	return 0, err
}

// Release implements socket.Socket.Release.
func (s *socketOperations) Release(ctx context.Context) {
	t := kernel.TaskFromContext(ctx)
	t.Kernel().DeleteSocket(&s.vfsfd)
	stack.notifier.RemoveFD(s.fd)
	cgo.Close(s.fd)
}

// GetSockName implements socket.Socket.GetSockName.
func (s *socketOperations) GetSockName(t *kernel.Task) (linux.SockAddr, uint32, *syserr.Error) {
	addrlen := uint32(sizeofSockaddr)
	addr := make([]byte, sizeofSockaddr)
	rc := cgo.Getsockname(s.fd, addr, &addrlen)
	if rc < 0 {
		return nil, 0, int2err(rc)
	}
	return socket.UnmarshalSockAddr(s.family, addr), addrlen, nil
}

// GetPeerName implements socket.Socket.GetPeerName.
func (s *socketOperations) GetPeerName(t *kernel.Task) (linux.SockAddr, uint32, *syserr.Error) {
	addrlen := uint32(sizeofSockaddr)
	addr := make([]byte, sizeofSockaddr)
	rc := cgo.GetPeername(s.fd, addr, &addrlen)
	if rc < 0 {
		return nil, 0, int2err(rc)
	}
	return socket.UnmarshalSockAddr(s.family, addr), addrlen, nil
}

// GetPeerCreds implements socket.Socket.GetPeerCreds.
func (s *socketOperations) GetPeerCreds(t *kernel.Task) (marshal.Marshallable, *syserr.Error) {
	return nil, syserr.ErrInvalidEndpointState
}

// recv is a helper function for doing non-blocking read once.
// It returns:
// 1. number of bytes received;
// 2. sender address for non-stream socket;
// 3. control message;
// 4. message flag;
// 5. internal error if any
func (s *socketOperations) recv(t *kernel.Task, dst usermem.IOSequence, sysflags int, addr []byte, control []byte) (int64, []byte, []byte, int, error) {
	bytes := dst.NumBytes()
	// Directly return if dst is empty.
	if bytes == 0 {
		return 0, addr, control, 0, nil
	}

	// Slow path for non-stream socket(e.g., UDP, raw socket).
	if s.skType != linux.SOCK_STREAM {
		if len(addr) == 0 && s.skType == linux.SOCK_DGRAM {
			addr = make([]byte, sizeofSockaddr)
		}

		tmpBuf := make([]byte, bytes)
		tmpBS := safemem.BlockSeqOf(safemem.BlockFromSafeSlice(tmpBuf))
		iovs := iovecsFromBlockSeq(tmpBS, nil)
		rc, la, lc, flag := cgo.Recvmsg(s.fd, iovs, addr, control, sysflags)
		if rc < 0 {
			_, err := translateReturn(rc)
			return 0, addr /* return the original slice */, control /* return the original slice */, 0, err
		}

		n, err := dst.CopyOut(t, tmpBuf[:rc])
		return int64(n), addr[:la], control[:lc], flag, err
	}

	// Fast path for stream socket(e.g., TCP socket).
	rw := getReadWriter(s.fd)
	rw.to = control
	rw.flags = uint32(sysflags)

	n, err := dst.CopyOutFrom(t, rw)

	control = rw.to
	msgFlags := int(rw.flags)
	putReadWriter(rw)

	return n, nil /* ignore for TCP */, control, msgFlags, err
}

// RecvMsg implements socket.Socket.RecvMsg.
func (s *socketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags int, haveDeadline bool, deadline ktime.Time, senderRequested bool, controlDataLen uint64) (int, int, linux.SockAddr, uint32, socket.ControlMessages, *syserr.Error) {
	var addr, control []byte
	if senderRequested {
		if s.skType != linux.SOCK_STREAM {
			addr = make([]byte, sizeofSockaddr)
		} else {
			// According to UNIX98, msg_name/msg_namelen are ignored on connected socket.
			senderRequested = false
		}
	}
	if controlDataLen > 0 {
		control = make([]byte, controlDataLen)
	}

	// Store nonblock info from original flag.
	nonblock := flags&syscall.MSG_DONTWAIT != 0
	waitall := !nonblock && flags&syscall.MSG_WAITALL != 0

	var mflag int
	var n int64
	var err error
	// Always set as non-blocking for recv.
	sysflags := flags | syscall.MSG_DONTWAIT
	n, addr, control, mflag, err = s.recv(t, dst, sysflags, addr, control)

	var senderAddr linux.SockAddr
	var addrlen uint32
	// For non-blocking RecvMsg, we could return after recv once.
	if nonblock {
		if err != nil {
			// Clear controlMessages when error occurs.
			control = nil
		} else if senderRequested && len(addr) > 0 {
			senderAddr = socket.UnmarshalSockAddr(s.family, addr)
			addrlen = uint32(len(addr))
		}
		controlMessages := buildControlMessage(control)
		// The socket is shutdown, report ErrWouldBlock for non-blocking mode.
		if err == error(syscall.ESHUTDOWN) {
			err = linuxerr.ErrWouldBlock
		}
		return int(n), mflag, senderAddr, addrlen, *controlMessages, syserr.FromError(err)
	}

	// For blocking RecvMsg, we need to finish reading all data before return.
	rn := n
	for err == linuxerr.ErrWouldBlock || (waitall && err == nil && rn < dst.NumBytes()) {
		dst = dst.DropFirst(int(rn))
		// Wait on POLLIN event.
		if haveDeadline {
			err = s.waitEventT(t, waiter.EventIn, deadline)
		} else {
			err = s.waitEvent(t, waiter.EventIn)
		}
		if err != nil {
			if n == 0 {
				// There is no POLLIN event reported before timeout.
				if err == linuxerr.ETIMEDOUT {
					err = linuxerr.ErrWouldBlock
				}
				return 0, 0, nil, 0, socket.ControlMessages{}, syserr.FromError(err)
			}
			// Clear event-waiting error.
			err = nil
			mflag = 0
			break
		}
		rn, addr, control, mflag, err = s.recv(t, dst, sysflags, addr, control)
		n += rn
	}

	// Plugin stack finds the socket has been shutdown,
	// report ESHUTDOWN through controlMessages.
	if err == error(syscall.ESHUTDOWN) {
		n = 0
		err = nil
	} else if n > 0 {
		// If any data has been received, clear internal error.
		err = nil
	}

	// For receiving error other than ESHUTDOWN and not receiving any data,
	// clear controlMessages and report through internal error.
	if err != nil {
		control = nil
	} else if senderRequested && len(addr) > 0 {
		senderAddr = socket.UnmarshalSockAddr(s.family, addr)
		addrlen = uint32(len(addr))
	}
	controlMessages := buildControlMessage(control)
	return int(n), mflag, senderAddr, addrlen, *controlMessages, syserr.FromError(err)
}

// Read implements socket.Socket.Read.
func (s *socketOperations) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) {
	if opts.Flags != 0 {
		return 0, linuxerr.EOPNOTSUPP
	}
	nonblock := (s.vfsfd.StatusFlags() & linux.O_NONBLOCK) != 0

	bytes := dst.NumBytes()
	// Directly return if dst is empty.
	if bytes == 0 {
		return 0, nil
	}

	rw := getReadWriter(s.fd)
	n, err := dst.CopyOutFrom(ctx, rw)
	putReadWriter(rw)

	if err == error(syscall.ESHUTDOWN) {
		if nonblock {
			err = linuxerr.ErrWouldBlock
		} else {
			n = 0
			err = nil
		}
	}

	return int64(n), err
}

func (s *socketOperations) send(t *kernel.Task, src usermem.IOSequence, to []byte, sysflags int) (int64, error) {
	bytes := src.NumBytes()
	// If src is empty, let plugin stack handle sending 0-byte data based on protocol.
	if bytes == 0 {
		rc := cgo.Sendto(s.fd, 0, 0, sysflags, to)
		ret, err := translateReturn(rc)
		return int64(ret), err
	}

	rw := getReadWriter(s.fd)
	rw.to = to
	n, err := src.CopyInTo(t, rw)
	putReadWriter(rw)
	return n, err
}

// SendMsg implements socket.Socket.SendMsg.
func (s *socketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, haveDeadline bool, deadline ktime.Time, controlMessages socket.ControlMessages) (int, *syserr.Error) {
	total := src.NumBytes()
	nonblock := flags & syscall.MSG_DONTWAIT
	sysflags := flags | syscall.MSG_DONTWAIT

	n, err := s.send(t, src, to, sysflags)

	if nonblock != 0 {
		return int(n), syserr.FromError(err)
	}

	var sent int64
	for err == nil || err == linuxerr.ErrWouldBlock {
		if n > 0 {
			src = src.DropFirst64(n)
			sent += n
		}

		if sent == total {
			return int(total), nil
		}

		if haveDeadline {
			err = s.waitEventT(t, waiter.EventOut, deadline)
		} else {
			err = s.waitEvent(t, waiter.EventOut)
		}

		if err != nil {
			if err == linuxerr.ETIMEDOUT {
				err = linuxerr.ErrWouldBlock
			}
			return int(sent), syserr.FromError(err)
		}

		n, err = s.send(t, src, to, sysflags)
	}

	return int(sent), syserr.FromError(err)
}

// Write implements socket.Socket.Write.
func (s *socketOperations) Write(ctx context.Context, src usermem.IOSequence, opts vfs.WriteOptions) (int64, error) {
	bytes := src.NumBytes()
	// If src is empty, let plugin stack handle sending 0-byte data based on protocol.
	if bytes == 0 {
		rc := cgo.Write(s.fd, 0, 0)
		ret, err := translateReturn(rc)
		return int64(ret), err
	}

	rw := getReadWriter(s.fd)
	n, err := src.CopyInTo(ctx, rw)
	putReadWriter(rw)
	if n < bytes && err == nil {
		return n, linuxerr.ErrWouldBlock
	}
	return int64(n), err
}

// waitEvent implements blocking wait on given events
// until one of the events have been reported.
func (s *socketOperations) waitEvent(ctx context.Context, event waiter.EventMask) error {
	var err error
	t := ctx.(*kernel.Task)
	e, ch := waiter.NewChannelEntry(event | waiter.EventErr | waiter.EventHUp)
	s.EventRegister(&e)

	// It's possible events happens between last check and EventRegister.
	// If this happens and we don't check readiness again, we would miss
	// the event and get blocked forever.
	if s.Readiness(event|waiter.EventErr|waiter.EventHUp) == 0 {
		err = t.Block(ch)
	}

	s.EventUnregister(&e)
	return err
}

// waitEventT implements blocking wait on given events
// until one of the events have been reported or timeout based on given deadline.
func (s *socketOperations) waitEventT(ctx context.Context, event waiter.EventMask, deadline ktime.Time) error {
	var err error
	t := ctx.(*kernel.Task)
	e, ch := waiter.NewChannelEntry(event | waiter.EventErr | waiter.EventHUp)
	s.EventRegister(&e)

	if s.Readiness(event|waiter.EventErr|waiter.EventHUp) == 0 {
		err = t.BlockWithDeadline(ch, true, deadline)
	}

	s.EventUnregister(&e)
	return err
}
