/******************************************************************************\ * This is a part of the Microsoft Source Code Samples. * Copyright 1995 - 1997 Microsoft Corporation. * All rights reserved. * This source code is only intended as a supplement to * Microsoft Development Tools and/or WinHelp documentation. * See these sources for detailed information regarding the * Microsoft samples programs. \******************************************************************************/ /*++ Copyright (c) 1997 Microsoft Corporation Module Name: SrvStoC.c Abstract: This file implements the server-to-client flow of data for remote server. The data is the output of the child program intermingled with client input. Author: Dave Hart 30 May 1997 Environment: Console App. User mode. Revision History: --*/ #include #include #include #include #include #include "Remote.h" #include "Server.h" VOID FASTCALL StartServerToClientFlow( VOID ) { PREMOTE_CLIENT pClient; // // Start read operations against the temp file for // all active clients that aren't currently doing // read temp/write client operations and that are // fully connected. // for (pClient = (PREMOTE_CLIENT) ClientListHead.Flink; pClient != (PREMOTE_CLIENT) &ClientListHead; pClient = (PREMOTE_CLIENT) pClient->Links.Flink ) { if (! pClient->cbWrite) { StartReadTempFile( pClient ); } } } VOID FASTCALL StartReadTempFile( PREMOTE_CLIENT pClient ) { // // pClient->cbWrite is used dually. WriteSessionOutputCompleted // uses it when 0 bytes are written to know how much to ask // to write when it resubmits the request. We use it to // indicate whether a read temp/write session chain of I/Os // is currently active for this client. // if (pClient->cbWrite) { ErrorExit("StartReadTempFile entered with nonzero cbWrite."); } if (dwWriteFilePointer > pClient->dwFilePos) { pClient->cbWrite = min(BUFFSIZE, dwWriteFilePointer - pClient->dwFilePos); pClient->WriteOverlapped.OffsetHigh = 0; pClient->WriteOverlapped.Offset = pClient->dwFilePos; if ( ! ReadFileEx( pClient->rSaveFile, pClient->ReadTempBuffer, pClient->cbWrite, &pClient->WriteOverlapped, ReadTempFileCompleted )) { if (ERROR_HANDLE_EOF == GetLastError()) { pClient->cbWrite = 0; } else { TRACE(SESSION, ("ReadFileEx for temp file failed error %d, closing client.\n", GetLastError())); CloseClient(pClient); } } } } VOID WINAPI ReadTempFileCompleted( DWORD dwError, DWORD cbRead, LPOVERLAPPED lpO ) { PREMOTE_CLIENT pClient; pClient = CONTAINING_RECORD(lpO, REMOTE_CLIENT, WriteOverlapped); if (HandleSessionError(pClient, dwError)) { return; } if (cbRead != pClient->cbWrite) { TRACE(SESSION, ("Read %d from temp file asked for %d\n", cbRead, pClient->cbWrite)); } if (cbRead) { pClient->cbReadTempBuffer = cbRead; pClient->dwFilePos += cbRead; StartWriteSessionOutput(pClient); } else { // // Note that the server to client flow is halting for now // for this client. // pClient->cbWrite = 0; } } VOID FASTCALL StartWriteSessionOutput( PREMOTE_CLIENT pClient ) { DWORD cbRead; char *pch; cbRead = pClient->cbReadTempBuffer; // // We need to split commands from other text read // from the temp file and hold off on writing them // to the client until we make sure we're not the // client that submitted it. This isn't perfect // since we match on client name which can be // duplicated but it solves the problem of // duplicated input most of the time. // for (pch = pClient->ReadTempBuffer; pch < pClient->ReadTempBuffer + cbRead; pch++) { if ( ! (pClient->ServerFlags & SFLG_READINGCOMMAND) ) { if (BEGINMARK == *pch) { pClient->ServerFlags |= SFLG_READINGCOMMAND; if (pch != pClient->ReadTempBuffer && pClient->cbWriteBuffer) { // // Start a write of everything we've come across // before the start of this command, with // WriteSessionOutputCompletedWriteNext specified // so we can continue processing the remainder // of pReadTempBuffer. // pClient->cbReadTempBuffer -= (DWORD)( pch - pClient->ReadTempBuffer) + 1; cbRead = pClient->cbReadTempBuffer; #if DBG if (pClient->cbReadTempBuffer == (DWORD)-1) { ErrorExit("cbReadTempBuffer underflow."); } #endif MoveMemory(pClient->ReadTempBuffer, pch + 1, cbRead); pClient->cbWrite = pClient->cbWriteBuffer; pClient->WriteOverlapped.OffsetHigh = 0; pClient->WriteOverlapped.Offset = 0; if ( ! WriteFileEx( pClient->PipeWriteH, pClient->WriteBuffer, pClient->cbWrite, &pClient->WriteOverlapped, WriteSessionOutputCompletedWriteNext )) { CloseClient(pClient); } TRACE(SESSION, ("%p Wrote %d bytes pre-command output\n", pClient, pClient->cbWrite)); pClient->cbWriteBuffer = 0; return; } } else { if (pClient->cbWriteBuffer == BUFFSIZE) { ErrorExit("cbWriteBuffer overflow"); } pClient->WriteBuffer[ pClient->cbWriteBuffer++ ] = *pch; } } else { if (ENDMARK == *pch || pClient->cbCommandBuffer == BUFFSIZE) { pClient->ServerFlags &= ~SFLG_READINGCOMMAND; // // Preceding ENDMARK is the pClient in hex ascii of the // client that generated the command, not null terminated. // if (ENDMARK == *pch) { pClient->cbCommandBuffer -= min(pClient->cbCommandBuffer, sizeof(pClient->HexAsciiId)); } // // We hide each client's input from their output pipe // because their local remote.exe has already displayed it. // if ( pClient->cbCommandBuffer && ! (ENDMARK == *pch && ! memcmp( pch - sizeof(pClient->HexAsciiId), pClient->HexAsciiId, sizeof(pClient->HexAsciiId)))) { // // Start a write of the accumulated command with // WriteSessionOutputCompletedWriteNext specified // so we can continue processing the remainder // of pReadTempBuffer. // pClient->cbReadTempBuffer -= (DWORD)(pch - pClient->ReadTempBuffer) + 1; MoveMemory(pClient->ReadTempBuffer, pch + 1, pClient->cbReadTempBuffer); pClient->cbWrite = pClient->cbCommandBuffer; pClient->cbCommandBuffer = 0; pClient->WriteOverlapped.OffsetHigh = 0; pClient->WriteOverlapped.Offset = 0; if ( ! WriteFileEx( pClient->PipeWriteH, pClient->CommandBuffer, pClient->cbWrite, &pClient->WriteOverlapped, WriteSessionOutputCompletedWriteNext )) { CloseClient(pClient); return; } else { TRACE(SESSION, ("%p Wrote %d bytes command\n", pClient, pClient->cbWrite)); return; } } else { // // We're eating this command for this session. // pClient->cbCommandBuffer = 0; } } else { pClient->CommandBuffer[ pClient->cbCommandBuffer++ ] = *pch; } } } // // We're done with the ReadTempBuffer. // pClient->cbReadTempBuffer = 0; if (pClient->cbWriteBuffer) { pClient->cbWrite = pClient->cbWriteBuffer; pClient->WriteOverlapped.OffsetHigh = 0; pClient->WriteOverlapped.Offset = 0; if ( ! WriteFileEx( pClient->PipeWriteH, pClient->WriteBuffer, pClient->cbWrite, &pClient->WriteOverlapped, WriteSessionOutputCompletedReadNext )) { CloseClient(pClient); return; } else { TRACE(SESSION, ("%p Wrote %d bytes normal\n", pClient, pClient->cbWrite)); pClient->cbWriteBuffer = 0; } } else { // // Write buffer is empty. // pClient->cbWrite = 0; StartReadTempFile(pClient); } } BOOL FASTCALL WriteSessionOutputCompletedCommon( PREMOTE_CLIENT pClient, DWORD dwError, DWORD cbWritten, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine ) { if (HandleSessionError(pClient, dwError)) { return TRUE; } if (!pClient->cbWrite) { ErrorExit("Zero cbWrite in WriteSessionOutputCompletedCommon"); } if (!cbWritten && pClient->cbWrite) { printf("WriteSessionOutput zero bytes written of %d.\n", pClient->cbWrite); ErrorExit("WriteSessionOutputCompletedCommon failure"); return TRUE; } #if DBG if (cbWritten != pClient->cbWrite) { printf("%p cbWritten %d cbWrite %d\n", pClient, cbWritten, pClient->cbWrite); } #endif return FALSE; } VOID WINAPI WriteSessionOutputCompletedWriteNext( DWORD dwError, DWORD cbWritten, LPOVERLAPPED lpO ) { PREMOTE_CLIENT pClient; pClient = CONTAINING_RECORD(lpO, REMOTE_CLIENT, WriteOverlapped); if (WriteSessionOutputCompletedCommon( pClient, dwError, cbWritten, WriteSessionOutputCompletedWriteNext )) { return; } StartWriteSessionOutput(pClient); } VOID WINAPI WriteSessionOutputCompletedReadNext( DWORD dwError, DWORD cbWritten, LPOVERLAPPED lpO ) { PREMOTE_CLIENT pClient; pClient = CONTAINING_RECORD(lpO, REMOTE_CLIENT, WriteOverlapped); if (WriteSessionOutputCompletedCommon( pClient, dwError, cbWritten, WriteSessionOutputCompletedReadNext )) { return; } // // Start another temp file read. // pClient->cbWrite = 0; StartReadTempFile(pClient); }