//----------------------------------------------------------------- // Copyright (C) Microsoft Corporation, 1997 - 1999 // // server.c // // Thread code to manage communication between RPC client and // server for the HTTP RPC proxy. // // History: // // Edward Reus 00-00-97 Initial version. //----------------------------------------------------------------- #include #include #include #include #include #include #include "ecblist.h" #include "filter.h" #include "server.h" //----------------------------------------------------------------- // Globals: //----------------------------------------------------------------- HANDLE g_hServerThread = 0; DWORD g_dwThreadId = 0; extern SERVER_INFO *g_pServerInfo; //----------------------------------------------------------------- // CleanupECB() // // When the client or server side connection is closed, they call // this function to handle cleanup of the ECB. The active ECBs are // reference counted, since its used by both the server and client // side threads. When the count goes to zero, the ECB can be // destroyed by the IIS (HSE_REQ_DONE_WITH_SESSION). // //----------------------------------------------------------------- DWORD CleanupECB( EXTENSION_CONTROL_BLOCK *pECB ) { DWORD dwStatus = 0; if (DecrementECBRefCount(g_pServerInfo->pActiveECBList,pECB)) { // // The ECB reference count has reached zero, we can get // rid of it: // #ifdef DBG_ECBREF DbgPrint("CleanupECB(): Destroy ECB: 0x%x\n",pECB); #endif if (!pECB->ServerSupportFunction( pECB->ConnID, HSE_REQ_DONE_WITH_SESSION, NULL, NULL, NULL)) { dwStatus = GetLastError(); #ifdef DBG_ERROR DbgPrint("CleanupECB(): HSE_REQ_DONE_WITH_SESSION failed: %d\n",dwStatus); #endif } } return dwStatus; } #ifdef DBG //----------------------------------------------------------------- // CheckForOldECBs() // //----------------------------------------------------------------- void CheckForOldECBs() { int i; DWORD dwStatus; DWORD dwAgeMsec; DWORD dwTickCount = GetTickCount(); ACTIVE_ECB_LIST *pECBList = g_pServerInfo->pActiveECBList; ECB_ENTRY *pECBEntry; LIST_ENTRY *pEntry; LIST_ENTRY *pHead = NULL; LIST_ENTRY *pOldEntries = NULL; EXTENSION_CONTROL_BLOCK *pECB; // // Check for ECBs that are inactive (one side of connection // closed) for more that 10 minutes. // #define OLD_AGE_LIMIT 1000*60*10 dwStatus = RtlEnterCriticalSection(&pECBList->cs); ASSERT(dwStatus == 0); for (i=0; iHashTable[i]); pEntry = pHead->Flink; while (pEntry != pHead) { pECBEntry = CONTAINING_RECORD(pEntry,ECB_ENTRY,ListEntry); if (pECBEntry->dwTickCount) { // Ok, this is one where one of the server/client side // has closed the connection: // // dwAgeMsec is the age of the current ECB is msec. // if (pECBEntry->dwTickCount > dwTickCount) { // Rollover case (every ~49 days): dwAgeMsec = (0xFFFFFFFF - pECBEntry->dwTickCount) + dwTickCount; } else { dwAgeMsec = dwTickCount - pECBEntry->dwTickCount; } // ASSERT( dwAgeMsec <= OLD_AGE_LIMIT); if (dwAgeMsec > OLD_AGE_LIMIT) { RemoveEntryList(pEntry); pEntry->Blink = pOldEntries; pOldEntries = pEntry; } } pEntry = pEntry->Flink; } } dwStatus = RtlLeaveCriticalSection(&pECBList->cs); ASSERT(dwStatus == 0); while (pOldEntries) { pECBEntry = CONTAINING_RECORD(pOldEntries,ECB_ENTRY,ListEntry); pECB = pECBEntry->pECB; pOldEntries = pOldEntries->Blink; MemFree(pECBEntry); #ifdef DBG_ECBREF DbgPrint("CheckForOldECBs(): Age out pECB: 0x%x\n",pECB); #endif if (!pECB->ServerSupportFunction( pECB->ConnID, HSE_REQ_DONE_WITH_SESSION, NULL, NULL, NULL)) { #ifdef DBG_ERROR DbgPrint("CheckForOldECBs(): HSE_REQ_DONE_WITH_SESSION failed: %d\n", GetLastError()); #endif } } } #endif //----------------------------------------------------------------- // SendToClient() // // Forward data received from the server back to the client. //----------------------------------------------------------------- BOOL SendToClient( SERVER_INFO *pServerInfo, SERVER_OVERLAPPED *pOverlapped, DWORD dwReceiveSize, DWORD *pdwStatus ) { DWORD dwSize; DWORD dwFlags = (HSE_IO_SYNC | HSE_IO_NODELAY); UCHAR *pBuffer = pOverlapped->arBuffer; EXTENSION_CONTROL_BLOCK *pECB = pOverlapped->pECB; *pdwStatus = 0; // // Forward the data to the client: // dwSize = dwReceiveSize; while (dwReceiveSize) { if (!pECB->WriteClient(pECB->ConnID,pBuffer,&dwSize,dwFlags)) { *pdwStatus = GetLastError(); #ifdef DBG_ERROR DbgPrint("SendToClient(): WriteClient() failed: %d\n",*pdwStatus); #endif return FALSE; } dwReceiveSize -= dwSize; if (dwReceiveSize) { pBuffer += dwSize; } } return TRUE; } //----------------------------------------------------------------- // SubmitNewRead() // // Submit a read request on the socket connected to the // RPC server process. //----------------------------------------------------------------- BOOL SubmitNewRead( SERVER_INFO *pServerInfo, SERVER_OVERLAPPED *pOverlapped, DWORD *pdwStatus ) { DWORD dwBytesRead = 0; SERVER_CONNECTION *pConn = pOverlapped->pConn; *pdwStatus = 0; pOverlapped->Internal = 0; pOverlapped->InternalHigh = 0; pOverlapped->Offset = 0; pOverlapped->OffsetHigh = 0; pOverlapped->hEvent = 0; SetLastError(ERROR_SUCCESS); if (!ReadFile( (HANDLE)pConn->Socket, pOverlapped->arBuffer, READ_BUFFER_SIZE, &dwBytesRead, (OVERLAPPED*)pOverlapped ) ) { *pdwStatus = GetLastError(); if ( (*pdwStatus != ERROR_IO_PENDING) && (*pdwStatus != ERROR_SUCCESS) ) { #ifdef DBG_ERROR DbgPrint("SubmitNewRead(): ReadFile() Socket: %d failed: %d\n",pConn->Socket,*pdwStatus); #endif return FALSE; } } return TRUE; } //----------------------------------------------------------------- // ForwardAndSubmitNewRead() // // Forward data to the client, the submit a new read on the // server. //----------------------------------------------------------------- BOOL ForwardAndSubmitNewRead( SERVER_INFO *pServerInfo, SERVER_OVERLAPPED *pOverlapped, DWORD dwReceiveSize, DWORD *pdwStatus ) { DWORD dwBytesRead = 0; SERVER_CONNECTION *pConn = pOverlapped->pConn; *pdwStatus = 0; // // Forward the data to the client: // if (!SendToClient(pServerInfo,pOverlapped,dwReceiveSize,pdwStatus)) { return FALSE; } // // Submit another read on the socket: // if (!SubmitNewRead(pServerInfo,pOverlapped,pdwStatus)) { return FALSE; } return TRUE; } #if _MSC_FULL_VER >= 13008827 #pragma warning(push) #pragma warning(disable:4715) // Not all control paths return (due to infinite loop) #endif //----------------------------------------------------------------- // ServerReceiveThreadProc() // // This is the receive thread for the server. It monitors the // all the sockets to RPC servers that are currently connected // to the RPC proxy. When it gets incomming data from an RPC // server socket, it forwards the data to the client, then // submits a new read on the socket that data came in on. // // Once its started, it never stops... //----------------------------------------------------------------- DWORD WINAPI ServerReceiveThreadProc( void *pvServerInfo ) { int iRet; BOOL fWaitAll = FALSE; DWORD dwStatus; DWORD dwTimeout = TIMEOUT_MSEC; DWORD dwWhichEvent; ULONG_PTR dwKey; DWORD dwNumBytes; SERVER_OVERLAPPED *pOverlapped; SERVER_INFO *pServerInfo = (SERVER_INFO*)pvServerInfo; EXTENSION_CONTROL_BLOCK *pECB; DWORD dwTemp; DWORD dwSize; DWORD dwFlags; while (TRUE) { SetLastError(0); dwKey = 0; dwNumBytes = 0; pOverlapped = NULL; if (!GetQueuedCompletionStatus(pServerInfo->hIoCP,&dwNumBytes,&dwKey,(OVERLAPPED**)&pOverlapped,dwTimeout)) { dwStatus = GetLastError(); if (dwStatus == WAIT_TIMEOUT) { // Our reads are still posted, go around again: #ifdef DBG CheckForOldECBs(); #endif continue; } else if (dwStatus == ERROR_OPERATION_ABORTED) { // The posted read on the server was aborted (why?). Try // to resubmit the read... if ( (pOverlapped) && (!SubmitNewRead(pServerInfo,pOverlapped,&dwStatus)) ) { pECB = pOverlapped->pECB; CloseServerConnection(pOverlapped->pConn); FreeOverlapped(pOverlapped); CleanupECB(pECB); #ifdef DBG_ERROR DbgPrint("ServerReceiveThreadProc(): Aborted re-submit failed: %d\n",dwStatus); #endif } continue; } else if (dwStatus == ERROR_NETNAME_DELETED) { // The server connection has been closed: if (pOverlapped) { pECB = pOverlapped->pECB; CloseServerConnection(pOverlapped->pConn); FreeOverlapped(pOverlapped); CleanupECB(pECB); #ifdef DBG_ERROR DbgPrint("ServerReceiveThreadProc(): Socket(%d): ERROR_NETNAME_DELETED\n",dwKey,dwStatus); #endif } continue; } else { #ifdef DBG_ERROR DbgPrint("ServerReceiveThreadProc(): GetQueuedCompletionStatus() failed: %d\n",dwStatus); #endif if (pOverlapped) { pECB = pOverlapped->pECB; CloseServerConnection(pOverlapped->pConn); FreeOverlapped(pOverlapped); CleanupECB(pECB); } continue; } } // Check for incomming data from a server: if (pOverlapped) { pECB = pOverlapped->pECB; if (dwNumBytes) { // // data from server arrived... // if (!ForwardAndSubmitNewRead(pServerInfo,pOverlapped,dwNumBytes,&dwStatus)) { CloseServerConnection(pOverlapped->pConn); FreeOverlapped(pOverlapped); CleanupECB(pECB); #ifdef DBG_ERROR DbgPrint("ServerReceiveThreadProc(): ForwardAndSubmitNewRead(): failed: %d\n",dwStatus); #endif } } else { // Receive, but zero bytes, so connection was gracefully closed... CloseServerConnection(pOverlapped->pConn); FreeOverlapped(pOverlapped); CleanupECB(pECB); } } else { // The filter called EndOfSession() and posted this message to // us to close up... dwKey is the socket in question: #ifdef DBG_ERROR DbgPrint("ServerReceiveProc(): EndOfSession(): pOverlapped == NULL\n"); #endif iRet = closesocket( (SOCKET)dwKey ); #ifdef DBG_COUNTS if (iRet == SOCKET_ERROR) { DbgPrint("[6] closesocket(%d) failed: %d\n",dwKey,WSAGetLastError()); } else { int iCount = InterlockedDecrement(&g_iSocketCount); DbgPrint("[6] closesocket(%d): Count: %d -> %d\n", dwKey, 1+iCount, iCount ); } #endif } } return 0; } #if _MSC_FULL_VER >= 13008827 #pragma warning(pop) #endif //----------------------------------------------------------------- // CheckStartReceiveThread() // // Check to see of the server side receive thread is running, if // it isn't started yet, then start it. //----------------------------------------------------------------- BOOL CheckStartReceiveThread( SERVER_INFO *pServerInfo, DWORD *pdwStatus ) { *pdwStatus = 0; if (!g_hServerThread) { g_hServerThread = CreateThread( NULL, 0, ServerReceiveThreadProc, (void*)pServerInfo, 0, &g_dwThreadId ); if (!g_hServerThread) { *pdwStatus = GetLastError(); #ifdef DBG_ERROR DbgPrint("CheckStartServerThread(): CreateThread() failed: 0x%x\n",pdwStatus); #endif return FALSE; } } return TRUE; } //----------------------------------------------------------------- // AsyncClientReadComplete() // // This function is called when data (a call) comes in from an // RPC client. It then forwards the data to the RPC server via // the function SendToServer(). //----------------------------------------------------------------- void WINAPI AsyncClientReadComplete( IN EXTENSION_CONTROL_BLOCK *pECB, IN void *pvOverlapped, IN DWORD dwBytes, IN DWORD dwStatus ) { int iRet; DWORD dwSize; DWORD dwFlags; DWORD dwLocalStatus; SERVER_CONNECTION *pConn; SERVER_OVERLAPPED *pOverlapped = (SERVER_OVERLAPPED*)pvOverlapped; SOCKET Socket; ASSERT(pECB == pOverlapped->pECB); pConn = pOverlapped->pConn; if (dwStatus == ERROR_SUCCESS) { if (dwBytes) { if (pOverlapped->fFirstRead) { pOverlapped->fFirstRead = FALSE; if ( (dwBytes < 72) && (pECB->lpbData) ) { #ifdef DBG_ERROR DbgPrint("AsyncClientReadComplete(): Bind missing bytes: %d\n",72-dwBytes); #endif dwStatus = SendToServer(pOverlapped->pConn,pECB->lpbData,72-dwBytes); } } // Got data from the client, forward it to the server: dwStatus = SendToServer(pOverlapped->pConn,pOverlapped->arBuffer,dwBytes); // Submit a new async read on the client: if (dwStatus == ERROR_SUCCESS) { dwSize = sizeof(pOverlapped->arBuffer); dwFlags = HSE_IO_ASYNC; if (!pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_ASYNC_READ_CLIENT, pOverlapped->arBuffer, &dwSize, &dwFlags)) { dwStatus = GetLastError(); } } } } if ((dwBytes == 0) || (dwStatus != ERROR_SUCCESS)) { // // Connection to client was closed (dwBytes == 0) or error. So // shutdown socket to server: // if (pOverlapped) { CloseServerConnection(pOverlapped->pConn); pOverlapped->pECB = NULL; FreeOverlapped(pOverlapped); CleanupECB(pECB); } #ifdef DBG_ERROR if ((dwStatus != ERROR_SUCCESS) && (dwStatus != ERROR_NETNAME_DELETED)) { DbgPrint("AsyncClientReadComplete(): Erorr: %d Close server socket: %d\n",dwStatus,pConn->Socket); } #endif } } //----------------------------------------------------------------- // StartAsyncClientRead() // // Called by the RpcIsapi half of the code to start an async read // on the client connection. //----------------------------------------------------------------- BOOL StartAsyncClientRead( EXTENSION_CONTROL_BLOCK *pECB, SERVER_CONNECTION *pConn, DWORD *pdwStatus ) { SERVER_OVERLAPPED *pOverlapped; pOverlapped = AllocOverlapped(); if (!pOverlapped) { *pdwStatus = RPC_S_OUT_OF_MEMORY; return FALSE; } *pdwStatus = 0; pOverlapped->pECB = pECB; // The SERVER_CONNECTION (pConn) is in two separate SERVER_OVERLAPPED // structures, one for client async reads and one for server async // reads, as well as in the filter context. So it is reference counted. pOverlapped->pConn = pConn; AddRefConnection(pConn); pOverlapped->fFirstRead = TRUE; if (!pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_IO_COMPLETION, AsyncClientReadComplete, NULL, (void*)pOverlapped)) { *pdwStatus = GetLastError(); FreeOverlapped(pOverlapped); #ifdef DBG_ERROR DbgPrint("StartAsyncClientRead(): HSE_REQ_IO_COMPLETION Failed: %d\n",*pdwStatus); #endif return FALSE; } pOverlapped->dwBytes = sizeof(pOverlapped->arBuffer); pOverlapped->dwFlags = HSE_IO_ASYNC; if (!pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_ASYNC_READ_CLIENT, pOverlapped->arBuffer, &pOverlapped->dwBytes, &pOverlapped->dwFlags)) { *pdwStatus = GetLastError(); FreeOverlapped(pOverlapped); #ifdef DBG_ERROR DbgPrint("StartAsyncClientRead(): HSE_REQ_ASYNC_READ_CLIENT Failed: %d\n",*pdwStatus); #endif return FALSE; } return TRUE; }