#include #include // 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 m_RecvQ; // queue of overlapped recv requests QueueOf 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;iNewSock(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; }