xbox-kernel/private/ntos/xnet/winsock/send.c
2020-09-30 17:17:25 +02:00

338 lines
8.0 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
send.c
Abstract:
Implementation of data transmission related Winsock APIs:
send
sendto
WSASend
WSASendTo
Revision History:
06/02/2000 davidx
Created it.
--*/
#include "precomp.h"
// Disable unreferenced label warning
#pragma warning(disable: 4102)
PRIVATE NTSTATUS
SockSend(
PCB* pcb,
SENDREQ* sendreq
)
/*++
Routine Description:
Internal function for sending out data from a socket
Arguments:
pcb - Points to the protocol control block
sendreq - Points to send user request information
Return Value:
Status code
--*/
{
NTSTATUS status;
WSAOVERLAPPED* overlapped;
if (!IsPcbConnected(pcb) && !sendreq->toaddr)
return NETERR(WSAENOTCONN);
if (IsPcbSendShutdown(pcb)) {
return !NT_SUCCESS(PcbGetErrStatus(pcb)) ?
PcbGetErrStatus(pcb) :
NETERR(WSAESHUTDOWN);
}
if ((overlapped = sendreq->overlapped) != NULL) {
sendreq->overlappedEvent = GetKernelEventObject(overlapped->hEvent);
if (!sendreq->overlappedEvent)
return NETERR(WSASYSCALLFAILURE);
}
// Check if we have room in the send buffer
if (IsPcbSendBufFull(pcb)) {
if (overlapped) {
// Queue the overlapped send request.
KeClearEvent(sendreq->overlappedEvent);
status = PcbQueueOverlappedSend(pcb, sendreq);
// The overlapped request was successfully queued up
if (status == NETERR_PENDING)
return status;
// The overlapped send request wasn't queued
// because of an error.
if (!NT_SUCCESS(status)) goto exit;
// The send buffer has opened up and
// the overlapped send request wasn't queued.
} else if (pcb->nonblocking || HasOverlappedSend(pcb)) {
return NETERR_WOULDBLOCK;
} else {
status = PcbWaitForEvent(pcb, PCBEVENT_WRITE, pcb->sendTimeout);
if (!NT_SUCCESS(status)) return status;
}
}
status = IsDgramPcb(pcb) ?
PcbSendDgram(pcb, sendreq) :
TcbSend((TCB*) pcb, sendreq);
exit:
if (overlapped) {
// An overlapped call was completed immediately
overlapped->_ioflags = 0;
overlapped->_ioxfercnt = sendreq->sendtotal;
overlapped->_iostatus = status;
// It would seem to be a waste to signal the event here.
// But that's win2k behavior.
KeSetEvent(sendreq->overlappedEvent, 0, FALSE);
ObDereferenceObject(sendreq->overlappedEvent);
}
return status;
}
int WSAAPI
send(
SOCKET s,
const char* buf,
int len,
int flags
)
/*++
Routine Description:
Refer to XAPI SDK documentation.
--*/
{
WSABUF wsabuf;
SENDREQ sendreq;
WinsockApiPrologSockLock_(send, SOCKET_ERROR);
WinsockApiParamCheck_(
(len > 0 && buf != NULL || len == 0) &&
(flags == 0));
wsabuf.len = len;
wsabuf.buf = (char*) buf;
sendreq.overlapped = NULL;
sendreq.bufs = &wsabuf;
sendreq.bufcnt = 1;
sendreq.sendtotal = len;
sendreq.toaddr = NULL;
err = SockSend(pcb, &sendreq);
MapNtStatusToWinsockError_(err);
WinsockApiExitSockUnlock_(sendreq.sendtotal, SOCKET_ERROR);
}
//
// Count the total number of bytes to send
//
INLINE UINT SockCountSendTotal(WSABUF* bufs, UINT bufcnt) {
UINT total = 0;
while (bufcnt--) {
total += bufs->len;
bufs++;
}
return total;
}
INLINE BOOL SockCheckSendWsaBuf(WSABUF* bufs, UINT bufcnt) {
if (bufs == NULL || bufcnt == 0)
return FALSE;
while (bufcnt--) {
if (bufs->len && bufs->buf == NULL)
return FALSE;
bufs++;
}
return TRUE;
}
int WSAAPI
WSASend(
SOCKET s,
LPWSABUF bufs,
DWORD bufcnt,
LPDWORD bytesSent,
DWORD flags,
LPWSAOVERLAPPED overlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE completionproc
)
/*++
Routine Description:
Refer to XAPI SDK documentation.
--*/
{
SENDREQ sendreq;
WinsockApiPrologSockLock_(WSASend, SOCKET_ERROR);
WinsockApiParamCheck_(
SockCheckSendWsaBuf(bufs, bufcnt) &&
bytesSent != NULL &&
flags == 0 &&
completionproc == NULL);
sendreq.overlapped = overlapped;
sendreq.bufs = bufs;
sendreq.bufcnt = bufcnt;
sendreq.sendtotal = SockCountSendTotal(bufs, bufcnt);
sendreq.toaddr = NULL;
err = SockSend(pcb, &sendreq);
if (NT_SUCCESS(err)) *bytesSent = sendreq.sendtotal;
MapNtStatusToWinsockError_(err);
WinsockApiExitSockUnlock_(NO_ERROR, SOCKET_ERROR);
}
int WSAAPI
sendto(
SOCKET s,
const char* buf,
int len,
int flags,
const struct sockaddr* to,
int tolen
)
/*++
Routine Description:
Refer to XAPI SDK documentation.
--*/
{
DWORD count;
INT err;
WSABUF wsabuf;
WinsockApiPrologLight_(sendto);
WinsockApiParamCheck_(len > 0 && buf != NULL || len == 0);
wsabuf.len = len;
wsabuf.buf = (char*) buf;
err = WSASendTo(s, &wsabuf, 1, &count, flags, to, tolen, NULL, NULL);
return (err == NO_ERROR) ? count : SOCKET_ERROR;
}
int WSAAPI
WSASendTo(
SOCKET s,
LPWSABUF bufs,
DWORD bufcnt,
LPDWORD bytesSent,
DWORD flags,
const struct sockaddr* toaddr,
int tolen,
LPWSAOVERLAPPED overlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE completionproc
)
/*++
Routine Description:
Refer to XAPI SDK documentation.
--*/
{
struct sockaddr_in* sin = (struct sockaddr_in*) toaddr;
SENDREQ sendreq;
WinsockApiPrologSockLock_(WSASendTo, SOCKET_ERROR);
WinsockApiParamCheck_(
SockCheckSendWsaBuf(bufs, bufcnt) &&
bytesSent != NULL &&
flags == 0 &&
(toaddr == NULL ||
tolen >= SOCKADDRLEN && sin->sin_family == AF_INET) &&
completionproc == NULL);
sendreq.overlapped = overlapped;
sendreq.bufs = bufs;
sendreq.bufcnt = bufcnt;
sendreq.sendtotal = SockCountSendTotal(bufs, bufcnt);
if (IsTcb(pcb) || toaddr == NULL) {
// For TCP sockets, WSASendTo is equivalent to WSASend.
// We simply ignore lpTo and iToLen parameters.
//
// Also, if toaddr parameter is NULL, we treat WSASendTo
// the same way as WSASend.
sendreq.toaddr = NULL;
err = SockSend(pcb, &sendreq);
} else {
// Must do this check because downstream code
// doesn't expect the destination address to be 0.
// Also, we consider sending to UDP port 0 an error.
if (sin->sin_addr.s_addr == 0 ||
sin->sin_port == 0 && pcb->type == SOCK_DGRAM) {
WinsockApiGotoExit_(WSAEADDRNOTAVAIL);
}
// Is this socket allowed to send broadcast
// datagrams on this socket?
if (IS_BCAST_IPADDR(sin->sin_addr.s_addr) && !pcb->broadcast) {
WinsockApiGotoExit_(WSAEACCES);
}
// If the socket is not bound, bind it here
if (!IsPcbBound(pcb)) {
err = PcbBind(pcb, 0, 0);
if (!NT_SUCCESS(err)) {
MapNtStatusToWinsockError_(err);
goto exit;
}
}
// Send to the new destination
sendreq.toaddr = sin;
err = SockSend(pcb, &sendreq);
}
if (NT_SUCCESS(err)) *bytesSent = sendreq.sendtotal;
MapNtStatusToWinsockError_(err);
WinsockApiExitSockUnlock_(NO_ERROR, SOCKET_ERROR);
}