2495 lines
72 KiB
C++
2495 lines
72 KiB
C++
// ---------------------------------------------------------------------------------------
|
|
// sock.cpp
|
|
//
|
|
// Copyright (C) Microsoft Corporation
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
#include "xnp.h"
|
|
#include "xnver.h"
|
|
|
|
#pragma warning(disable:4102)
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// Trace tags
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
DefineTag(sock, 0);
|
|
DefineTag(sockWarn, TAG_ENABLE);
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// socket
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
SOCKET CXnSock::socket(IN int af, IN int type, IN int protocol)
|
|
{
|
|
WinsockApiProlog_(socket, INVALID_SOCKET);
|
|
|
|
ICHECK(SOCK, USER);
|
|
|
|
CSocket * pSocket;
|
|
|
|
if (af != 0 && af != AF_INET)
|
|
err = WSAEAFNOSUPPORT;
|
|
else if (type != 0 && type != SOCK_STREAM && type != SOCK_DGRAM)
|
|
err = WSAESOCKTNOSUPPORT;
|
|
else if (protocol != 0 && protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
|
|
err = WSAEPROTONOSUPPORT;
|
|
else
|
|
{
|
|
if (type == 0)
|
|
type = (protocol == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
|
|
if (protocol == 0)
|
|
protocol = (type == SOCK_DGRAM) ? IPPROTO_UDP : IPPROTO_TCP;
|
|
if ((type == SOCK_STREAM) != (protocol == IPPROTO_TCP))
|
|
err = WSAEPROTONOSUPPORT;
|
|
}
|
|
|
|
WinsockApiCheckError_(INVALID_SOCKET);
|
|
|
|
pSocket = SockAlloc(type == SOCK_STREAM, FALSE);
|
|
|
|
if (pSocket == NULL)
|
|
{
|
|
WinsockApiReturnError_(WSAENOBUFS, INVALID_SOCKET);
|
|
}
|
|
|
|
RaiseToDpc();
|
|
pSocket->SetFlags(SOCKF_OWNED);
|
|
pSocket->Enqueue(&_leSockets);
|
|
|
|
return((SOCKET)pSocket);
|
|
}
|
|
|
|
CSocket * CXnSock::SockAlloc(BOOL fTcp, BOOL fPoolAlloc)
|
|
{
|
|
ICHECK(SOCK, USER|UDPC|SDPC);
|
|
|
|
CSocket * pSocket;
|
|
UINT cb = fTcp ? sizeof(CTcpSocket) : sizeof(CSocket);
|
|
ULONG tag = fTcp ? PTAG_CTcpSocket : PTAG_CSocket;
|
|
|
|
if (_cSockets >= cfgSockMaxSockets)
|
|
{
|
|
TraceSz(Warning, "Too many sockets");
|
|
return NULL;
|
|
}
|
|
|
|
if (fPoolAlloc)
|
|
pSocket = (CSocket *)PoolAllocZ(cb, tag);
|
|
else
|
|
pSocket = (CSocket *)SysAllocZ(cb, tag);
|
|
|
|
if (pSocket)
|
|
{
|
|
pSocket->Init(this, fTcp, fPoolAlloc);
|
|
|
|
if (fTcp)
|
|
{
|
|
((CTcpSocket *)pSocket)->TcpInit(this);
|
|
}
|
|
|
|
InterlockedIncrement((LONG *)&_cSockets);
|
|
}
|
|
|
|
return(pSocket);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// closesocket
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
int CXnSock::closesocket(IN SOCKET s)
|
|
{
|
|
WinsockApiPrologSockLock_(closesocket, SOCKET_ERROR);
|
|
|
|
RaiseToDpc();
|
|
|
|
if (pSocket == NULL)
|
|
err = NETERR_PARAM;
|
|
else if (pSocket->TestFlags(SOCKF_CLOSED))
|
|
{
|
|
SockFree(pSocket);
|
|
err = 0;
|
|
}
|
|
else
|
|
{
|
|
pSocket->ClearFlags(SOCKF_OWNED);
|
|
err = SockClose(pSocket, FALSE);
|
|
}
|
|
|
|
MapNtStatusToWinsockError_(err);
|
|
|
|
// Don't call SockUnlock here because the socket is most likely deallocated by now
|
|
|
|
WinsockApiCheckError_(SOCKET_ERROR);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
NTSTATUS CXnSock::SockClose(CSocket * pSocket, BOOL fForce)
|
|
{
|
|
RaiseToDpc();
|
|
|
|
// If a TCP is being gracefully closed, then we'll
|
|
// leave the the CSocket structure in the global list
|
|
// until the connection is really gone.
|
|
if (pSocket->IsTcp() && !TcpClose((CTcpSocket *)pSocket, fForce))
|
|
{
|
|
pSocket->SetFlags(SOCKF_NOMORE_XMIT|SOCKF_NOMORE_RECV|SOCKF_LINGERING);
|
|
pSocket->SetClosed();
|
|
return(NETERR_OK);
|
|
}
|
|
|
|
pSocket->Dequeue();
|
|
|
|
// Clean up the information associated with the CSocket
|
|
SockCleanup(pSocket);
|
|
|
|
Assert(!pSocket->IsTcp() || !((CTcpSocket *)pSocket)->GetTimer()->IsActive());
|
|
|
|
EvtTerm(pSocket->GetEvent());
|
|
|
|
InterlockedDecrement((LONG *)&_cSockets);
|
|
|
|
if (pSocket->TestFlags(SOCKF_OWNED))
|
|
pSocket->SetFlags(SOCKF_CLOSED);
|
|
else
|
|
SockFree(pSocket);
|
|
|
|
return(NETERR_OK);
|
|
}
|
|
|
|
void CXnSock::SockFree(CSocket * pSocket)
|
|
{
|
|
// Mark socket as closed even though we are freeing the memory just in case the
|
|
// user tries to access it anyways.
|
|
|
|
pSocket->SetClosed();
|
|
|
|
if (pSocket->TestFlags(SOCKF_POOLALLOC))
|
|
PoolFree(pSocket);
|
|
else
|
|
SysFree(pSocket);
|
|
}
|
|
|
|
void CXnSock::SockCleanup(CSocket * pSocket)
|
|
{
|
|
ICHECK(SOCK, UDPC|SDPC);
|
|
|
|
NTSTATUS status;
|
|
|
|
status = NT_SUCCESS(pSocket->GetStatus()) ? NETERR_CANCELLED : pSocket->GetStatus();
|
|
|
|
SockReqComplete(pSocket, pSocket->GetRecvReq(), status);
|
|
SockReqComplete(pSocket, pSocket->GetSendReq(), status);
|
|
|
|
// Flush receive buffers
|
|
|
|
SockFlushRecvBuffers(pSocket);
|
|
|
|
// Flush send buffers
|
|
|
|
while (pSocket->HasSendBuf())
|
|
{
|
|
CSendBuf * pSendBuf = pSocket->DequeueSendBuf();
|
|
SockReleaseSendBuf(pSendBuf);
|
|
}
|
|
|
|
pSocket->SetCbSendBuf(0);
|
|
|
|
if (pSocket->_prte)
|
|
{
|
|
RouteRelease(pSocket->_prte);
|
|
pSocket->_prte = NULL;
|
|
}
|
|
}
|
|
|
|
void CXnSock::SockReleaseSendBuf(CSendBuf * pSendBuf)
|
|
{
|
|
if (pSendBuf->Release() == 0)
|
|
PacketFree(pSendBuf);
|
|
else
|
|
pSendBuf->SetPfn((PFNPKTFREE)PacketFree);
|
|
}
|
|
|
|
//
|
|
// Flush a socket's receive buffers
|
|
//
|
|
void CXnSock::SockFlushRecvBuffers(CSocket * pSocket)
|
|
{
|
|
while (pSocket->HasRecvBuf())
|
|
{
|
|
PoolFree(pSocket->DequeueRecvBuf());
|
|
}
|
|
|
|
pSocket->SetCbRecvBuf(0);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// shutdown
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
int CXnSock::shutdown(IN SOCKET s, IN int how)
|
|
{
|
|
DWORD dwFlags;
|
|
WinsockApiPrologSockLock_(shutdown, SOCKET_ERROR);
|
|
|
|
WinsockApiParamCheck_(
|
|
how == SD_SEND ||
|
|
how == SD_RECEIVE ||
|
|
how == SD_BOTH);
|
|
|
|
switch (how) {
|
|
case SD_SEND:
|
|
dwFlags = SOCKF_NOMORE_XMIT;
|
|
break;
|
|
|
|
case SD_RECEIVE:
|
|
dwFlags = SOCKF_NOMORE_RECV;
|
|
break;
|
|
|
|
default:
|
|
dwFlags = SOCKF_NOMORE_XMIT|SOCKF_NOMORE_RECV;
|
|
break;
|
|
}
|
|
|
|
if (pSocket->IsUdp())
|
|
{
|
|
err = UdpShutdown(pSocket, dwFlags);
|
|
}
|
|
else
|
|
{
|
|
if (!pSocket->TestFlags(SOCKF_CONNECTED))
|
|
{
|
|
WinsockApiGotoExit_(WSAENOTCONN);
|
|
}
|
|
err = TcpShutdown((CTcpSocket*) pSocket, dwFlags, TRUE);
|
|
}
|
|
|
|
MapNtStatusToWinsockError_(err);
|
|
WinsockApiExitSockUnlock_(NO_ERROR, SOCKET_ERROR);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// ioctlsocket
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
int CXnSock::ioctlsocket(SOCKET s, long cmd, u_long* argp)
|
|
{
|
|
KIRQL irql;
|
|
UINT bytesReady;
|
|
|
|
WinsockApiPrologSockLock_(ioctlsocket, SOCKET_ERROR);
|
|
WinsockApiParamCheck_(argp != NULL);
|
|
|
|
switch (cmd) {
|
|
case FIONBIO:
|
|
pSocket->SetFlags((*argp) ? SOCKF_OPT_NONBLOCKING : 0, SOCKF_OPT_NONBLOCKING);
|
|
break;
|
|
|
|
case FIONREAD:
|
|
{
|
|
RaiseToDpc();
|
|
|
|
if (pSocket->IsUdp())
|
|
{
|
|
// For UDP sockets, return the size of the first buffered datagram
|
|
// NOTE: For compatibility with win2k behavior, if the first datagram
|
|
// is 0-sized, we'll return 1.
|
|
if (pSocket->IsUdpRecvBufEmpty())
|
|
{
|
|
bytesReady = 0;
|
|
}
|
|
else
|
|
{
|
|
bytesReady = ((CUdpRecvBuf *)pSocket->GetRecvBufFirst())->GetCbBuf();
|
|
if (bytesReady == 0)
|
|
bytesReady = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// For TCP sockets, return the total number of bytes
|
|
// available for reading.
|
|
bytesReady = pSocket->GetCbRecvBuf();
|
|
}
|
|
*argp = bytesReady;
|
|
break;
|
|
}
|
|
|
|
// case SIOCATMARK:
|
|
default:
|
|
WinsockApiGotoExit_(WSAENOPROTOOPT);
|
|
break;
|
|
}
|
|
|
|
WinsockApiExitSockUnlock_(NO_ERROR, SOCKET_ERROR);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// setsockopt
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
int CXnSock::setsockopt(SOCKET s, int level, int optname, const char* optval, int optlen)
|
|
{
|
|
INT val;
|
|
WinsockApiPrologSockLock_(setsockopt, SOCKET_ERROR);
|
|
WinsockApiParamCheck_(optval != NULL && optlen > 0);
|
|
|
|
if (optlen < (INT) sizeof(INT))
|
|
val = (UCHAR) *optval;
|
|
else
|
|
val = *((INT*) optval);
|
|
|
|
switch (level)
|
|
{
|
|
case SOL_SOCKET:
|
|
{
|
|
RaiseToDpc();
|
|
|
|
// NOTE: Some options may not be applicable to datagram sockets,
|
|
// while others may not be applicable to stream sockets. But
|
|
// we'll let apps set those options here anyway (to avoid extra
|
|
// checking code). Downstream code just won't use those
|
|
|
|
switch (optname)
|
|
{
|
|
case SO_BROADCAST:
|
|
if (pSocket->IsTcp()) goto noopt;
|
|
pSocket->SetFlags(val ? SOCKF_OPT_BROADCAST : 0, SOCKF_OPT_BROADCAST);
|
|
break;
|
|
|
|
case SO_DONTLINGER:
|
|
if (pSocket->IsUdp()) goto noopt;
|
|
((CTcpSocket *)pSocket)->SetLingerOnOff(!val);
|
|
break;
|
|
|
|
case SO_LINGER:
|
|
if (pSocket->IsUdp()) goto noopt;
|
|
WinsockApiParamCheck_(optlen >= sizeof(LINGER));
|
|
((CTcpSocket *)pSocket)->SetLinger((LINGER *)optval);
|
|
break;
|
|
|
|
case SO_REUSEADDR:
|
|
if (pSocket->TestFlags(SOCKF_OPT_REUSEADDR) && val != 0) goto inval;
|
|
pSocket->SetFlags(val? SOCKF_OPT_REUSEADDR : 0, SOCKF_OPT_REUSEADDR);
|
|
break;
|
|
|
|
case SO_EXCLUSIVEADDRUSE:
|
|
if (pSocket->TestFlags(SOCKF_OPT_EXCLUSIVEADDR) && val != 0) goto inval;
|
|
pSocket->SetFlags(val ? SOCKF_OPT_EXCLUSIVEADDR : 0, SOCKF_OPT_EXCLUSIVEADDR);
|
|
break;
|
|
|
|
case SO_RCVTIMEO:
|
|
// Timeout value is in milliseconds
|
|
WinsockApiParamCheck_(optlen >= sizeof(INT));
|
|
pSocket->_uiRecvTimeout = val;
|
|
break;
|
|
|
|
case SO_SNDTIMEO:
|
|
// Timeout value is in milliseconds
|
|
WinsockApiParamCheck_(optlen >= sizeof(INT));
|
|
pSocket->_uiSendTimeout = val;
|
|
break;
|
|
|
|
case SO_RCVBUF:
|
|
WinsockApiParamCheck_(optlen >= sizeof(INT));
|
|
err = SockUpdateBufferSize(pSocket, pSocket->_cbMaxSendBuf, val);
|
|
if (!NT_SUCCESS(err)) goto inval;
|
|
break;
|
|
|
|
case SO_SNDBUF:
|
|
WinsockApiParamCheck_(optlen >= sizeof(INT));
|
|
err = SockUpdateBufferSize(pSocket, val, pSocket->_cbMaxRecvBuf);
|
|
if (!NT_SUCCESS(err)) goto inval;
|
|
break;
|
|
|
|
#ifdef XNET_FEATURE_ONLINE
|
|
|
|
case SO_INSECURE:
|
|
if (optlen == sizeof(CXoBase *) && optval == (char *)IpGetXoBase())
|
|
pSocket->SetFlags(SOCKF_INSECURE);
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
goto noopt;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IPPROTO_TCP:
|
|
{
|
|
if (pSocket->IsUdp()) goto noopt;
|
|
|
|
switch (optname) {
|
|
case TCP_NODELAY:
|
|
pSocket->SetFlags(val ? SOCKF_OPT_NONAGLE : 0, SOCKF_OPT_NONAGLE);
|
|
break;
|
|
|
|
default:
|
|
goto noopt;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
goto inval;
|
|
}
|
|
|
|
WinsockApiExitSockUnlock_(NO_ERROR, SOCKET_ERROR);
|
|
|
|
inval:
|
|
err = WSAEINVAL;
|
|
goto exit;
|
|
|
|
noopt:
|
|
err = WSAENOPROTOOPT;
|
|
goto exit;
|
|
}
|
|
|
|
NTSTATUS CXnSock::SockUpdateBufferSize(CSocket* pSocket, INT sendBufsize, INT recvBufsize)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Update the send and receive buffer sizes
|
|
|
|
Arguments:
|
|
|
|
pSocket - Points to the CSocket structure
|
|
sendBufsize, recvBufsize -
|
|
Specifies the new send and receive buffer sizes
|
|
|
|
Return Value:
|
|
|
|
Status code
|
|
|
|
--*/
|
|
|
|
{
|
|
ICHECK(SOCK, UDPC);
|
|
|
|
NTSTATUS status;
|
|
|
|
if (sendBufsize > (INT) cfgSockMaxSendBufsizeInK * 1024)
|
|
sendBufsize = (INT) cfgSockMaxSendBufsizeInK * 1024;
|
|
else if (sendBufsize <= 0) {
|
|
// NOTE: we never set actual send buffer size to 0
|
|
// because we don't support the no-buffering option.
|
|
sendBufsize = 1;
|
|
}
|
|
|
|
if (recvBufsize > (INT) cfgSockMaxRecvBufsizeInK * 1024)
|
|
recvBufsize = (INT) cfgSockMaxRecvBufsizeInK * 1024;
|
|
else if (recvBufsize <= 0)
|
|
recvBufsize = 0;
|
|
|
|
status = NETERR_OK;
|
|
|
|
if (pSocket->IsUdp())
|
|
{
|
|
// For datagram sockets, we'll just update the send and
|
|
// receive buffers sizes. If the current buffers are bigger
|
|
// than the specified limits, we'll leave the current data alone.
|
|
pSocket->_cbMaxSendBuf = sendBufsize;
|
|
pSocket->_cbMaxRecvBuf = recvBufsize;
|
|
}
|
|
else
|
|
{
|
|
CTcpSocket* pTcpSocket = (CTcpSocket*) pSocket;
|
|
|
|
// Set the send buffer size. If the current send buffer size
|
|
// is larger than the specified limit, leave the current data untouched.
|
|
pTcpSocket->_cbMaxSendBuf = sendBufsize;
|
|
|
|
// If the TCP socket is already connected,
|
|
// don't allow the app to reduce the receive buffer size.
|
|
if (!pTcpSocket->IsIdleState() && recvBufsize < (INT) pTcpSocket->_cbMaxRecvBuf)
|
|
{
|
|
status = NETERR_PARAM;
|
|
}
|
|
else
|
|
{
|
|
// NOTE: we don't update receive window to the connection peer
|
|
// right away. The new window information will be sent
|
|
// in the next outgoing ACK segment.
|
|
pTcpSocket->_cbMaxRecvBuf = recvBufsize;
|
|
}
|
|
}
|
|
|
|
// NOTE: If the send buffer has just opened up,
|
|
// we don't check to see if there is any pending
|
|
// overlapped send request that can be started.
|
|
// Rather the overlapped send request will be started
|
|
// by the normal process.
|
|
|
|
return(status);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// getsockopt
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
int CXnSock::getsockopt(SOCKET s, int level, int optname, char * optval, int * optlen)
|
|
{
|
|
INT val;
|
|
WinsockApiPrologSockLock_(getsockopt, SOCKET_ERROR);
|
|
|
|
WinsockApiParamCheck_(
|
|
optval != NULL &&
|
|
optlen != NULL &&
|
|
*optlen > 0);
|
|
|
|
memset(optval, 0, *optlen);
|
|
|
|
switch (level) {
|
|
case SOL_SOCKET:
|
|
|
|
switch (optname) {
|
|
case SO_BROADCAST:
|
|
if (pSocket->IsTcp()) goto noopt;
|
|
val = pSocket->TestFlags(SOCKF_OPT_BROADCAST);
|
|
break;
|
|
|
|
case SO_DONTLINGER:
|
|
if (pSocket->IsUdp()) goto noopt;
|
|
val = !((CTcpSocket *)pSocket)->GetLingerOnOff();
|
|
break;
|
|
|
|
case SO_LINGER:
|
|
if (pSocket->IsUdp()) goto noopt;
|
|
WinsockApiParamCheck_(*optlen >= sizeof(LINGER));
|
|
((CTcpSocket *)pSocket)->GetLinger((LINGER *)optval);
|
|
*optlen = sizeof(LINGER);
|
|
goto exit;
|
|
|
|
case SO_REUSEADDR:
|
|
val = pSocket->TestFlags(SOCKF_OPT_REUSEADDR);
|
|
break;
|
|
|
|
case SO_EXCLUSIVEADDRUSE:
|
|
val = pSocket->TestFlags(SOCKF_OPT_EXCLUSIVEADDR);
|
|
break;
|
|
|
|
case SO_RCVTIMEO:
|
|
WinsockApiParamCheck_(*optlen >= sizeof(INT));
|
|
val = pSocket->_uiRecvTimeout;
|
|
break;
|
|
|
|
case SO_SNDTIMEO:
|
|
WinsockApiParamCheck_(*optlen >= sizeof(INT));
|
|
val = pSocket->_uiSendTimeout;
|
|
break;
|
|
|
|
case SO_RCVBUF:
|
|
WinsockApiParamCheck_(*optlen >= sizeof(INT));
|
|
val = pSocket->_cbMaxRecvBuf;
|
|
break;
|
|
|
|
case SO_SNDBUF:
|
|
WinsockApiParamCheck_(*optlen >= sizeof(INT));
|
|
val = pSocket->_cbMaxSendBuf;
|
|
break;
|
|
|
|
case SO_TYPE:
|
|
val = pSocket->IsUdp() ? SOCK_DGRAM : SOCK_STREAM;
|
|
break;
|
|
|
|
case SO_ACCEPTCONN:
|
|
if (pSocket->IsUdp()) goto noopt;
|
|
val = ((CTcpSocket *)pSocket)->IsListenState();
|
|
break;
|
|
|
|
default:
|
|
goto noopt;
|
|
}
|
|
break;
|
|
|
|
case IPPROTO_TCP:
|
|
if (pSocket->IsUdp())
|
|
goto noopt;
|
|
|
|
switch (optname) {
|
|
case TCP_NODELAY:
|
|
val = pSocket->TestFlags(SOCKF_OPT_NONAGLE);
|
|
break;
|
|
|
|
default:
|
|
goto noopt;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
err = WSAEINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
if (*optlen < sizeof(INT)) {
|
|
*optval = (CHAR) val;
|
|
*optlen = 1;
|
|
} else {
|
|
*((INT*) optval) = val;
|
|
*optlen = sizeof(INT);
|
|
}
|
|
|
|
WinsockApiExitSockUnlock_(NO_ERROR, SOCKET_ERROR);
|
|
|
|
noopt:
|
|
err = WSAENOPROTOOPT;
|
|
goto exit;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// getsockname
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
int CXnSock::getsockname(SOCKET s, struct sockaddr* name, int* namelen)
|
|
{
|
|
struct sockaddr_in* sin;
|
|
WinsockApiPrologSockLock_(getsockname, SOCKET_ERROR);
|
|
|
|
WinsockApiParamCheck_(
|
|
name != NULL &&
|
|
namelen != NULL &&
|
|
*namelen >= SOCKADDRLEN);
|
|
|
|
if (!pSocket->TestFlags(SOCKF_BOUND))
|
|
{
|
|
WinsockApiGotoExit_(WSAEINVAL);
|
|
}
|
|
|
|
sin = (struct sockaddr_in*) name;
|
|
memset(sin, 0, SOCKADDRLEN);
|
|
sin->sin_family = AF_INET;
|
|
sin->sin_port = pSocket->_ipportSrc;
|
|
sin->sin_addr.s_addr = 0;
|
|
|
|
*namelen = SOCKADDRLEN;
|
|
err = NO_ERROR;
|
|
|
|
WinsockApiExitSockUnlock_(NO_ERROR, SOCKET_ERROR);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// getpeername
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
int CXnSock::getpeername(SOCKET s, struct sockaddr * name, int * namelen)
|
|
{
|
|
struct sockaddr_in* sin;
|
|
WinsockApiPrologSockLock_(getpeername, SOCKET_ERROR);
|
|
|
|
WinsockApiParamCheck_(
|
|
name != NULL &&
|
|
namelen != NULL &&
|
|
*namelen >= SOCKADDRLEN);
|
|
|
|
if (!pSocket->TestFlags(SOCKF_CONNECTED)) {
|
|
WinsockApiGotoExit_(WSAENOTCONN);
|
|
}
|
|
|
|
sin = (struct sockaddr_in*) name;
|
|
sin->sin_family = AF_INET;
|
|
sin->sin_port = pSocket->_ipportDst;
|
|
sin->sin_addr.s_addr = pSocket->_ipaDst;
|
|
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
|
|
|
|
*namelen = SOCKADDRLEN;
|
|
err = NO_ERROR;
|
|
|
|
SecRegSetOwned(sin->sin_addr.s_addr);
|
|
|
|
WinsockApiExitSockUnlock_(NO_ERROR, SOCKET_ERROR);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// bind
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
int CXnSock::bind(SOCKET s, const struct sockaddr * name, int namelen)
|
|
{
|
|
const struct sockaddr_in* sin = (const struct sockaddr_in*) name;
|
|
WinsockApiPrologSockLock_(bind, SOCKET_ERROR);
|
|
|
|
WinsockApiParamCheck_(
|
|
name != NULL &&
|
|
namelen >= SOCKADDRLEN &&
|
|
sin->sin_family == AF_INET &&
|
|
sin->sin_addr.s_addr == 0);
|
|
|
|
if (pSocket->TestFlags(SOCKF_BOUND))
|
|
{
|
|
WinsockApiGotoExit_(WSAEINVAL);
|
|
}
|
|
|
|
if (sin->sin_addr.s_addr != 0)
|
|
{
|
|
WinsockApiGotoExit_(WSAEADDRNOTAVAIL);
|
|
}
|
|
|
|
err = SockBind(pSocket, sin->sin_port);
|
|
|
|
MapNtStatusToWinsockError_(err);
|
|
|
|
WinsockApiExitSockUnlock_(NO_ERROR, SOCKET_ERROR);
|
|
}
|
|
|
|
NTSTATUS CXnSock::SockBind(CSocket * pSocket, CIpPort ipportBind)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Bind a socket to the specified local address
|
|
|
|
Arguments:
|
|
|
|
pSocket - Points to the protocol control block
|
|
srcaddr, srcport - Specifies the local socket address
|
|
|
|
Return Value:
|
|
|
|
Status code
|
|
|
|
--*/
|
|
|
|
{
|
|
int cRetry = ipportBind ? 0 : min(cfgSockMaxSockets, TEMP_PORT_COUNT);
|
|
CSocket * pSocketCur;
|
|
|
|
RaiseToDpc();
|
|
|
|
Assert(!pSocket->TestFlags(SOCKF_BOUND));
|
|
|
|
while (1)
|
|
{
|
|
if (cRetry-- > 0)
|
|
{
|
|
while (1)
|
|
{
|
|
_ipportTempNext += 1;
|
|
|
|
if (_ipportTempNext > MAX_TEMP_PORT)
|
|
_ipportTempNext = MIN_TEMP_PORT;
|
|
|
|
if (_ipportTempNext != NTOHS(ESPUDP_CLIENT_PORT))
|
|
break;
|
|
}
|
|
|
|
ipportBind = HTONS((WORD)_ipportTempNext);
|
|
}
|
|
|
|
for (pSocketCur = GetFirstSocket(); pSocketCur; pSocketCur = GetNextSocket(pSocketCur))
|
|
{
|
|
if (pSocketCur->TestFlags(SOCKF_BOUND))
|
|
{
|
|
Assert(pSocketCur != pSocket);
|
|
|
|
if ( (pSocketCur->IsUdp() == pSocket->IsUdp())
|
|
&& (pSocketCur->_ipportSrc == ipportBind)
|
|
&& ( pSocketCur->TestFlags(SOCKF_OPT_EXCLUSIVEADDR)
|
|
|| pSocket->TestFlags(SOCKF_OPT_EXCLUSIVEADDR)
|
|
|| !pSocket->TestFlags(SOCKF_OPT_REUSEADDR)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pSocketCur == NULL)
|
|
{
|
|
pSocket->_ipportSrc = ipportBind;
|
|
pSocket->SetFlags(SOCKF_BOUND);
|
|
return(NETERR_OK);
|
|
}
|
|
|
|
if (cRetry <= 0)
|
|
{
|
|
return(NETERR_ADDRINUSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// connect
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
int CXnSock::connect(SOCKET s, const struct sockaddr * name, int namelen)
|
|
{
|
|
const struct sockaddr_in* sin = (const struct sockaddr_in*) name;
|
|
CIpAddr dstaddr;
|
|
CIpPort dstport;
|
|
WinsockApiPrologSockLock_(connect, SOCKET_ERROR);
|
|
|
|
WinsockApiParamCheck_(
|
|
name != NULL &&
|
|
namelen >= SOCKADDRLEN &&
|
|
sin->sin_family == AF_INET);
|
|
|
|
if (pSocket->TestFlags(SOCKF_CONNECTED) && pSocket->IsTcp()) {
|
|
WinsockApiGotoExit_(WSAEISCONN);
|
|
}
|
|
|
|
dstaddr = sin->sin_addr.s_addr;
|
|
dstport = sin->sin_port;
|
|
if (pSocket->IsUdp())
|
|
{
|
|
err = UdpConnect(pSocket, dstaddr, dstport);
|
|
}
|
|
else if (pSocket->TestFlags(SOCKF_OPT_NONBLOCKING))
|
|
{
|
|
err = TcpConnect((CTcpSocket*) pSocket, dstaddr, dstport, FALSE);
|
|
|
|
// For nonblocking socket, we'll return WSAEWOULDBLOCK
|
|
// error code but the operation proceeds.
|
|
if (NT_SUCCESS(err)) err = NETERR_WOULDBLOCK;
|
|
}
|
|
else
|
|
{
|
|
err = TcpConnect((CTcpSocket*) pSocket, dstaddr, dstport, FALSE);
|
|
if (NT_SUCCESS(err))
|
|
{
|
|
// For blocking socket, we need to wait here for
|
|
// the operation to complete.
|
|
err = SockWaitForEvent(pSocket, SOCKF_EVENT_CONNECT, 0);
|
|
pSocket->SetFlags(SOCKF_CONNECT_SELECTED);
|
|
}
|
|
}
|
|
|
|
MapNtStatusToWinsockError_(err);
|
|
|
|
WinsockApiExitSockUnlock_(NO_ERROR, SOCKET_ERROR);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// listen
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
int CXnSock::listen(SOCKET s, int backlog)
|
|
{
|
|
WinsockApiPrologSockLock_(listen, SOCKET_ERROR);
|
|
|
|
WinsockApiParamCheck_(pSocket->IsTcp());
|
|
|
|
if (pSocket->TestFlags(SOCKF_CONNECTED))
|
|
{
|
|
WinsockApiGotoExit_(WSAEISCONN);
|
|
}
|
|
|
|
if (!pSocket->TestFlags(SOCKF_BOUND))
|
|
{
|
|
WinsockApiGotoExit_(WSAEINVAL);
|
|
}
|
|
|
|
err = TcpListen((CTcpSocket*) pSocket, backlog);
|
|
MapNtStatusToWinsockError_(err);
|
|
|
|
WinsockApiExitSockUnlock_(NO_ERROR, SOCKET_ERROR);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// accept
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
SOCKET CXnSock::accept(SOCKET s, struct sockaddr * addr, int * addrlen)
|
|
{
|
|
CTcpSocket * pTcpSocketChild = NULL;
|
|
|
|
WinsockApiPrologSockLock_(accept, INVALID_SOCKET);
|
|
|
|
WinsockApiParamCheck_(
|
|
pSocket->IsTcp() &&
|
|
(addr == NULL ||
|
|
addrlen != NULL && *addrlen >= SOCKADDRLEN));
|
|
|
|
CTcpSocket * pTcpSocket = (CTcpSocket *)pSocket;
|
|
|
|
if (!pTcpSocket->IsTcp() || !pTcpSocket->IsListenState())
|
|
{
|
|
WinsockApiGotoExit_(WSAEINVAL);
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
{
|
|
RaiseToDpc();
|
|
pTcpSocketChild = pTcpSocket->DequeueConnectedChild();
|
|
}
|
|
|
|
if (pTcpSocketChild)
|
|
break;
|
|
|
|
// For nonblocking socket, return an error code
|
|
// if we don't have any pending connection requests.
|
|
|
|
if (pTcpSocket->TestFlags(SOCKF_OPT_NONBLOCKING))
|
|
{
|
|
WinsockApiGotoExit_(WSAEWOULDBLOCK);
|
|
}
|
|
|
|
// For blocking sockets, wait until there
|
|
// is a connection request.
|
|
|
|
err = SockWaitForEvent(pSocket, SOCKF_EVENT_ACCEPT, 0);
|
|
|
|
if (err != 0)
|
|
break;
|
|
}
|
|
|
|
MapNtStatusToWinsockError_(err);
|
|
|
|
if (pTcpSocketChild)
|
|
{
|
|
pTcpSocketChild->SetFlags(SOCKF_OWNED);
|
|
|
|
if (addr && addrlen)
|
|
{
|
|
struct sockaddr_in* sin;
|
|
|
|
sin = (struct sockaddr_in*) addr;
|
|
sin->sin_family = AF_INET;
|
|
sin->sin_port = pTcpSocketChild->_ipportDst;
|
|
sin->sin_addr.s_addr = pTcpSocketChild->_ipaDst;
|
|
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
|
|
*addrlen = SOCKADDRLEN;
|
|
|
|
SecRegSetOwned(pTcpSocketChild->_ipaDst);
|
|
}
|
|
}
|
|
|
|
WinsockApiExitSockUnlock_((SOCKET) pTcpSocketChild, INVALID_SOCKET);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// select
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
//
|
|
// Count the total number of socket handles
|
|
//
|
|
#define SOCKETS_IN_SET(_set) ((_set) ? ((_set)->fd_count & 0xffff) : 0)
|
|
|
|
//
|
|
// Select event masks
|
|
//
|
|
#define SELECT_READ_EVENTS (SOCKF_EVENT_READ|SOCKF_EVENT_ACCEPT|SOCKF_EVENT_CLOSE|SOCKF_EVENT_RESET)
|
|
#define SELECT_WRITE_EVENTS (SOCKF_EVENT_WRITE|SOCKF_EVENT_CONNECT)
|
|
#define SELECT_EXCEPT_EVENTS (SOCKF_EVENT_RESET)
|
|
|
|
//
|
|
// Number of KWAIT_BLOCKs allocated on the stack for select() calls.
|
|
//
|
|
#define SELECT_STACK_KWAIT_BLOCKS 3
|
|
|
|
int CXnSock::select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, const struct timeval* timeout)
|
|
{
|
|
SELECTINFO tempinfo;
|
|
PRKEVENT tempevent;
|
|
SELECTINFO* selinfo;
|
|
PRKEVENT* events;
|
|
INT index, rdcnt, rwcnt, selcnt = 0;
|
|
CSocket* pSocket;
|
|
LARGE_INTEGER waittime;
|
|
LARGE_INTEGER* pwait;
|
|
#ifdef XNET_FEATURE_XBOX
|
|
KWAIT_BLOCK tempWaitBlocks[SELECT_STACK_KWAIT_BLOCKS];
|
|
PKWAIT_BLOCK waitBlockArray = tempWaitBlocks;
|
|
#endif
|
|
|
|
WinsockApiProlog_(select, SOCKET_ERROR);
|
|
|
|
// Count the total number of sockets
|
|
// (ignore the input nfds parameter)
|
|
rdcnt = SOCKETS_IN_SET(readfds);
|
|
rwcnt = rdcnt + SOCKETS_IN_SET(writefds);
|
|
nfds = rwcnt + SOCKETS_IN_SET(exceptfds);
|
|
|
|
if (nfds == 0) {
|
|
WinsockApiReturnError_(WSAEINVAL, SOCKET_ERROR);
|
|
}
|
|
|
|
if (nfds == 1) {
|
|
// Use temporary stack buffers for the special case
|
|
// where there is only one socket. This saves us from
|
|
// two extra memory allocations.
|
|
events = &tempevent;
|
|
selinfo = &tempinfo;
|
|
memset(selinfo, 0, sizeof(SELECTINFO));
|
|
} else {
|
|
selinfo = (SELECTINFO*) SysAllocZ(nfds*sizeof(SELECTINFO), PTAG_select);
|
|
events = (PRKEVENT*) SysAlloc(nfds*sizeof(PRKEVENT), PTAG_select);
|
|
if (!selinfo || !events) {
|
|
nfds = 0;
|
|
WinsockApiGotoExit_(WSAENOBUFS);
|
|
}
|
|
}
|
|
|
|
// Lock all the socket handles
|
|
if ((err = SockLockSelectSockets(readfds, selinfo, 0, SELECT_READ_EVENTS)) != 0 ||
|
|
(err = SockLockSelectSockets(writefds, selinfo, rdcnt, SELECT_WRITE_EVENTS)) != 0 ||
|
|
(err = SockLockSelectSockets(exceptfds, selinfo, rwcnt, SELECT_EXCEPT_EVENTS)) != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
// Compute the wait time in 100ns unit
|
|
if (timeout) {
|
|
pwait = &waittime;
|
|
#ifdef XNET_FEATURE_XBOX
|
|
waittime.QuadPart = Int32x32To64(timeout->tv_sec, -10000000) +
|
|
Int32x32To64(timeout->tv_usec, -10);
|
|
#else
|
|
waittime.QuadPart = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
|
|
#endif
|
|
} else {
|
|
pwait = NULL;
|
|
}
|
|
|
|
// Check if we to wait:
|
|
// if we do, set up the socket event flags
|
|
if (!pwait || pwait->QuadPart) {
|
|
INT waitCount = 0;
|
|
for (index=0; index < nfds; index++) {
|
|
pSocket = selinfo[index].pSocket;
|
|
if (selinfo[index].pSocketMasks) {
|
|
if (SockCheckSelectEvents(pSocket, selinfo[index].pSocketMasks, -1)) break;
|
|
events[waitCount++] = pSocket->GetEvent();
|
|
}
|
|
}
|
|
|
|
if (index == nfds) {
|
|
|
|
#ifdef XNET_FEATURE_XBOX
|
|
if (waitCount > SELECT_STACK_KWAIT_BLOCKS) {
|
|
waitBlockArray = (PKWAIT_BLOCK) SysAllocZ(waitCount * sizeof(KWAIT_BLOCK), PTAG_select);
|
|
if (!waitBlockArray) {
|
|
WinsockApiGotoExit_(WSAENOBUFS);
|
|
}
|
|
}
|
|
|
|
#ifdef XNET_FEATURE_XBDM_SERVER
|
|
|
|
do {
|
|
err = KeWaitForMultipleObjects(
|
|
waitCount,
|
|
(void **)events,
|
|
WaitAny,
|
|
UserRequest,
|
|
KernelMode,
|
|
TRUE,
|
|
pwait,
|
|
waitBlockArray);
|
|
} while(err == STATUS_KERNEL_APC);
|
|
|
|
if(err == STATUS_ALERTED)
|
|
err = STATUS_TIMEOUT;
|
|
|
|
#else
|
|
|
|
err = KeWaitForMultipleObjects(
|
|
waitCount,
|
|
(void **)events,
|
|
WaitAny,
|
|
UserRequest,
|
|
UserMode,
|
|
FALSE,
|
|
pwait,
|
|
waitBlockArray);
|
|
|
|
#endif // XBDM
|
|
|
|
if ((err < 0 || err >= waitCount) && err != STATUS_TIMEOUT) {
|
|
WinsockApiGotoExit_(WSAEFAULT);
|
|
}
|
|
#else
|
|
err = WaitForMultipleObjects(waitCount, (HANDLE *)events, FALSE, (DWORD)waittime.QuadPart);
|
|
if (err == WAIT_FAILED)
|
|
WinsockApiGotoExit_(WSAEFAULT);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Determine which socket events are ready
|
|
// and return appropriate information
|
|
|
|
if (readfds) { FD_ZERO(readfds); }
|
|
if (writefds) { FD_ZERO(writefds); }
|
|
if (exceptfds) { FD_ZERO(exceptfds); }
|
|
|
|
for (index=selcnt=0; index < nfds; index++)
|
|
{
|
|
if (SockCheckSelectEvents(selinfo[index].pSocket, selinfo[index].eventMasks, 0))
|
|
{
|
|
FD_SET(selinfo[index].s, selinfo[index].fdset);
|
|
selcnt++;
|
|
}
|
|
}
|
|
|
|
err = NO_ERROR;
|
|
|
|
exit:
|
|
|
|
for (index=0; index < nfds; index++)
|
|
{
|
|
pSocket = selinfo[index].pSocket;
|
|
|
|
if (pSocket && selinfo[index].pSocketMasks)
|
|
{
|
|
pSocket->ClearFlags(SOCKF_EVENT_MASK);
|
|
pSocket->Unlock();
|
|
}
|
|
}
|
|
|
|
#ifdef XNET_FEATURE_XBOX
|
|
if (waitBlockArray != tempWaitBlocks) { SysFree(waitBlockArray); }
|
|
#endif
|
|
if (selinfo != &tempinfo) { SysFree(selinfo); }
|
|
if (events != &tempevent) { SysFree(events); }
|
|
|
|
WinsockApiCheckError_(SOCKET_ERROR);
|
|
return selcnt;
|
|
}
|
|
|
|
INT CXnSock::SockLockSelectSockets(fd_set* fdset, SELECTINFO* selinfo, INT offset, INT eventMasks)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Lock the socket handles that was passed to the select API
|
|
|
|
Arguments:
|
|
|
|
fdset - Points to the socket set
|
|
selinfo - Points to an array of SELECTINFO structures
|
|
for storing the locked socket information
|
|
eventMasks - Specifies the interested events
|
|
|
|
Return Value:
|
|
|
|
Winsock error code
|
|
|
|
--*/
|
|
|
|
{
|
|
INT i, count;
|
|
|
|
// Nothing to do if the set is empty
|
|
count = SOCKETS_IN_SET(fdset);
|
|
|
|
for (i=0; i < count; i++) {
|
|
SOCKET s = fdset->fd_array[i];
|
|
INT j, k = offset + i;
|
|
|
|
selinfo[k].s = s;
|
|
selinfo[k].fdset = fdset;
|
|
selinfo[k].eventMasks = eventMasks;
|
|
|
|
// Check to see if the socket is already used
|
|
// in the same select call
|
|
for (j=0; j < k && selinfo[j].s != s; j++)
|
|
;
|
|
|
|
if (j == k) {
|
|
//
|
|
// The socket isn't seen already
|
|
//
|
|
selinfo[k].pSocketMasks = eventMasks;
|
|
selinfo[k].pSocket = CSocket::Lock(s);
|
|
if (!selinfo[k].pSocket)
|
|
return GetLastError();
|
|
} else {
|
|
//
|
|
// The socket is already seen
|
|
//
|
|
selinfo[j].pSocketMasks |= eventMasks;
|
|
selinfo[k].pSocketMasks = 0;
|
|
selinfo[k].pSocket = selinfo[j].pSocket;
|
|
}
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
INT CXnSock::SockCheckSelectEvents(CSocket * pSocket, INT eventMasks, INT setwait)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if the specified socket events are available
|
|
and optionally set up the socket to wait for them
|
|
|
|
Arguments:
|
|
|
|
pSocket - Points to the protocol control block
|
|
eventMasks - Specifies the socket events the caller is interested in
|
|
setwait - Whether to set up the sockets to wait if
|
|
none of the specified events are avaiable
|
|
|
|
Return Value:
|
|
|
|
Set of event flags that are already available
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
INT readyMasks;
|
|
|
|
RaiseToDpc();
|
|
|
|
// Check to see if the specified event is already available
|
|
// Since our checks are trivial, it's faster to check
|
|
// everything instead of trying to check selectively
|
|
// based on the flags specified by the caller.
|
|
|
|
if (pSocket->IsTcp())
|
|
{
|
|
CTcpSocket * pTcpSocket = (CTcpSocket *)pSocket;
|
|
|
|
// If the connection was reset, return reset status
|
|
status = pSocket->GetStatus();
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
// Note: If we already told the app that the socket was connected
|
|
// and then the socket got resetted, we don't need to set the socket
|
|
// in the exceptfds again to tell the app the connection has failed.
|
|
|
|
if (eventMasks != SOCKF_EVENT_RESET || !pSocket->TestFlags(SOCKF_CONNECT_SELECTED))
|
|
{
|
|
return(SOCKF_EVENT_RESET);
|
|
}
|
|
}
|
|
|
|
readyMasks = (pTcpSocket->IsTcpRecvBufEmpty() ? 0 : SOCKF_EVENT_READ) |
|
|
(pTcpSocket->HasConnectedChild() ? SOCKF_EVENT_ACCEPT : 0) |
|
|
(pTcpSocket->IsFinReceived() ? SOCKF_EVENT_READ|SOCKF_EVENT_CLOSE : 0);
|
|
|
|
if (pSocket->TestFlags(SOCKF_CONNECTED))
|
|
{
|
|
if (!pSocket->IsSendBufFull())
|
|
{
|
|
readyMasks |= SOCKF_EVENT_WRITE;
|
|
}
|
|
|
|
// NOTE: we only signal the connect event exactly once
|
|
if ((eventMasks & SOCKF_EVENT_CONNECT) && !pSocket->TestFlags(SOCKF_CONNECT_SELECTED))
|
|
{
|
|
readyMasks |= SOCKF_EVENT_CONNECT;
|
|
if (setwait >= 0)
|
|
{
|
|
pSocket->SetFlags(SOCKF_CONNECT_SELECTED);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
readyMasks = (pSocket->IsUdpRecvBufEmpty() ? 0 : SOCKF_EVENT_READ) |
|
|
(pSocket->IsSendBufFull() ? 0 : SOCKF_EVENT_WRITE);
|
|
}
|
|
|
|
if ((readyMasks &= eventMasks) == 0 && setwait)
|
|
{
|
|
// Indicate that we're interested in the specified event
|
|
// and prepare to wait
|
|
pSocket->SetFlags(eventMasks, SOCKF_EVENT_MASK);
|
|
EvtClear(pSocket->GetEvent());
|
|
}
|
|
|
|
return(readyMasks);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// WSAGetOverlappedResult
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
BOOL CXnSock::WSAGetOverlappedResult(SOCKET s, LPWSAOVERLAPPED overlapped, LPDWORD byteCount, BOOL fWait, LPDWORD flags)
|
|
{
|
|
WinsockApiPrologSockLock_(WSAGetOverlappedResult, FALSE);
|
|
|
|
WinsockApiParamCheck_(
|
|
overlapped != NULL &&
|
|
overlapped->hEvent != NULL &&
|
|
byteCount != NULL &&
|
|
flags != NULL);
|
|
|
|
//
|
|
// Check if we need to wait for the I/O request to complete
|
|
//
|
|
if (overlapped->_iostatus == NETERR_PENDING && fWait)
|
|
{
|
|
WaitForSingleObject(overlapped->hEvent, INFINITE);
|
|
|
|
RaiseToDpc();
|
|
|
|
if (overlapped->_iostatus == NETERR_PENDING)
|
|
{
|
|
err = overlapped->_ioxfercnt ? NETERR_OK : NETERR_CANCELLED;
|
|
SockReqComplete(pSocket, (CSockReq *)overlapped->_ioreq, err);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the I/O request was completed,
|
|
// return the completion status information
|
|
//
|
|
if ((err = overlapped->_iostatus) != NETERR_PENDING && NT_SUCCESS(err))
|
|
{
|
|
*byteCount = overlapped->_ioxfercnt;
|
|
*flags = overlapped->_ioflags;
|
|
}
|
|
|
|
if (err == NETERR_PENDING)
|
|
{
|
|
WinsockApiGotoExit_(WSA_IO_INCOMPLETE);
|
|
}
|
|
else
|
|
{
|
|
MapNtStatusToWinsockError_(err);
|
|
}
|
|
|
|
WinsockApiExitSockUnlock_(TRUE, FALSE);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// WSACancelOverlappedIO
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
INT CXnSock::WSACancelOverlappedIO(SOCKET s)
|
|
{
|
|
WinsockApiPrologSockLock_(WSACancelOverlappedIO, SOCKET_ERROR);
|
|
|
|
{
|
|
RaiseToDpc();
|
|
SockReqComplete(pSocket, pSocket->GetRecvReq(), NETERR_CANCELLED);
|
|
SockReqComplete(pSocket, pSocket->GetSendReq(), NETERR_CANCELLED);
|
|
}
|
|
|
|
WinsockApiGotoExit_(NO_ERROR);
|
|
WinsockApiExitSockUnlock_(NO_ERROR, SOCKET_ERROR);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// recv
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
int CXnSock::recv(SOCKET s, char* buf, int len, int flags)
|
|
{
|
|
CRecvReq pRecvReq;
|
|
DWORD count;
|
|
WinsockApiPrologSockLock_(recv, SOCKET_ERROR);
|
|
|
|
WinsockApiParamCheck_(
|
|
(len > 0 && buf != NULL || len == 0) &&
|
|
flags == 0);
|
|
|
|
pRecvReq.buf = (BYTE*) buf;
|
|
pRecvReq.buflen = len;
|
|
pRecvReq.flags = flags;
|
|
pRecvReq.bytesRecv = &count;
|
|
pRecvReq.fromaddr = NULL;
|
|
pRecvReq._pWsaOverlapped = NULL;
|
|
|
|
err = SockRead(pSocket, &pRecvReq);
|
|
|
|
MapNtStatusToWinsockError_(err);
|
|
WinsockApiExitSockUnlock_(count, SOCKET_ERROR);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// WSARecv
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
//
|
|
// Verify buffers passed to WSARecv API
|
|
// NOTE: we do not support more than 1 receive buffers.
|
|
//
|
|
INLINE
|
|
BOOL CheckRecvWsaBuf(WSABUF* bufs, UINT bufcnt)
|
|
{
|
|
return (bufcnt == 1 && bufs != NULL && (bufs->len > 0 && bufs->buf != NULL || bufs->len == 0));
|
|
}
|
|
|
|
int CXnSock::WSARecv(SOCKET s, LPWSABUF bufs, DWORD bufcnt, LPDWORD bytesRecv, LPDWORD flags,
|
|
LPWSAOVERLAPPED overlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE completionproc)
|
|
{
|
|
CRecvReq pRecvReq;
|
|
WinsockApiPrologSockLock_(WSARecv, SOCKET_ERROR);
|
|
|
|
WinsockApiParamCheck_(
|
|
CheckRecvWsaBuf(bufs, bufcnt) &&
|
|
bytesRecv != NULL &&
|
|
flags != NULL && *flags == 0 &&
|
|
completionproc == NULL);
|
|
|
|
pRecvReq.buf = (BYTE*) bufs->buf;
|
|
pRecvReq.buflen = bufs->len;
|
|
pRecvReq.flags = *flags;
|
|
pRecvReq.bytesRecv = bytesRecv;
|
|
pRecvReq.fromaddr = NULL;
|
|
pRecvReq._pWsaOverlapped = overlapped;
|
|
|
|
err = SockRead(pSocket, &pRecvReq);
|
|
|
|
*flags = pRecvReq.flags;
|
|
|
|
MapNtStatusToWinsockError_(err);
|
|
WinsockApiExitSockUnlock_(NO_ERROR, SOCKET_ERROR);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// recvfrom
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
int CXnSock::recvfrom(SOCKET s, char FAR * buf, int len, int flags, struct sockaddr * from, int * fromlen)
|
|
{
|
|
DWORD count;
|
|
INT err;
|
|
WSABUF wsabuf;
|
|
|
|
WinsockApiPrologLight_(recvfrom);
|
|
WinsockApiParamCheck_(
|
|
(len > 0 && buf != NULL || len == 0) &&
|
|
(from == NULL ||
|
|
fromlen != NULL && *fromlen >= SOCKADDRLEN));
|
|
|
|
wsabuf.len = len;
|
|
wsabuf.buf = buf;
|
|
err = WSARecvFrom(s, &wsabuf, 1, &count, (DWORD*) &flags, from, fromlen, NULL, NULL);
|
|
return (err == NO_ERROR) ? count : SOCKET_ERROR;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// WSARecvFrom
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
int CXnSock::WSARecvFrom(SOCKET s, LPWSABUF bufs, DWORD bufcnt, LPDWORD bytesRecv, LPDWORD flags,
|
|
struct sockaddr * fromaddr, LPINT fromlen, LPWSAOVERLAPPED overlapped,
|
|
LPWSAOVERLAPPED_COMPLETION_ROUTINE completionproc)
|
|
{
|
|
CRecvReq pRecvReq;
|
|
WinsockApiPrologSockLock_(WSARecvFrom, SOCKET_ERROR);
|
|
|
|
WinsockApiParamCheck_(
|
|
CheckRecvWsaBuf(bufs, bufcnt) &&
|
|
bytesRecv != NULL &&
|
|
(fromaddr == NULL ||
|
|
fromlen != NULL && *fromlen >= SOCKADDRLEN) &&
|
|
flags != NULL && *flags == 0 &&
|
|
completionproc == NULL);
|
|
|
|
// Winsock documentation on this call is extremely confusing
|
|
// regarding the correct behavior for connection-oriented sockets
|
|
// Here I've taken the liberty to treat WSARecvFrom the same way
|
|
// as WSARecv for such cases.
|
|
if (pSocket->IsTcp()) {
|
|
TraceSz(Verbose, "WSARecvFrom called on stream socket!");
|
|
}
|
|
|
|
pRecvReq.buf = (BYTE*) bufs->buf;
|
|
pRecvReq.buflen = bufs->len;
|
|
pRecvReq.flags = *flags;
|
|
pRecvReq.bytesRecv = bytesRecv;
|
|
pRecvReq._pWsaOverlapped = overlapped;
|
|
pRecvReq.fromaddr = (struct sockaddr_in*) fromaddr;
|
|
|
|
if (fromaddr)
|
|
{
|
|
memset(fromaddr, 0, SOCKADDRLEN);
|
|
*fromlen = SOCKADDRLEN;
|
|
pRecvReq.fromaddr->sin_family = AF_INET;
|
|
}
|
|
|
|
err = SockRead(pSocket, &pRecvReq);
|
|
*flags = pRecvReq.flags;
|
|
MapNtStatusToWinsockError_(err);
|
|
WinsockApiExitSockUnlock_(NO_ERROR, SOCKET_ERROR);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// SockRead
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
NTSTATUS CXnSock::SockRead(CSocket * pSocket, CRecvReq * pRecvReq)
|
|
{
|
|
WSAOVERLAPPED * pWsaOverlapped;
|
|
WSAOVERLAPPED WsaOverlappedTemp;
|
|
NTSTATUS status;
|
|
|
|
if (pSocket->IsTcp())
|
|
{
|
|
if (!pSocket->TestFlags(SOCKF_CONNECTED))
|
|
{
|
|
return NETERR(WSAENOTCONN);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!pSocket->TestFlags(SOCKF_BOUND))
|
|
{
|
|
return NETERR(WSAEINVAL);
|
|
}
|
|
}
|
|
|
|
if (pSocket->TestFlags(SOCKF_NOMORE_RECV))
|
|
{
|
|
return(!NT_SUCCESS(pSocket->GetStatus()) ? pSocket->GetStatus() : NETERR(WSAESHUTDOWN));
|
|
}
|
|
|
|
// Since all our sockets are overlapped, we don't
|
|
// enforce the Win32 behavior that the input socket
|
|
// must be a non-overlapped socket.
|
|
|
|
pWsaOverlapped = pRecvReq->_pWsaOverlapped;
|
|
|
|
if (pWsaOverlapped != NULL)
|
|
{
|
|
pRecvReq->_pEvent = EvtFromHandle(pWsaOverlapped->hEvent);
|
|
|
|
if (pRecvReq->_pEvent == NULL)
|
|
{
|
|
return(NETERR(WSASYSCALLFAILURE));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pRecvReq->_pEvent = NULL;
|
|
}
|
|
|
|
// Check if we have any buffered data waiting to be read
|
|
|
|
if (pSocket->IsRecvBufEmpty())
|
|
{
|
|
if (pWsaOverlapped)
|
|
{
|
|
// Overlapped call. This used to clear the event here, but that breaks if the caller
|
|
// is using the same event for multiple sockets and/or online tasks. So now we
|
|
// let the caller reset the event themselves before making the overlapped call.
|
|
// Normally they would use an auto-reset event anyways, so there is nothing for them to do.
|
|
}
|
|
else if (pSocket->TestFlags(SOCKF_OPT_NONBLOCKING))
|
|
{
|
|
// Nonoverlapped call and socket is nonblocking:
|
|
// just return WOULDBLOCK error code.
|
|
//
|
|
// Note: For TCP socket, if FIN has been received
|
|
// we should return success with bytesRecv set to 0.
|
|
if (pSocket->IsUdp() || !((CTcpSocket *)pSocket)->IsFinReceived())
|
|
{
|
|
return(NETERR(WSAEWOULDBLOCK));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Blocking call - prepare to wait
|
|
pRecvReq->_pWsaOverlapped = &WsaOverlappedTemp;
|
|
pRecvReq->_pEvent = pSocket->GetEvent();
|
|
EvtClear(pRecvReq->_pEvent);
|
|
}
|
|
}
|
|
|
|
status = pSocket->IsUdp() ?
|
|
UdpRead(pSocket, pRecvReq) :
|
|
TcpRead((CTcpSocket *) pSocket, pRecvReq);
|
|
|
|
if (status == NETERR_PENDING)
|
|
{
|
|
if (pWsaOverlapped == NULL)
|
|
{
|
|
// A blocking call is still in progress
|
|
EvtWait(pRecvReq->_pEvent, pSocket->_uiRecvTimeout);
|
|
|
|
if (WsaOverlappedTemp._iostatus == NETERR_PENDING)
|
|
{
|
|
RaiseToDpc();
|
|
|
|
if (WsaOverlappedTemp._iostatus == NETERR_PENDING)
|
|
{
|
|
// We can use pRecvReq directly here because
|
|
// blocking recv call is treated as a special case
|
|
// in SockQueueOverlappedRecv
|
|
SockReqComplete(pSocket, pRecvReq, NETERR_TIMEOUT);
|
|
}
|
|
}
|
|
|
|
*pRecvReq->bytesRecv = WsaOverlappedTemp._ioxfercnt;
|
|
pRecvReq->flags = WsaOverlappedTemp._ioflags;
|
|
status = WsaOverlappedTemp._iostatus;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pWsaOverlapped)
|
|
{
|
|
// An overlapped call was completed immediately
|
|
pWsaOverlapped->_ioflags = pRecvReq->flags;
|
|
pWsaOverlapped->_ioxfercnt = *pRecvReq->bytesRecv;
|
|
pWsaOverlapped->_iostatus = status;
|
|
|
|
// It would seem to be a waste to signal the event here.
|
|
// But that's win2k behavior.
|
|
EvtSet(pRecvReq->_pEvent, 0);
|
|
EvtDereference(pRecvReq->_pEvent);
|
|
}
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
NTSTATUS CXnSock::RecvReqEnqueue(CSocket * pSocket, CRecvReq * pRecvReq)
|
|
{
|
|
ICHECK(SOCK, UDPC|SDPC);
|
|
|
|
CRecvReq * pRecvReqNew;
|
|
|
|
if (pSocket->HasRecvReq())
|
|
{
|
|
TraceSz2(sockWarn, "[%08lX:%s] Can only have one overlapped read at a time",
|
|
pSocket, pSocket->TypeStr());
|
|
return(NETERR_WOULDBLOCK);
|
|
}
|
|
else if (!pRecvReq->_pEvent)
|
|
{
|
|
TraceSz2(sockWarn, "[%08lX:%s] Overlapped read on nonblocking socket must supply an event",
|
|
pSocket, pSocket->TypeStr());
|
|
return(NETERR_WOULDBLOCK);
|
|
}
|
|
|
|
if (pRecvReq->_pEvent == pSocket->GetEvent())
|
|
{
|
|
// If the wait event is our internal per-CSocket event,
|
|
// then this is a special case for implementing the
|
|
// blocking recv call.
|
|
//
|
|
// In this case, we avoid an allocation by directly
|
|
// queuing up the RECVREQ structure that was passed
|
|
// in from the caller (winsock layer). This works because
|
|
// the caller will wait for the recv to complete after
|
|
// this function returns.
|
|
|
|
pRecvReqNew = pRecvReq;
|
|
}
|
|
else
|
|
{
|
|
pRecvReqNew = (CRecvReq *)SysAlloc(sizeof(CRecvReq), PTAG_CRecvReq);
|
|
|
|
if (pRecvReqNew == NULL)
|
|
{
|
|
TraceSz2(sockWarn, "[%08lX:%s] Out of memory allocating overlapped read control block",
|
|
pSocket, pSocket->TypeStr());
|
|
return(NETERR_MEMORY);
|
|
}
|
|
|
|
*pRecvReqNew = *pRecvReq;
|
|
}
|
|
|
|
pSocket->SetRecvReq(pRecvReqNew);
|
|
|
|
pRecvReqNew->_pWsaOverlapped->_ioxfercnt = 0;
|
|
pRecvReqNew->_pWsaOverlapped->_ioflags = 0;
|
|
pRecvReqNew->_pWsaOverlapped->_ioreq = (UINT_PTR)pRecvReqNew;
|
|
pRecvReqNew->_pWsaOverlapped->_iostatus = (DWORD)NETERR_PENDING;
|
|
|
|
TraceSz3(sock, "[%08lX:%s] Enqueued overlapped read of %ld bytes",
|
|
pSocket, pSocket->TypeStr(), pRecvReq->GetCbBuf());
|
|
|
|
return(NETERR_PENDING);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// Forward Declarations
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
UINT SockCountSendTotal(WSABUF * bufs, UINT bufcnt);
|
|
BOOL SockCheckSendWsaBuf(WSABUF * bufs, UINT bufcnt);
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// send
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
int CXnSock::send(SOCKET s, const char* buf, int len, int flags)
|
|
{
|
|
WSABUF wsabuf;
|
|
CSendReq SendReq;
|
|
WinsockApiPrologSockLock_(send, SOCKET_ERROR);
|
|
|
|
WinsockApiParamCheck_(
|
|
(len > 0 && buf != NULL || len == 0) &&
|
|
(flags == 0));
|
|
|
|
wsabuf.len = len;
|
|
wsabuf.buf = (char*) buf;
|
|
SendReq._pWsaOverlapped = NULL;
|
|
SendReq.bufs = &wsabuf;
|
|
SendReq.bufcnt = 1;
|
|
SendReq.sendtotal = len;
|
|
SendReq.toaddr = NULL;
|
|
|
|
err = SockSend(pSocket, &SendReq);
|
|
MapNtStatusToWinsockError_(err);
|
|
WinsockApiExitSockUnlock_(SendReq.sendtotal, SOCKET_ERROR);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// WSASend
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
int CXnSock::WSASend(SOCKET s, LPWSABUF bufs, DWORD bufcnt, LPDWORD bytesSent, DWORD flags,
|
|
LPWSAOVERLAPPED overlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE completionproc)
|
|
{
|
|
CSendReq SendReq;
|
|
WinsockApiPrologSockLock_(WSASend, SOCKET_ERROR);
|
|
|
|
WinsockApiParamCheck_(
|
|
SockCheckSendWsaBuf(bufs, bufcnt) &&
|
|
bytesSent != NULL &&
|
|
flags == 0 &&
|
|
completionproc == NULL);
|
|
|
|
SendReq._pWsaOverlapped = overlapped;
|
|
SendReq.bufs = bufs;
|
|
SendReq.bufcnt = (WORD)bufcnt;
|
|
SendReq.sendtotal = SockCountSendTotal(bufs, bufcnt);
|
|
SendReq.toaddr = NULL;
|
|
|
|
err = SockSend(pSocket, &SendReq);
|
|
if (NT_SUCCESS(err)) *bytesSent = SendReq.sendtotal;
|
|
MapNtStatusToWinsockError_(err);
|
|
WinsockApiExitSockUnlock_(NO_ERROR, SOCKET_ERROR);
|
|
}
|
|
|
|
BOOL SockCheckSendWsaBuf(WSABUF* bufs, UINT bufcnt)
|
|
{
|
|
if (bufs == NULL || bufcnt == 0 || bufcnt > 0xFFFF)
|
|
return FALSE;
|
|
|
|
while (bufcnt--) {
|
|
if (bufs->len && bufs->buf == NULL)
|
|
return FALSE;
|
|
bufs++;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
UINT SockCountSendTotal(WSABUF* bufs, UINT bufcnt)
|
|
{
|
|
UINT total = 0;
|
|
|
|
while (bufcnt--)
|
|
{
|
|
total += bufs->len;
|
|
bufs++;
|
|
}
|
|
|
|
return(total);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// sendto
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
int CXnSock::sendto(SOCKET s, const char* buf, int len, int flags, const struct sockaddr* to, int tolen)
|
|
{
|
|
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;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// WSASendTo
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
int CXnSock::WSASendTo(SOCKET s, LPWSABUF bufs, DWORD bufcnt, LPDWORD bytesSent, DWORD flags,
|
|
const struct sockaddr* toaddr, int tolen, LPWSAOVERLAPPED overlapped,
|
|
LPWSAOVERLAPPED_COMPLETION_ROUTINE completionproc)
|
|
{
|
|
struct sockaddr_in* sin = (struct sockaddr_in*) toaddr;
|
|
CSendReq 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._pWsaOverlapped = overlapped;
|
|
SendReq.bufs = bufs;
|
|
SendReq.bufcnt = (WORD)bufcnt;
|
|
SendReq.sendtotal = SockCountSendTotal(bufs, bufcnt);
|
|
|
|
if (pSocket->IsTcp() || 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;
|
|
}
|
|
else
|
|
{
|
|
CIpAddr ipaDst = sin->sin_addr.s_addr;
|
|
|
|
// 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 ( ipaDst == 0
|
|
|| sin->sin_port == 0
|
|
|| ipaDst.IsMulticast()
|
|
|| (ipaDst.IsLoopback() && ipaDst != IPADDR_LOOPBACK))
|
|
{
|
|
WinsockApiGotoExit_(WSAEADDRNOTAVAIL);
|
|
}
|
|
|
|
// Is this socket allowed to send broadcast
|
|
// datagrams on this socket?
|
|
if (ipaDst.IsBroadcast() && !pSocket->TestFlags(SOCKF_OPT_BROADCAST))
|
|
{
|
|
WinsockApiGotoExit_(WSAEACCES);
|
|
}
|
|
|
|
// If the socket is not bound, bind it here
|
|
if (!pSocket->TestFlags(SOCKF_BOUND))
|
|
{
|
|
err = SockBind(pSocket, 0);
|
|
|
|
if (!NT_SUCCESS(err))
|
|
{
|
|
MapNtStatusToWinsockError_(err);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
// Send to the new destination
|
|
SendReq.toaddr = sin;
|
|
}
|
|
|
|
err = SockSend(pSocket, &SendReq);
|
|
|
|
if (NT_SUCCESS(err))
|
|
{
|
|
*bytesSent = SendReq.sendtotal;
|
|
}
|
|
|
|
MapNtStatusToWinsockError_(err);
|
|
WinsockApiExitSockUnlock_(NO_ERROR, SOCKET_ERROR);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// SockSend
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
NTSTATUS CXnSock::SockSend(CSocket* pSocket, CSendReq* pSendReq)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Internal function for sending out data from a socket
|
|
|
|
Arguments:
|
|
|
|
pSocket - Points to the protocol control block
|
|
pSendReq - Points to send user request information
|
|
|
|
Return Value:
|
|
|
|
Status code
|
|
|
|
--*/
|
|
|
|
{
|
|
WSAOVERLAPPED * pWsaOverlapped;
|
|
NTSTATUS status;
|
|
|
|
if (!pSocket->TestFlags(SOCKF_CONNECTED) && !pSendReq->toaddr)
|
|
return NETERR(WSAENOTCONN);
|
|
|
|
if (pSocket->TestFlags(SOCKF_NOMORE_XMIT))
|
|
{
|
|
return(!NT_SUCCESS(pSocket->GetStatus()) ? pSocket->GetStatus() : NETERR(WSAESHUTDOWN));
|
|
}
|
|
|
|
pWsaOverlapped = pSendReq->_pWsaOverlapped;
|
|
|
|
if (pWsaOverlapped != NULL)
|
|
{
|
|
pSendReq->_pEvent = EvtFromHandle(pWsaOverlapped->hEvent);
|
|
|
|
if (pSendReq->_pEvent == NULL)
|
|
{
|
|
return(NETERR(WSASYSCALLFAILURE));
|
|
}
|
|
}
|
|
|
|
// Check if we have room in the send buffer
|
|
|
|
if (pSocket->IsSendBufFull())
|
|
{
|
|
if (pWsaOverlapped)
|
|
{
|
|
// Overlapped call. This used to clear the event here, but that breaks if the caller
|
|
// is using the same event for multiple sockets and/or online tasks. So now we
|
|
// let the caller reset the event themselves before making the overlapped call.
|
|
// Normally they would use an auto-reset event anyways, so there is nothing for them to do.
|
|
|
|
status = SockQueueOverlappedSend(pSocket, pSendReq);
|
|
|
|
// 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 (pSocket->TestFlags(SOCKF_OPT_NONBLOCKING) || pSocket->HasSendReq())
|
|
{
|
|
return(NETERR_WOULDBLOCK);
|
|
}
|
|
else
|
|
{
|
|
status = SockWaitForEvent(pSocket, SOCKF_EVENT_WRITE, pSocket->_uiSendTimeout);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
return(status);
|
|
}
|
|
}
|
|
|
|
status = pSocket->IsUdp() ?
|
|
UdpSend(pSocket, pSendReq, 0) :
|
|
TcpSend((CTcpSocket *) pSocket, pSendReq, 0);
|
|
|
|
exit:
|
|
|
|
if (pWsaOverlapped)
|
|
{
|
|
// An overlapped call was completed immediately
|
|
pWsaOverlapped->_ioflags = 0;
|
|
pWsaOverlapped->_ioxfercnt = pSendReq->sendtotal;
|
|
pWsaOverlapped->_iostatus = status;
|
|
|
|
// It would seem to be a waste to signal the event here.
|
|
// But that's win2k behavior.
|
|
|
|
EvtSet(pSendReq->_pEvent, 0);
|
|
EvtDereference(pSendReq->_pEvent);
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
NTSTATUS CXnSock::SockQueueOverlappedSend(CSocket * pSocket, CSendReq * pSendReq)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Queue up an overlapped send request
|
|
|
|
Arguments:
|
|
|
|
pSocket - Points to the protocol control block
|
|
pSendReq - Points to the overlapped send request
|
|
|
|
Return Value:
|
|
|
|
Status code:
|
|
NETERR_PENDING - the request was successfully queued up
|
|
NETERR_OK - if the send buffer has opened up and
|
|
there is no need to queue up the request
|
|
otherwise - the request was not queued up due to an error
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
|
|
RaiseToDpc();
|
|
|
|
if (pSocket->HasSendReq())
|
|
{
|
|
// We only support 1 outstanding overlapped send request.
|
|
status = NETERR_WOULDBLOCK;
|
|
}
|
|
else if (!pSocket->IsSendBufFull())
|
|
{
|
|
// The send buffer opened up just as
|
|
// we were raising to DPC level
|
|
status = NETERR_OK;
|
|
}
|
|
else
|
|
{
|
|
CSendReq* newreq;
|
|
UINT size = sizeof(CSendReq) +
|
|
(pSendReq->toaddr ? sizeof(*pSendReq->toaddr) : 0) +
|
|
sizeof(WSABUF) * pSendReq->bufcnt;
|
|
|
|
newreq = (CSendReq*) SysAlloc(size, PTAG_CXmitReq);
|
|
if (!newreq)
|
|
{
|
|
// Out of memory
|
|
status = NETERR_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
VOID* bufs;
|
|
|
|
pSocket->SetSendReq(newreq);
|
|
*newreq = *pSendReq;
|
|
bufs = newreq+1;
|
|
if (pSendReq->toaddr) {
|
|
newreq->toaddr = (struct sockaddr_in*) bufs;
|
|
*newreq->toaddr = *pSendReq->toaddr;
|
|
bufs = newreq->toaddr + 1;
|
|
}
|
|
|
|
newreq->bufs = (WSABUF*) bufs;
|
|
memcpy(bufs, pSendReq->bufs, sizeof(WSABUF) * pSendReq->bufcnt);
|
|
|
|
newreq->_pWsaOverlapped->_ioxfercnt = 0;
|
|
newreq->_pWsaOverlapped->_ioflags = 0;
|
|
newreq->_pWsaOverlapped->_ioreq = (UINT_PTR) newreq;
|
|
newreq->_pWsaOverlapped->_iostatus = status = NETERR_PENDING;
|
|
}
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// inet_addr
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
ULONG _WSAAPI_ inet_addr(const char * pch)
|
|
{
|
|
WinsockApiPrologTrivial_(inet_addr);
|
|
WinsockApiParamCheck_(pch != NULL);
|
|
|
|
// Convert the string representation of IP address to its binary form.
|
|
// The following formats are recognized:
|
|
// a.b.c.d 8-8-8-8
|
|
// a.b.c 8-8-16
|
|
// a.b 8-24
|
|
// a 32
|
|
// Each field can be in decimal, octal, or hex format.
|
|
|
|
ULONG fields[4], addr;
|
|
UINT fieldcnt = 0;
|
|
const UCHAR* p = (const UCHAR *) pch;
|
|
|
|
// NOTE: We don't handle overflow conditions.
|
|
|
|
while (TRUE) {
|
|
// skip leading spaces
|
|
while (*p == ' ') p++;
|
|
if (fieldcnt >= 4 || *p == 0) break;
|
|
|
|
addr = 0;
|
|
if (*p == '0' && (p[1] == 'x' || p[1] == 'X')) {
|
|
// hex number
|
|
const UCHAR* q = (p += 2);
|
|
|
|
while (TRUE) {
|
|
if (*p >= '0' && *p <= '9')
|
|
addr = (addr << 4) + (*p - '0');
|
|
else if (*p >= 'a' && *p <= 'f')
|
|
addr = (addr << 4) + 10 + (*p - 'a');
|
|
else if (*p >= 'A' && *p <= 'F')
|
|
addr = (addr << 4) + 10 + (*p - 'A');
|
|
else
|
|
break;
|
|
p++;
|
|
}
|
|
|
|
if (q == p)
|
|
{
|
|
return(INADDR_NONE);
|
|
}
|
|
|
|
} else if (*p == '0') {
|
|
// octal number
|
|
do {
|
|
addr = (addr << 3) + (*p - '0');
|
|
p++;
|
|
} while (*p >= '0' && *p <= '7');
|
|
} else if (*p >= '1' && *p <= '9') {
|
|
// decimal number
|
|
do {
|
|
addr = addr*10 + (*p - '0');
|
|
p++;
|
|
} while (*p >= '0' && *p <= '9');
|
|
} else {
|
|
// invalid character
|
|
break;
|
|
}
|
|
|
|
// skip trailing spaces and . separator
|
|
while (*p == ' ') p++;
|
|
if (*p == '.') p++;
|
|
|
|
fields[fieldcnt++] = addr;
|
|
}
|
|
|
|
if (*p)
|
|
{
|
|
return(INADDR_NONE);
|
|
}
|
|
|
|
switch (fieldcnt) {
|
|
case 1:
|
|
addr = fields[0];
|
|
break;
|
|
case 2:
|
|
addr = ((fields[0] & 0xff) << 24) |
|
|
(fields[1] & 0xffffff);
|
|
break;
|
|
case 3:
|
|
addr = ((fields[0] & 0xff) << 24) |
|
|
((fields[1] & 0xff) << 16) |
|
|
(fields[2] & 0xffff);
|
|
break;
|
|
case 4:
|
|
addr = ((fields[0] & 0xff) << 24) |
|
|
((fields[1] & 0xff) << 16) |
|
|
((fields[2] & 0xff) << 8) |
|
|
(fields[3] & 0xff);
|
|
break;
|
|
|
|
default:
|
|
addr = 0;
|
|
break;
|
|
}
|
|
|
|
return(HTONL(addr));
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// Miscellanous
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
u_long _WSAAPI_ htonl(IN u_long hostlong) { return HTONL(hostlong); }
|
|
u_short _WSAAPI_ htons(IN u_short hostshort) { return HTONS(hostshort); }
|
|
u_long _WSAAPI_ ntohl(IN u_long netlong) { return NTOHL(netlong); }
|
|
u_short _WSAAPI_ ntohs(IN u_short netshort) { return NTOHS(netshort); }
|
|
int _WSAAPI_ WSAGetLastError() { return(GetLastError()); }
|
|
void _WSAAPI_ WSASetLastError(IN int error) { SetLastError(error); }
|
|
WSAEVENT _WSAAPI_ WSACreateEvent() { return(CreateEvent(NULL, TRUE, FALSE, NULL)); }
|
|
BOOL _WSAAPI_ WSACloseEvent(IN WSAEVENT hEvent) { return(CloseHandle(hEvent)); }
|
|
BOOL _WSAAPI_ WSASetEvent(IN WSAEVENT hEvent) { return(SetEvent(hEvent)); }
|
|
BOOL _WSAAPI_ WSAResetEvent(IN WSAEVENT hEvent) { return(ResetEvent(hEvent)); }
|
|
|
|
DWORD _WSAAPI_ WSAWaitForMultipleEvents(IN DWORD cEvents, IN const WSAEVENT* lphEvents,
|
|
IN BOOL fWaitAll, IN DWORD dwTimeout, IN BOOL fAlertable)
|
|
{
|
|
return(WaitForMultipleObjectsEx(cEvents, lphEvents, fWaitAll, dwTimeout, fAlertable));
|
|
}
|
|
|
|
int _WSAAPI_ __WSAFDIsSet(SOCKET fd, fd_set * set)
|
|
{
|
|
int i = (set->fd_count & 0xffff);
|
|
|
|
while (i--)
|
|
{
|
|
if (set->fd_array[i] == fd)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// SockLock
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
CSocket * CSocket::Lock(SOCKET s)
|
|
{
|
|
CSocket * pSocket;
|
|
LONG lLock;
|
|
int err;
|
|
|
|
// NOTE: We don't allow multiple threads to access the same
|
|
// socket simultaneously. If the app tries to do that, the first
|
|
// thread will succeed and other threads will get an error return.
|
|
|
|
if (s == 0 || s == INVALID_SOCKET)
|
|
{
|
|
err = WSAENOTSOCK;
|
|
}
|
|
else
|
|
{
|
|
pSocket = (CSocket *)s;
|
|
lLock = InterlockedCompareExchange(&pSocket->_lLock, SOCK_LOCK_BUSY, SOCK_LOCK_ACTIVE);
|
|
|
|
if (lLock == SOCK_LOCK_ACTIVE)
|
|
return(pSocket);
|
|
|
|
err = (lLock == SOCK_LOCK_BUSY) ? WSAEINPROGRESS : WSAENOTSOCK;
|
|
}
|
|
|
|
#if defined(XNET_FEATURE_TRACE) && !defined(XNET_FEATURE_XBDM_SERVER)
|
|
if (err == WSAEINPROGRESS)
|
|
{
|
|
TraceSz1(sockWarn, "[%X] Socket thread contention", s);
|
|
}
|
|
#endif
|
|
|
|
SetLastError(err);
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// CXnSock::SockInit
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
NTSTATUS CXnSock::SockInit(XNetInitParams * pxnip)
|
|
{
|
|
TCHECK(USER);
|
|
|
|
LARGE_INTEGER dueTime;
|
|
UINT period;
|
|
|
|
NTSTATUS status = IpInit(pxnip);
|
|
if (!NT_SUCCESS(status))
|
|
return(status);
|
|
|
|
SetInitFlag(INITF_SOCK);
|
|
|
|
InitializeListHead(&_leSockets);
|
|
InitializeListHead(&_leDeadSockets);
|
|
_ipportTempNext = MAX_TEMP_PORT;
|
|
|
|
return(NETERR_OK);
|
|
}
|
|
|
|
void CXnSock::SockTerm()
|
|
{
|
|
TCHECK(USER);
|
|
|
|
RaiseToDpc();
|
|
|
|
if (TestInitFlag(INITF_SOCK))
|
|
{
|
|
SockStop();
|
|
SockShutdown();
|
|
}
|
|
|
|
SetInitFlag(INITF_SOCK_TERM);
|
|
|
|
IpTerm();
|
|
}
|
|
|
|
void CXnSock::SockShutdown()
|
|
{
|
|
if (TestInitFlag(INITF_SOCK))
|
|
{
|
|
RaiseToDpc();
|
|
|
|
while (!IsListEmpty(&_leSockets))
|
|
{
|
|
CSocket * pSocket = (CSocket*)_leSockets.Flink;
|
|
|
|
if (pSocket->TestFlags(SOCKF_OWNED))
|
|
{
|
|
TraceSz1(LeakWarn, "Socket %08lX not closed before WSACleanup", pSocket);
|
|
}
|
|
|
|
SockClose(pSocket, TRUE);
|
|
}
|
|
|
|
while (!IsListEmpty(&_leDeadSockets))
|
|
{
|
|
CSocket * pSocket = (CSocket*) _leDeadSockets.Flink;
|
|
|
|
SockClose(pSocket, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// SockWaitForEvent
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
NTSTATUS CXnSock::SockWaitForEvent(CSocket* pSocket, INT eventMask, UINT timeout)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Block the current thread until the specified CSocket event is signalled
|
|
|
|
Arguments:
|
|
|
|
pSocket - Points to the protocol control block
|
|
eventMask - Flag bit to indicate which event to block on
|
|
timeout - Specifies the wait timeout (in milliseconds, 0 means forever)
|
|
|
|
Return Value:
|
|
|
|
Status code
|
|
|
|
--*/
|
|
|
|
{
|
|
INT readyMask;
|
|
NTSTATUS status = NETERR_OK;
|
|
|
|
//
|
|
// Check if the specified event is already available
|
|
// or if the socket connection has been reset.
|
|
//
|
|
readyMask = SockCheckSelectEvents(pSocket, eventMask, 1);
|
|
if (readyMask & SOCKF_EVENT_RESET) goto exit;
|
|
if (readyMask != 0) return NETERR_OK;
|
|
|
|
status = EvtWait(pSocket->GetEvent(), timeout) ? NETERR_OK : NETERR_TIMEOUT;
|
|
|
|
pSocket->ClearFlags(SOCKF_EVENT_MASK);
|
|
|
|
exit:
|
|
return NT_SUCCESS(status) ? pSocket->GetStatus() : status;
|
|
}
|
|
|
|
|
|
void CXnSock::SockReqComplete(CSocket * pSocket, CSockReq * pSockReq, NTSTATUS status)
|
|
{
|
|
if (pSockReq)
|
|
{
|
|
if (pSocket->GetSendReq() == pSockReq)
|
|
pSocket->SetSendReq(NULL);
|
|
else if (pSocket->GetRecvReq() == pSockReq)
|
|
pSocket->SetRecvReq(NULL);
|
|
|
|
pSockReq->_pWsaOverlapped->_iostatus = status;
|
|
|
|
EvtSet(pSockReq->_pEvent, EVENT_INCREMENT);
|
|
|
|
if (pSockReq->_pEvent != pSocket->GetEvent())
|
|
{
|
|
EvtDereference(pSockReq->_pEvent);
|
|
SysFree(pSockReq);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
CSocket * CXnSock::SockFindMatch(CIpPort toport, CIpAddr fromaddr, CIpPort fromport, BYTE type)
|
|
{
|
|
TCHECK(SDPC);
|
|
|
|
CSocket * pSocket;
|
|
CSocket * pSocketBest = NULL;
|
|
BOOL fIsUdp = (type == SOCK_DGRAM);
|
|
UINT cWildMin = 3;
|
|
UINT cWild;
|
|
|
|
for (pSocket = GetFirstSocket(); pSocket; pSocket = GetNextSocket(pSocket))
|
|
{
|
|
if (!!pSocket->IsUdp() != fIsUdp)
|
|
continue;
|
|
|
|
if (pSocket->_ipportSrc != toport)
|
|
continue;
|
|
|
|
cWild = 0;
|
|
|
|
if (pSocket->_ipaDst != fromaddr)
|
|
{
|
|
if (pSocket->_ipaDst)
|
|
continue;
|
|
|
|
cWild++;
|
|
}
|
|
|
|
if (pSocket->_ipportDst != fromport)
|
|
{
|
|
if (pSocket->_ipportDst)
|
|
continue;
|
|
|
|
cWild++;
|
|
}
|
|
|
|
if (cWild == 0)
|
|
{
|
|
pSocketBest = pSocket;
|
|
break;
|
|
}
|
|
|
|
if (cWild < cWildMin)
|
|
{
|
|
pSocketBest = pSocket;
|
|
cWildMin = cWild;
|
|
}
|
|
}
|
|
|
|
return(pSocketBest);
|
|
}
|
|
|
|
void CSocket::SetFlagsAndOr(DWORD dwAnd, DWORD dwOr)
|
|
{
|
|
ICHECK_(GetXnBase(), SOCK, USER|UDPC|SDPC);
|
|
|
|
DWORD dwFlagsOld, dwFlagsNew;
|
|
|
|
while (1)
|
|
{
|
|
dwFlagsOld = _dwFlags;
|
|
dwFlagsNew = (dwFlagsOld & dwAnd) | dwOr;
|
|
|
|
if (InterlockedCompareExchange((LONG *)&_dwFlags, (LONG)dwFlagsNew, (LONG)dwFlagsOld) == (LONG)dwFlagsOld)
|
|
break;
|
|
|
|
TraceSz(Warning, "CSocket::SetFlagsAndOr: Thread/DPC contention detected. Retrying.");
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// SockReset
|
|
// ---------------------------------------------------------------------------------------
|
|
|
|
void CXnSock::SockReset(CIpAddr ipa)
|
|
{
|
|
TCHECK(UDPC|SDPC);
|
|
|
|
if (ipa != 0 && TestInitFlag(INITF_SOCK) && !TestInitFlag(INITF_SOCK_TERM))
|
|
{
|
|
CSocket * pSocket;
|
|
CSocket * pSocketNext;
|
|
CTcpSocket * pTcpSocket;
|
|
|
|
for (pSocket = GetFirstSocket(); pSocket; pSocket = pSocketNext)
|
|
{
|
|
pSocketNext = GetNextSocket(pSocket);
|
|
|
|
if (pSocket->IsTcp() && pSocket->_ipaDst == ipa)
|
|
{
|
|
pTcpSocket = (CTcpSocket *)pSocket;
|
|
|
|
if (!pTcpSocket->IsIdleState())
|
|
{
|
|
TcpReset(pTcpSocket, NETERR_CONNRESET);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|