Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

987 lines
36 KiB

/*==========================================================================;
*
* Copyright (C) 1994-1997 Microsoft Corporation. All Rights Reserved.
*
* File: wsock2.c
* Content: DirectPlay Winsock 2 SP support. Called from dpsp.c.
* History:
* Date By Reason
* ==== == ======
* 7/11//97 andyco created it
* 2/13/98 aarono added async support.
* 4/6/98 aarono mapped WSAECONNRESET to DPERR_CONNECTIONLOST
* 6/6/98 aarono B#27187 fix ref counting on send blocks in sync error case
* 7/9/99 aarono Cleaning up GetLastError misuse, must call right away,
* before calling anything else, including DPF.
**************************************************************************/
// this module is for async connections and sends
// currently only used as the reply thread proc for async replies. see dpsp.c::sp_reply
#define INCL_WINSOCK_API_TYPEDEFS 1 // includes winsock 2 fn proto's, for getprocaddress
#include <winsock2.h>
#include "dpsp.h"
#undef DPF_MODNAME
#define DPF_MODNAME "AsyncSendThreadProc"
extern HINSTANCE hWS2; // dynaload the ws2_32.dll, so if it's not installed
// (e.g. win 95 gold) we still load
extern HINSTANCE hWSHIP6; // dynaload the wship6.dll, so if it's not installed
// prototypes for our dynaload fn's
LPFN_WSAWAITFORMULTIPLEEVENTS g_WSAWaitForMultipleEvents;
LPFN_WSASEND g_WSASend;
LPFN_WSASENDTO g_WSASendTo;
LPFN_WSACLOSEEVENT g_WSACloseEvent;
LPFN_WSACREATEEVENT g_WSACreateEvent;
LPFN_WSAENUMNETWORKEVENTS g_WSAEnumNetworkEvents;
LPFN_WSAEVENTSELECT g_WSAEventSelect;
LPFN_GETSOCKOPT g_getsockopt;
LPFN_GETADDRINFO g_getaddrinfo;
LPFN_FREEADDRINFO g_freeaddrinfo;
// if no getaddrinfo, try loading from wship6.dll for Win2000 tech preview
HRESULT InitWship6()
{
hWSHIP6 = LoadLibrary("wship6.dll");
if (!hWSHIP6)
{
DPF(0,"Could not load wship6.dll\n");
goto LOADLIBRARYFAILED;
}
// get pointers to the entry points we need
g_getaddrinfo = (LPFN_GETADDRINFO)GetProcAddress(hWSHIP6, "getaddrinfo");
if (!g_getaddrinfo) goto GETPROCADDRESSFAILED;
g_freeaddrinfo = (LPFN_FREEADDRINFO)GetProcAddress(hWSHIP6, "freeaddrinfo");
if (!g_freeaddrinfo) goto GETPROCADDRESSFAILED;
return DP_OK;
GETPROCADDRESSFAILED:
FreeLibrary(hWSHIP6);
hWSHIP6 = NULL;
// fall through
LOADLIBRARYFAILED:
g_getaddrinfo = NULL;
g_freeaddrinfo = NULL;
return DPERR_UNAVAILABLE;
}
// attempt to load the winsock 2 dll, and get our proc addresses from it
HRESULT InitWinsock2()
{
// load winsock library
hWS2 = LoadLibrary("ws2_32.dll");
if (!hWS2)
{
DPF(0,"Could not load ws2_32.dll\n");
// reset our winsock 2 global
goto LOADLIBRARYFAILED;
}
// get pointers to the entry points we need
g_WSAWaitForMultipleEvents = (LPFN_WSAWAITFORMULTIPLEEVENTS)GetProcAddress(hWS2, "WSAWaitForMultipleEvents");
if(!g_WSAWaitForMultipleEvents) goto GETPROCADDRESSFAILED;
g_WSASend = (LPFN_WSASEND)GetProcAddress(hWS2, "WSASend");
if (!g_WSASend) goto GETPROCADDRESSFAILED;
g_WSASendTo = (LPFN_WSASENDTO)GetProcAddress(hWS2, "WSASendTo");
if (!g_WSASendTo) goto GETPROCADDRESSFAILED;
g_WSAEventSelect = ( LPFN_WSAEVENTSELECT )GetProcAddress(hWS2, "WSAEventSelect");
if (!g_WSAEventSelect) goto GETPROCADDRESSFAILED;
g_WSAEnumNetworkEvents = (LPFN_WSAENUMNETWORKEVENTS)GetProcAddress(hWS2, "WSAEnumNetworkEvents");
if (!g_WSAEnumNetworkEvents) goto GETPROCADDRESSFAILED;
g_WSACreateEvent = (LPFN_WSACREATEEVENT)GetProcAddress(hWS2, "WSACreateEvent");
if (!g_WSACreateEvent) goto GETPROCADDRESSFAILED;
g_WSACloseEvent = (LPFN_WSACLOSEEVENT)GetProcAddress(hWS2, "WSACloseEvent");
if (!g_WSACloseEvent) goto GETPROCADDRESSFAILED;
g_getsockopt = (LPFN_GETSOCKOPT)GetProcAddress(hWS2, "getsockopt");
if (!g_getsockopt) goto GETPROCADDRESSFAILED;
g_getaddrinfo = (LPFN_GETADDRINFO)GetProcAddress(hWS2, "getaddrinfo");
g_freeaddrinfo = (LPFN_FREEADDRINFO)GetProcAddress(hWS2, "freeaddrinfo");
if (!g_getaddrinfo || !g_freeaddrinfo)
{
if (FAILED(InitWship6()))
{
goto GETPROCADDRESSFAILED;
}
}
return DP_OK;
GETPROCADDRESSFAILED:
DPF(0,"Could not find required Winsock entry point");
FreeLibrary(hWS2);
hWS2 = NULL;
// fall through
LOADLIBRARYFAILED:
g_WSAEventSelect = NULL;
g_WSAEnumNetworkEvents = NULL;
g_WSACreateEvent = NULL;
g_WSACloseEvent = NULL;
return DPERR_UNAVAILABLE;
} // InitWinsock2
// remove the reply node from the list
void DeleteReplyNode(LPGLOBALDATA pgd,LPREPLYLIST prd, BOOL bKillSocket)
{
LPREPLYLIST prdPrev;
ENTER_DPSP();
// 1st, remove prd from the list
// is it the root?
if (prd == pgd->pReplyList) pgd->pReplyList = pgd->pReplyList->pNextReply;
else
{
BOOL bFound = FALSE;
// it's not the root - take it out of the middle
prdPrev = pgd->pReplyList;
while (prdPrev && !bFound)
{
if (prdPrev->pNextReply == prd)
{
prdPrev->pNextReply = prd->pNextReply;
bFound = TRUE;
}
else
{
prdPrev = prdPrev->pNextReply;
}
} // while
ASSERT(bFound);
} // not the root
// now clean up prd
// nuke the socket
if (bKillSocket)
KillSocket(prd->sSocket,TRUE,FALSE);
// free up the node
if (prd->lpMessage) MemFree(prd->lpMessage);
MemFree(prd);
LEAVE_DPSP();
return ;
} // DeleteReplyNode
/*
** AsyncConnectAndSend
*
* CALLED BY: AsyncSendThreadProc
*
* DESCRIPTION:
*
* if necessary, creates a non-blocking socket, and initiates a connection
* to address specified in prd
* once connection has been completed, does a synchronous (blocking) send and
* removes prd from the global list
*/
HRESULT AsyncConnectAndSend(LPGLOBALDATA pgd,LPREPLYLIST prd)
{
UINT err;
HRESULT hr;
SOCKADDR_IN6 sockaddr;
UINT addrlen = sizeof(SOCKADDR_IN6);
BOOL bConnectionExists = FALSE;
BOOL bKillConnection = TRUE;
if (INVALID_SOCKET == prd->sSocket)
{
u_long lNonBlock = 1; // passed to ioctlsocket to make socket non-blocking
DPID dpidPlayer=0;
#ifdef FULLDUPLEX_SUPPORT
// if client wants us to reuse a connection, it would have indicated so and the connection
// would have been added to our send list by now. See if it exists.
// TODO - we don't want to search the list everytime - find a better way
bConnectionExists = FindSocketInBag(pgd, &prd->sockaddr, &prd->sSocket, &dpidPlayer);
#endif // FULLDUPLEX_SUPPORT
if (!bConnectionExists)
{
SOCKET sSocket;
// socket didn't exist in our send list, let's send it on a new temporary connection
DEBUGPRINTADDR(9,"Sending aync reply on a new connection to - ", &(prd->sockaddr));
// need to get the new socket
hr = CreateSocket(pgd,&sSocket,SOCK_STREAM,0,&sockaddr_any,&err,FALSE);
if (FAILED(hr))
{
DPF(0,"create async socket failed - err = %d\n",err);
return hr;
}
prd->sSocket = sSocket;
// set socket to non-blocking
err = ioctlsocket(prd->sSocket,FIONBIO,&lNonBlock);
if (SOCKET_ERROR == err)
{
err = WSAGetLastError();
DPF(0,"could not set non-blocking mode on socket err = %d!",err);
DPF(0,"will revert to synchronous behavior. bummer");
}
// now, start the connect
SetReturnAddress(prd->lpMessage,pgd->sSystemStreamSocket);
err = connect(prd->sSocket,(LPSOCKADDR)&prd->sockaddr,addrlen);
if (SOCKET_ERROR == err)
{
err = WSAGetLastError();
if (WSAEWOULDBLOCK == err)
{
// this is expected. the operation needs time to complete.
// select will tell us when the socket is good to go.
return DP_OK;
}
// else it's a real error!
DPF(0,"async reply - connect failed - error = %d\n",err);
DEBUGPRINTADDR(0,"async reply - connect failed - addr = ",(LPSOCKADDR)&(prd->sockaddr));
goto CLEANUP_EXIT;
}
}
else
{
// we found our connection, let's reuse it
// set it to non-blocking
DEBUGPRINTADDR(9,"Sending async reply on an existing connection to - ", &(prd->sockaddr));
err = ioctlsocket(prd->sSocket,FIONBIO,&lNonBlock);
if (SOCKET_ERROR == err)
{
err = WSAGetLastError();
DPF(0,"could not set non-blocking mode on socket err = %d!",err);
DPF(0,"will revert to synchronous behavior. bummer");
}
// once we have a player id, the session has started. let's hold on to the connection
// we have and reuse it for the rest of the session
if (dpidPlayer) bKillConnection = FALSE;
} // FindSocketInBag
} // INVALID_SOCKET
// once we get here, we should have a connected socket ready to send!
err = 0;
// keep spitting bits at the socket until we finish or get an error
while ((prd->dwBytesLeft != 0) && (SOCKET_ERROR != err))
{
err = send(prd->sSocket,prd->pbSend,prd->dwBytesLeft,0);
if (SOCKET_ERROR != err)
{
// some bytes went out on the wire
prd->dwBytesLeft -= err; // we just sent err bytes
prd->pbSend += err; // advance our send buffer by err bytes
}
}
// now, we've either finished the send, or we have an error
if (SOCKET_ERROR == err)
{
err = WSAGetLastError();
if (WSAEWOULDBLOCK == err)
{
// this means we couldn't send any bytes w/o blocking
// that's ok. we'll let select tell us when it's ready to not block
return DP_OK;
}
// else it's a real eror!
// any other error, we give up and clean up this reply
DPF(0,"async send - send failed - error = %d\n",err);
DEBUGPRINTADDR(0,"async send - send failed - addr = ",(LPSOCKADDR)&(prd->sockaddr));
}
else ASSERT(0 == prd->dwBytesLeft); // if it's not an error, we better have sent it all
// fall through
CLEANUP_EXIT:
if (bConnectionExists && bKillConnection)
{
// close the connection after we're done
RemoveSocketFromReceiveList(pgd,prd->sSocket);
RemoveSocketFromBag(pgd,prd->sSocket);
// so DeleteReplyNode won't try to kill socket again
prd->sSocket = INVALID_SOCKET;
}
// remove the node from the list
DeleteReplyNode(pgd,prd,bKillConnection);
return DP_OK;
} // AsyncConnectAndSend
// walk the reply list, tell winsock to watch any of the nodes which has a valid socket
// (i.e. has a connection or send pending)
HRESULT DoEventSelect(LPGLOBALDATA pgd,WSAEVENT hSelectEvent)
{
UINT err;
LPREPLYLIST prd;
ENTER_DPSP();
prd = pgd->pReplyList;
while (prd)
{
if (INVALID_SOCKET != prd->sSocket)
{
// have winscok tell us when anything good (connection complete, ready to write more data)
// happens on this socket
err = g_WSAEventSelect(prd->sSocket,hSelectEvent,FD_WRITE | FD_CONNECT);
if (SOCKET_ERROR == err)
{
err = WSAGetLastError();
DPF(0,"could not do event select ! err = %d!",err);
// keep trying...
}
} // invalid_socket
prd = prd->pNextReply;
}
LEAVE_DPSP();
return DP_OK;
} // DoEventSelect
// wsaeventselect woke us up. one or more of our sockets had something happen
// (e.g. connect completed, send ready for more data, etc.)
// walk the reply list, find nodes who need to be serviced
void ServiceReplyList(LPGLOBALDATA pgd,WSAEVENT hEvent)
{
UINT err;
LPREPLYLIST prd,prdNext;
WSANETWORKEVENTS WSANetEvents;
ENTER_DPSP();
Top:
prd = pgd->pReplyList;
while (prd)
{
// save this now - asyncconnectandsend could destroy prd
prdNext = prd->pNextReply;
if (INVALID_SOCKET != prd->sSocket)
{
// go ask winsock if this socket had anything intersting happen
err = g_WSAEnumNetworkEvents(prd->sSocket,NULL,&WSANetEvents);
if (SOCKET_ERROR == err)
{
err = WSAGetLastError();
DPF(0,"could not enum events!! err = %d!",err);
// keep trying...
}
else
{
BOOL bError=FALSE;
// no error - go see what we got
if ((WSANetEvents.lNetworkEvents & FD_CONNECT) || (WSANetEvents.lNetworkEvents & FD_WRITE))
{
// was there an error?
if (WSANetEvents.iErrorCode[FD_CONNECT_BIT])
{
// we got a connect error!
DPF(0,"async reply - WSANetEvents - connect failed - error = %d\n",
WSANetEvents.iErrorCode[FD_CONNECT_BIT]);
DEBUGPRINTADDR(0,"async reply - connect failed - addr = ",
(LPSOCKADDR)&(prd->sockaddr));
RemovePendingAsyncSends(pgd, prd->dwPlayerTo);
goto Top;
}
if (WSANetEvents.iErrorCode[FD_WRITE_BIT])
{
// we got a send error!
DPF(0,"async reply - WSANetEvents - send failed - error = %d\n",
WSANetEvents.iErrorCode[FD_WRITE_BIT]);
DEBUGPRINTADDR(0,"async reply - send failed - addr = ",
(LPSOCKADDR)&(prd->sockaddr));
RemovePendingAsyncSends(pgd, prd->dwPlayerTo);
goto Top;
}
// note - we try + send even if there was an error. seems like it's worth a shot...
// go try + send
AsyncConnectAndSend(pgd,prd);
}
}
} // invalid_socket
else
{
// it it's an invalid socket, we need to init our connect and send
AsyncConnectAndSend(pgd,prd);
}
prd = prdNext;
}
LEAVE_DPSP();
return ;
} // ServiceReplyList
// this thread works on doing async sends
DWORD WINAPI AsyncSendThreadProc(LPVOID pvCast)
{
HRESULT hr=DP_OK;
LPGLOBALDATA pgd = (LPGLOBALDATA) pvCast;
HANDLE hHandleList[3];
DWORD rc;
WSAEVENT hSelectEvent; // event used by WSASelectEvent
DPF(9,"Entered AsyncSendThreadProc\n");
// get the event 4 selectevent
hSelectEvent = g_WSACreateEvent();
if (WSA_INVALID_EVENT == hSelectEvent)
{
rc = WSAGetLastError();
DPF(0,"could not create winsock event - rc = %d\n",rc);
ExitThread(0);
return 0;
}
hHandleList[0] = hSelectEvent;
hHandleList[1] = pgd->hReplyEvent;
// This extra handle is here because of a Windows 95 bug. Windows
// will occasionally miss when it walks the handle table, causing
// my thread to wait on the wrong handles. By putting a guaranteed
// invalid handle at the end of our array, the kernel will do a
// forced re-walk of the handle table and find the correct handles.
hHandleList[2] = INVALID_HANDLE_VALUE;
while (1)
{
// tell winsock to watch all of our reply nodes. it will set our event
// when something cool happens...
DoEventSelect(pgd,hSelectEvent);
// wait on our event. when it's set, we either split, or empty the reply list
rc = WaitForMultipleObjectsEx(2,hHandleList,FALSE,INFINITE,TRUE);
if ((DWORD)-1 == rc)
{
DWORD dwError = GetLastError();
// rut roh! errror on the wait
DPF(0,"!!!!! error on WaitForMultipleObjects -- async reply bailing -- dwError = %d",dwError);
goto CLEANUP_EXIT;
}
if (rc == WAIT_OBJECT_0) // a-josbor: need to reset this manual event
{
ResetEvent(hSelectEvent);
}
// ok. someone woke us up. it could be 1. shutdown, or 2. one
// of our sockets needs attention (i.e. a connect completed), or 3. someone
// put a new reply node on the list
// shutdown?
if (pgd->bShutdown)
{
goto CLEANUP_EXIT;
}
DPF(8,"In AsyncSendThreadProc, servicing event %d\n", rc - WAIT_OBJECT_0);
// otherwise, it must be a socket in need or a new replynode
ServiceReplyList(pgd,hSelectEvent);
} // 1
CLEANUP_EXIT:
ENTER_DPSP();
// cleanout reply list
while (pgd->pReplyList) DeleteReplyNode(pgd,pgd->pReplyList,TRUE);
CloseHandle(pgd->hReplyEvent);
pgd->hReplyEvent = 0;
LEAVE_DPSP();
g_WSACloseEvent(hSelectEvent);
DPF(6,"replythreadproc exit");
return 0;
} // AsyncSendThreadProc
HRESULT GetMaxUdpBufferSize(SOCKET socket, UINT * piMaxUdpDg)
{
INT iBufferSize;
INT err;
ASSERT(piMaxUdpDg);
iBufferSize = sizeof(UINT);
err = g_getsockopt(socket, SOL_SOCKET, SO_MAX_MSG_SIZE, (LPBYTE)piMaxUdpDg, &iBufferSize);
if (SOCKET_ERROR == err)
{
DPF(0,"getsockopt for SO_MAX_MSG_SIZE returned err = %d", WSAGetLastError());
return DPERR_UNAVAILABLE;
}
return DP_OK;
}
int
Dplay_GetAddrInfo(const char FAR * nodename, const char FAR * servname, LPADDRINFO hints, ADDRINFO FAR * FAR * res)
{
return g_getaddrinfo(nodename, servname, hints, res);
}
void
Dplay_FreeAddrInfo(LPADDRINFO ai)
{
g_freeaddrinfo(ai);
}
#ifdef SENDEX
DWORD wsaoDecRef(LPSENDINFO pSendInfo)
{
#define pgd (pSendInfo->pgd)
DWORD count;
#ifdef DEBUG
EnterCriticalSection(&pgd->csSendEx);
count=(--pSendInfo->RefCount);
LeaveCriticalSection(&pgd->csSendEx);
#else
count=InterlockedDecrement(&pSendInfo->RefCount);
#endif
if(!count){
EnterCriticalSection(&pgd->csSendEx);
Delete(&pSendInfo->PendingSendQ);
pgd->dwBytesPending -= pSendInfo->dwMessageSize;
pgd->dwMessagesPending -= 1;
LeaveCriticalSection(&pgd->csSendEx);
DPF(9,"RefCount 0 pSendInfo %x , SC context %x, status=%x \n",pSendInfo, pSendInfo->dwUserContext,pSendInfo->Status);
if(pSendInfo->dwSendFlags & DPSEND_ASYNC){
pSendInfo->lpISP->lpVtbl->SendComplete(pSendInfo->lpISP,(LPVOID)pSendInfo->dwUserContext,pSendInfo->Status);
}
pgd->pSendInfoPool->Release(pgd->pSendInfoPool, pSendInfo);
} else {
DPF(9,"wsaoDecRef pSendInfo %x, Refcount= %d\n",pSendInfo,pSendInfo->RefCount);
}
if(count& 0x80000000){
DEBUG_BREAK();
}
return count;
#undef pgd
}
void CALLBACK SendComplete(
DWORD dwError,
DWORD cbTransferred,
LPWSAOVERLAPPED lpOverlapped,
DWORD dwFlags
)
{
LPSENDINFO lpSendInfo=(LPSENDINFO)CONTAINING_RECORD(lpOverlapped,SENDINFO,wsao);
DPF(9,"DPWSOCK:SendComplete, lpSendInfo %x\n",lpSendInfo);
if(dwError){
DPF(0,"DPWSOCK: send completion error, dwError=x%x\n",dwError);
lpSendInfo->Status=DPERR_GENERIC;
}
wsaoDecRef(lpSendInfo);
}
HRESULT DoSend(LPGLOBALDATA pgd, LPSENDINFO pSendInfo)
{
#define fAsync (pSendInfo->dwSendFlags & DPSEND_ASYNC)
DWORD dwBytesSent;
UINT err;
HRESULT hr;
if(pSendInfo->dwFlags & SI_RELIABLE){
// Reliable Send
DPF(9,"WSASend, pSendInfo %x\n",pSendInfo);
// send the message
err = g_WSASend(pSendInfo->sSocket,
(LPWSABUF)&pSendInfo->SendArray[pSendInfo->iFirstBuf],
pSendInfo->cBuffers,
&dwBytesSent,
0, /*flags*/
(fAsync)?(&pSendInfo->wsao):NULL,
(fAsync)?(SendComplete):NULL);
if(!err){
DPF(9,"WSASend, sent synchronously, pSendInfo %x\n",pSendInfo);
wsaoDecRef(pSendInfo);
hr=DP_OK;
} else {
if (SOCKET_ERROR == err)
{
err = WSAGetLastError();
if(err==WSA_IO_PENDING){
hr=DPERR_PENDING;
wsaoDecRef(pSendInfo);
DPF(9,"ASYNC SEND Pending pSendInfo %x\n",pSendInfo);
} else {
if(err==WSAECONNRESET){
hr=DPERR_CONNECTIONLOST;
} else {
hr=DPERR_GENERIC;
}
if(fAsync){
// Got an error, need to dump 2 refs.
pSendInfo->RefCount=1;
pSendInfo->Status=hr;
}
wsaoDecRef(pSendInfo);
// we got a socket from the bag. send failed,
// so we're cruising it from the bag
DPF(0,"send error - err = %d\n",err);
DPF(4,"send failed - removing socket from bag");
RemovePlayerFromSocketBag(pgd,pSendInfo->idTo);
}
}
}
} else {
// Datagram Send
DEBUGPRINTADDR(5,"unreliable send - sending to ",&pSendInfo->sockaddr);
// send the message
err = g_WSASendTo(pSendInfo->sSocket,
(LPWSABUF)&pSendInfo->SendArray[pSendInfo->iFirstBuf],
pSendInfo->cBuffers,
&dwBytesSent,
0, /*flags*/
(LPSOCKADDR)&pSendInfo->sockaddr,
sizeof(SOCKADDR),
(fAsync)?(&pSendInfo->wsao):NULL,
(fAsync)?(SendComplete):NULL);
if(!err){
hr=DP_OK;
wsaoDecRef(pSendInfo);
} else {
if (SOCKET_ERROR == err)
{
err = WSAGetLastError();
if(err==WSA_IO_PENDING){
hr=DPERR_PENDING;
wsaoDecRef(pSendInfo);
} else {
hr=DPERR_GENERIC;
if(fAsync){
// some error, force completion.
pSendInfo->RefCount=1;
pSendInfo->Status=DPERR_GENERIC;
}
wsaoDecRef(pSendInfo);
DPF(0,"send error - err = %d\n",err);
}
} else {
DEBUG_BREAK();// SHOULD NEVER HAPPEN
}
}
}
return hr;
#undef fAsync
}
// Alert thread provides a thread for send completions to run on.
DWORD WINAPI SPSendThread(LPVOID lpv)
{
LPGLOBALDATA pgd=(LPGLOBALDATA) lpv;
LPSENDINFO pSendInfo;
DWORD rcWait=WAIT_IO_COMPLETION;
BILINK *pBilink;
BOOL bSent;
pgd->BogusHandle=INVALID_HANDLE_VALUE; // workaround win95 wait for multiple bug.
while(!pgd->bStopSendThread){
rcWait=g_WSAWaitForMultipleEvents(1,&pgd->hSendWait,FALSE,INFINITE,TRUE);
#ifdef DEBUG
if(rcWait==WAIT_IO_COMPLETION){
DPF(9,"ooooh, IO completion\n");
}
#endif
do {
bSent = FALSE;
EnterCriticalSection(&pgd->csSendEx);
pBilink=pgd->ReadyToSendQ.next;
if(pBilink != &pgd->ReadyToSendQ){
Delete(pBilink);
LeaveCriticalSection(&pgd->csSendEx);
pSendInfo=CONTAINING_RECORD(pBilink, SENDINFO, ReadyToSendQ);
DoSend(pgd, pSendInfo);
bSent=TRUE;
} else {
LeaveCriticalSection(&pgd->csSendEx);
}
} while (bSent);
}
pgd->bSendThreadRunning=FALSE;
return FALSE;
#undef hWait
}
void QueueForSend(LPGLOBALDATA pgd,LPSENDINFO pSendInfo)
{
EnterCriticalSection(&pgd->csSendEx);
InsertBefore(&pSendInfo->ReadyToSendQ,&pgd->ReadyToSendQ);
LeaveCriticalSection(&pgd->csSendEx);
SetEvent(pgd->hSendWait);
}
// some common code for InternalReliableSendEx and UnreliableSendEx
VOID CommonInitForSend(LPGLOBALDATA pgd,LPDPSP_SENDEXDATA psd,LPSENDINFO pSendInfo)
{
pSendInfo->dwMessageSize= psd->dwMessageSize;
pSendInfo->dwUserContext= (DWORD_PTR)psd->lpDPContext;
pSendInfo->RefCount = 2; // one for completion, 1 for this routine
pSendInfo->pgd = pgd;
pSendInfo->lpISP = psd->lpISP;
pSendInfo->Status = DP_OK;
pSendInfo->idTo = psd->idPlayerTo;
pSendInfo->idFrom = psd->idPlayerFrom;
pSendInfo->dwSendFlags = psd->dwFlags;
if(psd->lpdwSPMsgID){
*psd->lpdwSPMsgID=0;
}
EnterCriticalSection(&pgd->csSendEx);
InsertBefore(&pSendInfo->PendingSendQ,&pgd->PendingSendQ);
pgd->dwBytesPending += psd->dwMessageSize;
pgd->dwMessagesPending += 1;
LeaveCriticalSection(&pgd->csSendEx);
}
VOID UnpendSendInfo(LPGLOBALDATA pgd, LPSENDINFO pSendInfo)
{
EnterCriticalSection(&pgd->csSendEx);
Delete(&pSendInfo->PendingSendQ);
pgd->dwBytesPending -= pSendInfo->dwMessageSize;
pgd->dwMessagesPending -= 1;
LeaveCriticalSection(&pgd->csSendEx);
}
HRESULT InternalReliableSendEx(LPGLOBALDATA pgd, LPDPSP_SENDEXDATA psd, LPSENDINFO pSendInfo, SOCKADDR_IN6 *lpSockAddr)
{
HRESULT hr;
SOCKET sSocket = INVALID_SOCKET;
BOOL fCreate=FALSE;
// see if we have a connection already
hr = GetSocketFromBag(pgd,&sSocket,psd->idPlayerTo,lpSockAddr);
if(hr != DP_OK){
hr=DPERR_GENERIC;
return hr;
}
CommonInitForSend(pgd,psd,pSendInfo); // puts 2 refs on send.
pSendInfo->dwFlags = SI_RELIABLE;
pSendInfo->sSocket = sSocket;
pSendInfo->iFirstBuf = 0;
pSendInfo->cBuffers = psd->cBuffers+1;
if(psd->dwFlags & DPSEND_ASYNC){
QueueForSend(pgd,pSendInfo);
hr=DPERR_PENDING;
} else {
hr=DoSend(pgd,pSendInfo);
if(hr==DP_OK || hr==DPERR_PENDING){
wsaoDecRef(pSendInfo);
} else {
// error,
UnpendSendInfo(pgd, pSendInfo);
}
}
return hr;
}
HRESULT UnreliableSendEx(LPDPSP_SENDEXDATA psd, LPSENDINFO pSendInfo)
{
SOCKADDR_IN6 sockaddr;
INT iAddrLen = sizeof(sockaddr);
HRESULT hr=DP_OK;
UINT err;
DWORD dwSize = sizeof(SPPLAYERDATA);
LPSPPLAYERDATA ppdTo;
DWORD dwDataSize = sizeof(GLOBALDATA);
LPGLOBALDATA pgd;
BOOL bSendHeader;
// get the global data
hr =psd->lpISP->lpVtbl->GetSPData(psd->lpISP,(LPVOID *)&pgd,&dwDataSize,DPGET_LOCAL);
if (FAILED(hr) || (dwDataSize != sizeof(GLOBALDATA) ))
{
DPF_ERR("couldn't get SP data from DirectPlay - failing");
return E_FAIL;
}
if (pgd->iMaxUdpDg && (psd->dwMessageSize >= pgd->iMaxUdpDg))
{
return DPERR_SENDTOOBIG;
}
if (INVALID_SOCKET == pgd->sUnreliableSocket)
{
hr = CreateSocket(pgd,&(pgd->sUnreliableSocket),SOCK_DGRAM,0,&sockaddr_any,&err,FALSE);
if (FAILED(hr))
{
DPF(0,"create unreliable send socket failed - err = %d\n",err);
return hr;
}
}
// get to address
if (0 == psd->idPlayerTo)
{
sockaddr = pgd->saddrNS;
}
else
{
hr = psd->lpISP->lpVtbl->GetSPPlayerData(psd->lpISP,psd->idPlayerTo,&ppdTo,&dwSize,DPGET_REMOTE);
if (FAILED(hr))
{
ASSERT(FALSE);
return hr;
}
sockaddr = *(DGRAM_PSOCKADDR(ppdTo));
}
// put the token + size on front of the mesage
SetMessageHeader((LPVOID)(pSendInfo->SendArray[0].buf),psd->dwMessageSize+sizeof(MESSAGEHEADER),TOKEN);
bSendHeader=TRUE;
if (psd->bSystemMessage)
{
SetReturnAddress(pSendInfo->SendArray[0].buf,SERVICE_SOCKET(pgd));
} // reply
else
{
// see if we can send this message w/ no header
// if the message is smaller than a dword, or, if it's a valid sp header (fooling us
// on the other end, don't send any header
if ( !((psd->dwMessageSize >= sizeof(DWORD)) && !(VALID_SP_MESSAGE(pSendInfo->SendArray[0].buf))) )
{
bSendHeader=FALSE;
}
}
CommonInitForSend(pgd,psd,pSendInfo);
pSendInfo->dwFlags = SI_DATAGRAM;
pSendInfo->sSocket = pgd->sUnreliableSocket;
pSendInfo->sockaddr = sockaddr;
if(bSendHeader){
pSendInfo->iFirstBuf=0;
pSendInfo->cBuffers =psd->cBuffers+1;
} else {
pSendInfo->iFirstBuf=1;
pSendInfo->cBuffers=psd->cBuffers;
}
if(psd->dwFlags & DPSEND_ASYNC){
QueueForSend(pgd,pSendInfo);
hr=DPERR_PENDING;
} else {
hr=DoSend(pgd,pSendInfo);
if(hr==DP_OK || hr==DPERR_PENDING){
wsaoDecRef(pSendInfo);
} else {
UnpendSendInfo(pgd, pSendInfo);
}
}
return hr;
} // UnreliableSendEx
#endif //SendEx