|
|
#include <rrcm.h>
#include <queue.h>
// Forward declarations
DWORD WINAPI WS1MsgThread (LPVOID ); LRESULT CALLBACK WS1WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); BOOL StartWS1MsgThread(); BOOL StopWS1MsgThread(); void __stdcall SendRecvCompleteAPC(ULONG_PTR dw);
class CWS2EmulSock;
#define WM_WSA_READWRITE (WM_USER + 100)
// structure used to store WSASendTo/WSARecvFrom parameters
struct WSAIOREQUEST { WSAOVERLAPPED *pOverlapped; WSABUF wsabuf[2]; DWORD dwBufCount; union { struct { struct sockaddr *pRecvFromAddr; LPINT pRecvFromLen; }; struct sockaddr SendToAddr; }; };
// global Winsock emulation state
struct WS2Emul { #define MAX_EMUL_SOCKETS 10
CWS2EmulSock *m_pEmulSocks[MAX_EMUL_SOCKETS]; int numSockets; HWND hWnd; HANDLE hMsgThread; HANDLE hAckEvent; // external crit sect serializes the WS2EmulXX apis
// to make them multi-thread safe
CRITICAL_SECTION extcs; // internal crit sect serializes access between
// MsgThread and WS2EmulXX apis
// Never claim extcs while holding intcs.
// (Is there a more elegant way to do this?)
CRITICAL_SECTION intcs; } g_WS2Emul;
/*
CWS2EmulSock - WS2 socket emulation class Manages queues of overlapped i/o requests */
class CWS2EmulSock { public: CWS2EmulSock(int myIndex) : m_myIndex(myIndex), m_RecvThreadId(0),m_SendThreadId(0), m_hRecvThread(NULL), m_hSendThread(NULL), m_sock(INVALID_SOCKET) { ZeroMemory(&m_SendOverlapped, sizeof(m_SendOverlapped));} ; BOOL NewSock(int af, int type, int protocol); int Close(); int RecvFrom( LPWSABUF lpBuffers,DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, struct sockaddr FAR * lpFrom, LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); int SendTo( LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent,DWORD dwFlags, const struct sockaddr FAR * lpTo, int iTolen, LPWSAOVERLAPPED lpOverlapped,LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
LRESULT HandleMessage(WPARAM wParam, LPARAM lParam); // WS1 window msg handler
SOCKET GetSocket() { return m_sock;} WSAOVERLAPPED *GetSendOverlapped() {return &m_SendOverlapped;} private: SOCKET m_sock; // real socket handle
int m_myIndex; // fake socket handle
QueueOf<WSAIOREQUEST> m_RecvQ; // queue of overlapped recv requests
QueueOf<WSAIOREQUEST> m_SendQ; // queue of overlapped send requests
WSAOVERLAPPED m_SendOverlapped; // used only for synchronous send calls
// the following fields are used to issue Send/Recv APCs
DWORD m_RecvThreadId; DWORD m_SendThreadId; HANDLE m_hRecvThread; // thread issuing receive requests
HANDLE m_hSendThread; // thread issuing send requests
};
void WS2EmulInit() { InitializeCriticalSection(&g_WS2Emul.extcs); InitializeCriticalSection(&g_WS2Emul.intcs); }
void WS2EmulTerminate() { DeleteCriticalSection(&g_WS2Emul.extcs); DeleteCriticalSection(&g_WS2Emul.intcs); }
BOOL CWS2EmulSock::NewSock(int af,int type, int protocol) { m_sock = socket(af,type,protocol); if (m_sock != INVALID_SOCKET) { WSAAsyncSelect(m_sock, g_WS2Emul.hWnd, WM_WSA_READWRITE+m_myIndex, FD_READ|FD_WRITE); } return m_sock != INVALID_SOCKET; }
int CWS2EmulSock::RecvFrom( LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, struct sockaddr FAR * lpFrom, LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) { WSAIOREQUEST ioreq; int error = 0; if (lpCompletionRoutine) { DWORD thid = GetCurrentThreadId(); HANDLE hCurProcess; if (thid != m_RecvThreadId) { // need to create a thread handle for QueueUserAPC
// typically this will only happen once
if (!m_RecvQ.IsEmpty()) { // dont allow simultaneous recv access by more than one thread
error = WSAEINVAL; } else { if (m_hRecvThread) CloseHandle(m_hRecvThread); m_hRecvThread = NULL;
hCurProcess = GetCurrentProcess(); m_RecvThreadId = thid; if (!DuplicateHandle(
hCurProcess, // handle to process with handle to duplicate
GetCurrentThread(), // handle to duplicate
hCurProcess, // handle to process to duplicate to
&m_hRecvThread, // pointer to duplicate handle
0, // access for duplicate handle
FALSE, // handle inheritance flag
DUPLICATE_SAME_ACCESS // optional actions
)) { error = WSAEINVAL; m_RecvThreadId = 0; } } } } if (error || dwBufferCount != 1 || !lpOverlapped) { WSASetLastError(WSAENOBUFS); return SOCKET_ERROR; } ioreq.pOverlapped = lpOverlapped; if (lpOverlapped) // cache away ptr to completion routine
lpOverlapped->Pointer = lpCompletionRoutine; ioreq.pRecvFromAddr = lpFrom; ioreq.pRecvFromLen = lpFromlen; ioreq.wsabuf[0] = lpBuffers[0]; ioreq.dwBufCount = dwBufferCount;
m_RecvQ.Put(ioreq); //LOG((LOGMSG_RECVFROM1,(UINT)lpOverlapped));
// signal WS1 send/recv thread
PostMessage(g_WS2Emul.hWnd, WM_WSA_READWRITE+m_myIndex, m_sock, FD_READ); WSASetLastError(ERROR_IO_PENDING); return SOCKET_ERROR; }
int CWS2EmulSock::SendTo( LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, const struct sockaddr FAR * lpTo, int iTolen, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) { WSAIOREQUEST ioreq; int error = 0;
if (lpCompletionRoutine) { DWORD thid = GetCurrentThreadId(); HANDLE hCurProcess; if (thid != m_SendThreadId) { // need to create a thread handle for QueueUserAPC
if (!m_SendQ.IsEmpty()) { // dont allow simultaneous send access by more than one thread
error = WSAEINVAL; } else { if (m_hSendThread) CloseHandle(m_hSendThread); m_hSendThread = NULL;
hCurProcess = GetCurrentProcess(); m_SendThreadId = thid; if (!DuplicateHandle(
hCurProcess, // handle to process with handle to duplicate
GetCurrentThread(), // handle to duplicate
hCurProcess, // handle to process to duplicate to
&m_hSendThread, // pointer to duplicate handle
0, // access for duplicate handle
FALSE, // handle inheritance flag
DUPLICATE_SAME_ACCESS // optional actions
)) { error = WSAEINVAL; m_SendThreadId = 0; } } } } if (error || dwBufferCount != 1 || iTolen != sizeof(struct sockaddr) || !lpOverlapped) { WSASetLastError(WSAEINVAL); return SOCKET_ERROR; } ioreq.pOverlapped = lpOverlapped; if (lpOverlapped) // cache away ptr to completion routine
lpOverlapped->Pointer = lpCompletionRoutine; ioreq.SendToAddr = *lpTo; ioreq.wsabuf[0] = lpBuffers[0]; ioreq.dwBufCount = dwBufferCount;
m_SendQ.Put(ioreq); // signal WS1 send/recv thread
PostMessage(g_WS2Emul.hWnd, WM_WSA_READWRITE+m_myIndex, m_sock, FD_WRITE); WSASetLastError(ERROR_IO_PENDING); return SOCKET_ERROR; }
/*
Close - close the socket and cancel pending i/o */ int CWS2EmulSock::Close() { WSAIOREQUEST ioreq; int status; status = closesocket(m_sock); m_sock = NULL; while (m_SendQ.Get(&ioreq)) { // complete the request
ioreq.pOverlapped->Internal = WSA_OPERATION_ABORTED; // if there is a callback routine, its address is cached in pOverlapped->Offset
if (ioreq.pOverlapped->Pointer) { QueueUserAPC(SendRecvCompleteAPC,m_hSendThread,(DWORD_PTR)ioreq.pOverlapped); } else { SetEvent((HANDLE)ioreq.pOverlapped->hEvent); } } while (m_RecvQ.Get(&ioreq)) { // complete the request
ioreq.pOverlapped->Internal = WSA_OPERATION_ABORTED; if (ioreq.pOverlapped->Pointer) { QueueUserAPC(SendRecvCompleteAPC,m_hRecvThread,(DWORD_PTR)ioreq.pOverlapped); } else { SetEvent((HANDLE)ioreq.pOverlapped->hEvent); } } if (m_hSendThread) { CloseHandle(m_hSendThread); m_hSendThread = NULL; } if (m_hRecvThread) { CloseHandle(m_hRecvThread); m_hRecvThread = NULL; } return 0; }
LRESULT CWS2EmulSock::HandleMessage(WPARAM sock, LPARAM lParam) { WORD wEvent= (WSAGETSELECTEVENT(lParam)); WORD wError= (WSAGETSELECTERROR(lParam)); int iRet; int status; WSAIOREQUEST ioreq; HANDLE hThread; // make sure the message is intended for this socket
if ((SOCKET) sock != m_sock) return 0;
// get the first RecvFrom or SendTo request, but leave it on the queue
// in case the request blocks
//if (wEvent == FD_READ)
// LOG((LOGMSG_ONREAD1, (UINT)sock));
if (wEvent == FD_READ && m_RecvQ.Peek(&ioreq)) { //LOG((LOGMSG_ONREAD2, (UINT)ioreq.pOverlapped));
iRet = recvfrom(m_sock, ioreq.wsabuf[0].buf, ioreq.wsabuf[0].len, 0, ioreq.pRecvFromAddr, ioreq.pRecvFromLen); } else if (wEvent == FD_WRITE && m_SendQ.Peek(&ioreq)) { iRet = sendto(m_sock, ioreq.wsabuf[0].buf, ioreq.wsabuf[0].len, 0, &ioreq.SendToAddr, sizeof(ioreq.SendToAddr)); } else // some other event or no queued request
return 1;
// complete send and recv
if(iRet >=0) { status = 0; ioreq.pOverlapped->InternalHigh = iRet; // numBytesReceived
} else { // error (or "would block") case falls out here
ASSERT(iRet == SOCKET_ERROR); status = WSAGetLastError(); ioreq.pOverlapped->InternalHigh = 0; } // check the error - it could be blocking
if (status != WSAEWOULDBLOCK) { ioreq.pOverlapped->Internal = status; // pull request off the queue
if (wEvent == FD_READ) { m_RecvQ.Get(NULL); hThread = m_hRecvThread; //LOG((LOGMSG_ONREADDONE1, (UINT)ioreq.pOverlapped));
} else // wEvent == FD_WRITE
{ m_SendQ.Get(NULL); hThread = m_hSendThread; } // complete the request
if (ioreq.pOverlapped->Pointer) { // if there is a callback routine, its address is cached in pOverlapped->Offset
QueueUserAPC(SendRecvCompleteAPC,hThread, (DWORD_PTR)ioreq.pOverlapped); } else { SetEvent((HANDLE)ioreq.pOverlapped->hEvent); } } return 1; }
void __stdcall SendRecvCompleteAPC(ULONG_PTR dw) { WSAOVERLAPPED *pOverlapped = (WSAOVERLAPPED *)dw; LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine = (LPWSAOVERLAPPED_COMPLETION_ROUTINE) pOverlapped->Pointer;
//LOG((LOGMSG_RECVFROM2,(UINT)pOverlapped));
lpCompletionRoutine((DWORD)pOverlapped->Internal, (DWORD)pOverlapped->InternalHigh, pOverlapped, 0); } inline CWS2EmulSock * EmulSockFromSocket(SOCKET s) { return ( ((UINT) s < MAX_EMUL_SOCKETS) ? g_WS2Emul.m_pEmulSocks[s] : NULL); }
inline SOCKET MapSocket(SOCKET s) { return (((UINT) s < MAX_EMUL_SOCKETS) && g_WS2Emul.m_pEmulSocks[s] ? g_WS2Emul.m_pEmulSocks[s]->GetSocket() : INVALID_SOCKET); }
SOCKET PASCAL WS2EmulSocket( int af, int type, int protocol, LPWSAPROTOCOL_INFO lpProtocolInfo, GROUP, DWORD) { SOCKET s = INVALID_SOCKET; int i; CWS2EmulSock *pESock; if (g_WS2Emul.numSockets == MAX_EMUL_SOCKETS) return s;
EnterCriticalSection(&g_WS2Emul.extcs); if (af == FROM_PROTOCOL_INFO) af = lpProtocolInfo->iAddressFamily; if (type == FROM_PROTOCOL_INFO) type = lpProtocolInfo->iSocketType; if (protocol == FROM_PROTOCOL_INFO) protocol = lpProtocolInfo->iProtocol;
for (i=0;i<MAX_EMUL_SOCKETS;i++) { if (g_WS2Emul.m_pEmulSocks[i] == NULL) { pESock = new CWS2EmulSock(i); if (pESock) { if (++g_WS2Emul.numSockets == 1) { StartWS1MsgThread(); } if (pESock->NewSock(af,type,protocol)) { g_WS2Emul.m_pEmulSocks[i] = pESock; s = (SOCKET)i; } else { delete pESock; if (--g_WS2Emul.numSockets == 0) { StopWS1MsgThread(); } } } break; } } LeaveCriticalSection(&g_WS2Emul.extcs); return s; }
int PASCAL WS2EmulRecvFrom( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, struct sockaddr FAR * lpFrom, LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine ) { CWS2EmulSock *pESock; int iret; EnterCriticalSection(&g_WS2Emul.extcs);
if (pESock = EmulSockFromSocket(s)) { EnterCriticalSection(&g_WS2Emul.intcs); iret = pESock->RecvFrom(lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpFrom, lpFromlen, lpOverlapped, lpCompletionRoutine); LeaveCriticalSection(&g_WS2Emul.intcs); } else { WSASetLastError(WSAENOTSOCK); iret = SOCKET_ERROR; } LeaveCriticalSection(&g_WS2Emul.extcs);
return iret; } /*----------------------------------------------------------------------------
* Function: WS2EmulSendCB * Description: private Winsock callback * This is only called if WS2EmulSendTo is called in synchronous mode * (i.e.) lpOverlapped == NULL. In that case, the sync call is converted to async * using a private Overlapped struct, and the WS2EmulSendTo api blocks until * this routine sets the hEvent field to TRUE; * Input: * * Return: None *--------------------------------------------------------------------------*/ void CALLBACK WS2EmulSendCB (DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags) { lpOverlapped->Internal = dwError; lpOverlapped->InternalHigh = cbTransferred; lpOverlapped->hEvent = (WSAEVENT) TRUE; }
int PASCAL WS2EmulSendTo( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, const struct sockaddr FAR * lpTo, int iTolen, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine ) { CWS2EmulSock *pESock; int iret; BOOL fSync = FALSE; EnterCriticalSection(&g_WS2Emul.extcs); if (pESock = EmulSockFromSocket(s)) { if (!lpOverlapped) { // synchronous call - we use our own overlapped struct to issue the
// send request.
lpOverlapped = pESock->GetSendOverlapped(); lpOverlapped->hEvent = (WSAEVENT) FALSE; // will be set to TRUE in Callback
lpCompletionRoutine = &WS2EmulSendCB; fSync = TRUE; }
EnterCriticalSection(&g_WS2Emul.intcs); iret = pESock->SendTo(lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags, lpTo, iTolen, lpOverlapped, lpCompletionRoutine); LeaveCriticalSection(&g_WS2Emul.intcs);
if (fSync) { DWORD dwError; if (iret == SOCKET_ERROR) { dwError = WSAGetLastError(); if (dwError != WSA_IO_PENDING) { // there was an error so there will not be a callback
lpOverlapped->hEvent = (WSAEVENT) TRUE; lpOverlapped->Internal = dwError; } } // wait for the call to complete
// WS2EmulSendCB sets the hEvent field to TRUE and sets the Internal field to
// the completion status
while (!lpOverlapped->hEvent) { dwError =SleepEx(5000,TRUE); // WARNING: sleeping inside a Critical Section
ASSERT(dwError == WAIT_IO_COMPLETION); } WSASetLastError((int)lpOverlapped->Internal); if (lpNumberOfBytesSent) *lpNumberOfBytesSent = (DWORD)lpOverlapped->InternalHigh; iret = lpOverlapped->Internal ? SOCKET_ERROR : 0; }
} else { WSASetLastError(WSAENOTSOCK); iret = SOCKET_ERROR; } LeaveCriticalSection(&g_WS2Emul.extcs); return iret; }
int PASCAL WS2EmulCloseSocket(SOCKET s) { CWS2EmulSock *pESock; int iret; EnterCriticalSection(&g_WS2Emul.extcs);
if (pESock = EmulSockFromSocket(s)) { // shut out access to this socket
// by the MsgThread
EnterCriticalSection(&g_WS2Emul.intcs); g_WS2Emul.m_pEmulSocks[s] = NULL; pESock->Close(); delete pESock; LeaveCriticalSection(&g_WS2Emul.intcs); if (--g_WS2Emul.numSockets == 0) { // cant stop the thread with while holding intcs
StopWS1MsgThread(); }
iret = 0; } else { WSASetLastError(WSAENOTSOCK); iret = SOCKET_ERROR; } LeaveCriticalSection(&g_WS2Emul.extcs); return iret; }
int PASCAL WS2EmulSetSockOpt( SOCKET s, int level,int optname,const char FAR * optval,int optlen) { return setsockopt(MapSocket(s), level, optname, optval, optlen); }
int PASCAL WS2EmulBind( SOCKET s, const struct sockaddr FAR * name, int namelen) { return bind(MapSocket(s), name, namelen); }
int PASCAL WS2EmulGetSockName( SOCKET s, struct sockaddr * name, int * namelen ) { return getsockname(MapSocket(s), name, namelen); }
int PASCAL WS2EmulHtonl( SOCKET s, u_long hostlong, u_long FAR * lpnetlong ) { *lpnetlong = htonl(hostlong); return 0; } int PASCAL WS2EmulHtons( SOCKET s, u_short hostshort, u_short FAR * lpnetshort ) { *lpnetshort = htons(hostshort); return 0; }
int PASCAL WS2EmulNtohl( SOCKET s, u_long netlong, u_long FAR * lphostlong ) { *lphostlong = ntohl(netlong); return 0; }
int PASCAL WS2EmulNtohs( SOCKET s, u_short netshort, u_short FAR * lphostshort ) { *lphostshort = ntohs(netshort); return 0; }
int PASCAL WS2EmulGetHostName(char *name, int namelen) { return gethostname(name, namelen); }
struct hostent FAR * PASCAL WS2EmulGetHostByName(const char * name) { return gethostbyname(name); }
SOCKET PASCAL WS2EmulJoinLeaf( SOCKET s, const struct sockaddr FAR * name, int namelen, LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS, DWORD dwFlags ) { ASSERT(0); return (-1); }
int PASCAL WS2EmulIoctl(SOCKET s, DWORD dwIoControlCode, LPVOID lpvInBuffer, DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer, LPDWORD lpcbBytesReturned, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE )
{ ASSERT(0); return -1; }
BOOL StartWS1MsgThread() { DWORD threadId; DWORD dwStatus; ASSERT(g_WS2Emul.hMsgThread == 0); g_WS2Emul.hAckEvent = CreateEvent(NULL,FALSE,FALSE,NULL); g_WS2Emul.hMsgThread = CreateThread(NULL,0, WS1MsgThread, 0, 0, &threadId); dwStatus = WaitForSingleObject(g_WS2Emul.hAckEvent,INFINITE); return dwStatus == WAIT_OBJECT_0; }
BOOL StopWS1MsgThread() { if (g_WS2Emul.hMsgThread && g_WS2Emul.hWnd) { PostMessage(g_WS2Emul.hWnd, WM_CLOSE, 0, 0); WaitForSingleObject(g_WS2Emul.hMsgThread,INFINITE); CloseHandle(g_WS2Emul.hMsgThread); CloseHandle(g_WS2Emul.hAckEvent); g_WS2Emul.hMsgThread = NULL; g_WS2Emul.hAckEvent = NULL; } return TRUE; }
LRESULT CALLBACK WS1WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { int i; CWS2EmulSock *pESock; EnterCriticalSection(&g_WS2Emul.intcs); if (pESock = EmulSockFromSocket(msg - WM_WSA_READWRITE)) { LRESULT l = pESock->HandleMessage(wParam, lParam); LeaveCriticalSection(&g_WS2Emul.intcs); return l; } LeaveCriticalSection(&g_WS2Emul.intcs); if (msg == WM_DESTROY) PostQuitMessage(0);
return (DefWindowProc(hWnd, msg, wParam, lParam)); }
DWORD WINAPI WS1MsgThread (LPVOID ) {
HRESULT hr; BOOL fChange = FALSE; MSG msg;
// Register hidden window class:
WNDCLASS wcHidden = { 0L, WS1WindowProc, 0, 0, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, "WS1EmulWindowClass" }; if (RegisterClass(&wcHidden)) { // Create hidden window
// Create a hidden window for event processing:
g_WS2Emul.hWnd = ::CreateWindow( "WS1EmulWindowClass", "", WS_POPUP, // not visible!
0, 0, 0, 0, NULL, NULL, GetModuleHandle(NULL), NULL); }
if(!g_WS2Emul.hWnd) { hr = GetLastError(); goto CLEANUPEXIT; } //SetThreadPriority(m_hRecvThread, THREAD_PRIORITY_ABOVE_NORMAL);
// This function is guaranteed to create a queue on this thread
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
// notify thread creator that we're ready to recv messages
SetEvent(g_WS2Emul.hAckEvent);
// Wait for control messages or Winsock messages directed to
// our hidden window
while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } g_WS2Emul.hWnd = NULL; hr = S_OK;
CLEANUPEXIT: UnregisterClass("WS1EmulWindowClass",GetModuleHandle(NULL)); SetEvent(g_WS2Emul.hAckEvent); return hr; }
|