/*++ Copyright (c) 1992 Microsoft Corporation Module Name: tlpipe.c Abstract: This module contains the code for the named pipe transport layer which explicitly deals with the machanics of doing named pipes. Author: Wesley Witt (wesw) 25-Nov-93 Environment: Win32 User --*/ #include #include #include #ifdef OSDEBUG4 #include "od.h" #else #include "defs.h" #include "od.h" #endif #include "xport.h" #include "tlpipe.h" #undef MessageBox // // there can only be 2 control pipes open at once because // the connect thread can only service one client at a time // #define MAX_PIPES 2 #define BREAKIN_TIMEOUT 20 // in seconds #define ControlClient(f) ci[CiClient].f #define ControlConnect(f) ci[CiConnect].f typedef struct _tagCONTROL_PIPE_INFO { HANDLE hPipe; OVERLAPPED olConnect; OVERLAPPED olRead; OVERLAPPED olWrite; BOOL fConnected; CHAR szName[MAX_PATH]; CHAR szClientId[MAX_PATH]; } CONTROL_PIPE_INFO, *LPCONTROL_PIPE_INFO; CONTROL_PIPE_INFO ci[MAX_PIPES]; CHAR ClientId[MAX_PATH]; DWORD CiClient = 0; DWORD CiConnect = 0; extern BOOL FConnected; //********************************************************************************************* // Message Box Utiluties //********************************************************************************************* typedef struct _tagMESSAGEBOX_INFO { HWND hWnd; LPSTR lpText; LPSTR lpCaption; UINT uType; DWORD dwResponse; } MESSAGEBOX_INFO, *LPMESSAGEBOX_INFO; DWORD MessageBoxThread( LPMESSAGEBOX_INFO lpMbi ) { lpMbi->dwResponse = MessageBoxA( lpMbi->hWnd, lpMbi->lpText, lpMbi->lpCaption, lpMbi->uType ); return 0; } int WINAPI MessageBox( HWND hWnd , LPSTR lpText, LPSTR lpCaption , UINT uType, DWORD dwTimeout ) { MESSAGEBOX_INFO mbi; HANDLE hThread; DWORD id; mbi.hWnd = hWnd; mbi.lpText = lpText; mbi.lpCaption = lpCaption; mbi.uType = uType; hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)MessageBoxThread, &mbi, 0, &id); if (!hThread) { return 0; } if (WaitForSingleObject( hThread, dwTimeout ) == WAIT_TIMEOUT) { TerminateThread( hThread, 0 ); return STATUS_TIMEOUT; } return mbi.dwResponse; } VOID TlControlInitialization( VOID ) /*++ Routine Description: This function initializes all control pipe structures Arguments: None. Return Value: None. --*/ { DWORD i; ZeroMemory( ci, sizeof(ci) ); for (i=0; iClientId, ControlClient(szClientId) ); ControlClient(hPipe) = INVALID_HANDLE_VALUE; timeOut = TlUtilTime() + 10; while ((ControlClient(hPipe) == INVALID_HANDLE_VALUE) && (TlUtilTime() < timeOut)) { // // wait for the server to make a pipe available... // WaitNamedPipe( ControlClient(szName), 10000 ); // // create the client pipe // ControlClient(hPipe) = CreateFile( ControlClient(szName), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL ); if (ControlClient(hPipe) == INVALID_HANDLE_VALUE && GetLastError() == ERROR_BAD_NETPATH) { return xosdBadPipeServer; } } if (ControlClient(hPipe) == INVALID_HANDLE_VALUE) { return xosdCannotConnect; } mode = PIPE_READMODE_MESSAGE | PIPE_WAIT; if ( !SetNamedPipeHandleState( ControlClient(hPipe), &mode, NULL, NULL ) ) { DEBUG_OUT1("SetNamedPipeHandleState failed, ec=%u\n", GetLastError()); CloseHandle( ControlClient(hPipe) ); return xosdBadPipeName; } ControlClient(fConnected) = TRUE; cp->Length = sizeof(CONTROLPACKET); cp->Type = CP_REQUEST_CONNECTION; cp->Response = 0; TlWriteControl( CiClient, (PUCHAR)cp, cp->Length ); TlReadControl( CiClient, (PUCHAR)cp, sizeof(buf) ); if (cp->Response) { // // we have permission to open a control pipe // try to open a client debuggeer pipe // return xosdNone; } else { // // the server did not give us permission to // open a debugger connection // ControlClient(fConnected) = FALSE; CloseHandle( ControlClient(hPipe) ); return xosdCannotConnect; } return xosdNone; } XOSD TlConnectControlPipe( VOID ) { DWORD ec; DWORD status; LPSTR NewClientId; BYTE buf[256]; LPCONTROLPACKET cp = (LPCONTROLPACKET) &buf[0]; assert( ControlConnect(fConnected) == FALSE ); ControlConnect(fConnected) = ConnectNamedPipe( ControlConnect(hPipe), &ControlConnect(olConnect) ); if (!ControlConnect(fConnected)) { ec = GetLastError(); switch( ec ) { case ERROR_PIPE_CONNECTED: goto connected; case ERROR_IO_PENDING: break; default: DEBUG_OUT1("PLPIPE: ConnectNamedPipe failed, Error %u\n", ec); //DebugPrint("ConnectNamedPipe failed, Error=%u\n", ec); return xosdCannotConnect; } status = WaitForSingleObject( ControlConnect(olConnect.hEvent), MAX_CONNECT_WAIT * 1000 ); switch ( status ) { case WAIT_OBJECT_0: goto connected; case WAIT_TIMEOUT: //DebugPrint("ConnectNamedPipe timed out\n"); return xosdCannotConnect; default: ec = GetLastError(); DEBUG_OUT2("PLPIPE: ConnectNamedPipe failed, Status %u, ec=%u\n", status, ec); //DebugPrint("ConnectNamedPipe failed, Error=%u\n", ec); return xosdCannotConnect; } } connected: ControlConnect(fConnected) = TRUE; //DebugPrint("controlpipe connected\n"); // // the control pipe is now connected so we // must now negotiate a 'real' connection // if (!TlReadControl( CiConnect, (PUCHAR)cp, sizeof(buf) )) { //DebugPrint("read #1 failed\n"); TlDisconnectControl( CiConnect ); return xosdCannotConnect; } //DebugPrint("read #1 completed\n"); if (cp->Type == CP_REQUEST_CONNECTION) { NewClientId = _strdup( cp->ClientId ); if (FConnected) { // // in this case there is already a valid debugger connection. // the connection must be disconnected and a new connection // established with the new client. // // // forward the request to the currently connected client // the current client has the right to decline the new connection // cp->Type = CP_BREAKIN_CONNECTION; if (!TlWriteControl( CiClient, (PUCHAR)cp, cp->Length )) { //DebugPrint("write failed\n"); TlDisconnectControl( CiConnect ); return xosdCannotConnect; } if (!TlReadControl( CiClient, (PUCHAR)cp, sizeof(buf) )) { //DebugPrint("read failed\n"); TlDisconnectControl( CiConnect ); return xosdCannotConnect; } if (cp->Response) { TlPipeFailure(); FConnected = FALSE; TlDisconnectTransport(); } else { TlDisconnectControl( CiConnect ); return xosdCannotConnect; } } // // save the client id // strcpy( ClientId, NewClientId ); free( NewClientId ); // // tell the new client that the connection request is granted // cp->Response = 1; if (!TlWriteControl( CiConnect, (PUCHAR)cp, cp->Length )) { //DebugPrint("write #2 failed\n"); TlDisconnectControl( CiConnect ); return xosdCannotConnect; } //DebugPrint("write #2 completed\n"); if (CiConnect == 0) { CiClient = CiConnect; CiConnect = 1; } else { CiClient = CiConnect; CiConnect = 0; } } //DebugPrint("connection established\n"); return xosdNone; } BOOL TlWriteControl( DWORD ciIdx, PUCHAR pch, DWORD cch ) { DWORD dwBytesWritten; static DWORD cblast; DWORD error; if ( !ci[ciIdx].fConnected ) { return FALSE; } DEBUG_OUT1("PLPIPE: Writing... (Count %u)\n",cch); if (WriteFile(ci[ciIdx].hPipe, pch, cch, &dwBytesWritten, &ci[ciIdx].olWrite )) { // // Write was successful and finished // FlushFileBuffers( ci[ciIdx].hPipe ); if ( dwBytesWritten != cch ) { DEBUG_OUT2("PLPIPE: Wrote %u but asked for %u\n", dwBytesWritten, cch); return FALSE; } DEBUG_OUT1( "PLPIPE: Wrote (%u)\n", dwBytesWritten); cblast = cch; return TRUE; } // // We got a failure case -- there are now two possiblities. // 1. -- we have overlapped I/O or // 2. -- we are messed up // error = GetLastError(); switch ( error ) { case ERROR_PIPE_NOT_CONNECTED: case ERROR_BROKEN_PIPE: case ERROR_NO_DATA: DEBUG_OUT1("PLPIPE: Pipe is gone (1), error %u\n", error); break; case ERROR_IO_PENDING: dwBytesWritten = 0; goto WaitWrite; default: DEBUG_OUT1("PLPIPE: WriteFile failed, error %u\n", error); break; } return FALSE; WaitWrite: if (GetOverlappedResult(ci[ciIdx].hPipe, &ci[ciIdx].olWrite, &dwBytesWritten, TRUE)) { // // Read has successfully completed // FlushFileBuffers( ci[ciIdx].hPipe ); if ( dwBytesWritten != cch ) { DEBUG_OUT2("PLPIPE: Wrote %u but asked for %u\n", dwBytesWritten, cch); return FALSE; } DEBUG_OUT1("PLPIPE: Wrote (%u)\n", dwBytesWritten); cblast = cch; return TRUE; } error = GetLastError(); switch ( error ) { case ERROR_PIPE_NOT_CONNECTED: case ERROR_BROKEN_PIPE: case ERROR_NO_DATA: DEBUG_OUT1("PLPIPE: Pipe is gone (3), error %u\n", error); break; default: DEBUG_OUT1("PLPIPE: Get Read result failed, error %u\n", error); break; } return FALSE; } DWORD TlReadControl( DWORD ciIdx, PUCHAR pch, DWORD cch ) { DWORD dwBytesRead; DWORD error; if ( !ci[ciIdx].fConnected ) { return (DWORD) -1; } ResetEvent( ci[ciIdx].olRead.hEvent ); if (ReadFile(ci[ciIdx].hPipe, pch, cch, &dwBytesRead, &ci[ciIdx].olRead)) { // // Read was successful and finished return packet size and exit. // return dwBytesRead; } // // We got a failure case -- there are now two possibities. // 1. -- we have overlapped I/O, or // 2. -- we are messed up. // error = GetLastError(); switch ( error ) { case ERROR_MORE_DATA: DEBUG_OUT(("Message is too long\n")); assert( "Message is too long" && FALSE ); break; case ERROR_IO_PENDING: DEBUG_OUT1("PLPIPE: Read pending (%u bytes)...\n", cch); dwBytesRead = 0; goto WaitRead; case ERROR_PIPE_NOT_CONNECTED: case ERROR_BROKEN_PIPE: case ERROR_NO_DATA: DEBUG_OUT1("PLPIPE: Pipe is gone (2), error %u\n", error); return (DWORD) -1; default: DEBUG_OUT1("PLPIPE: ReadFile failed, error %u\n", error); break; } return 0; WaitRead: if (GetOverlappedResult(ci[ciIdx].hPipe, &ci[ciIdx].olRead, &dwBytesRead, TRUE)) { // // Read has successfully completed // return dwBytesRead; } error = GetLastError(); DEBUG_OUT1("PLPIPE: Pipe is gone (3), error %u\n", error); return (DWORD) -1; } VOID ControlPipeFailure( VOID ) { TlDisconnectControl( CiClient ); } BOOL TlDisconnectControl( DWORD ciIdx ) { BOOL Ok = TRUE; DWORD Error; DEBUG_OUT("PipeDisconnect\n"); Ok = DisconnectNamedPipe( ci[ciIdx].hPipe ); if ( !Ok ) { Error = GetLastError(); switch( Error ) { case ERROR_PIPE_NOT_CONNECTED: Ok = TRUE; break; default: DEBUG_OUT1("DisconnectNamedPipe failed, Error %u\n", Error); break; } } if ( Ok ) { DEBUG_OUT(( "PLPIPE: Disconnected\n" )); ci[ciIdx].fConnected = FALSE; } return Ok; } DWORD ControlReaderThread( LPVOID lpv ) { int cb; int response; BYTE buf[512]; LPCONTROLPACKET cp = (LPCONTROLPACKET) &buf[0]; CHAR szMsg[256]; while (TRUE) { // // Read the next packet item from the network // cb = TlReadControl( CiClient, (PUCHAR)cp, sizeof(buf) ); if (cb > 0) { assert( cp->Length == (DWORD)cb ); if (cp->Type == CP_BREAKIN_CONNECTION) { // // some hosehead wants to break in and use this // client's debug pipe // sprintf( szMsg, "%s is requesting permission to interrupt your debug session.\n\nYou have %d seconds to respond or permission will be granted\n\nWould you like to grant permission?", cp->ClientId, BREAKIN_TIMEOUT ); response = MessageBox( NULL, szMsg, "Windbg Remote Debugger", MB_ICONQUESTION | MB_OKCANCEL | MB_SETFOREGROUND, 1000 * BREAKIN_TIMEOUT ); if (response == STATUS_TIMEOUT || response == IDOK) { cp->Response = TRUE; } else { cp->Response = FALSE; } TlWriteControl( CiClient, (PUCHAR)cp, sizeof(*cp) ); } } } return 0; }