Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

751 lines
18 KiB

/*++
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 <windows.h>
#include <stdio.h>
#include <stdlib.h>
#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; i<MAX_PIPES; i++) {
ci[i].hPipe = INVALID_HANDLE_VALUE;
ci[i].olConnect.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
ci[i].olRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
ci[i].olWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
}
return;
}
XOSD
TlCreateControlPipe(
LPSTR szName
)
/*++
Routine Description:
This function creates the pipe which will be connected to windbgrm (server).
Arguments:
szName - Supplies the name of the pipe to create
Return Value:
XOSD error code.
--*/
{
SECURITY_DESCRIPTOR securityDescriptor;
SECURITY_ATTRIBUTES lsa;
DWORD ec;
DWORD i;
for (i=0; i<MAX_PIPES; i++) {
ci[i].fConnected = FALSE;
//
// Set a security descriptor
//
InitializeSecurityDescriptor( &securityDescriptor,
SECURITY_DESCRIPTOR_REVISION );
SetSecurityDescriptorDacl( &securityDescriptor, TRUE, NULL, FALSE );
lsa.nLength = sizeof(SECURITY_ATTRIBUTES);
lsa.lpSecurityDescriptor = &securityDescriptor;
lsa.bInheritHandle = TRUE;
sprintf( ci[i].szName, "\\\\.\\pipe\\%scontrol", szName );
ci[i].hPipe =
CreateNamedPipe( ci[i].szName,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
PIPE_BUFFER_SIZE,
PIPE_BUFFER_SIZE,
1000,
&lsa
);
if (ci[i].hPipe == INVALID_HANDLE_VALUE) {
ec = GetLastError();
DEBUG_OUT1("TLCreateControlPipe: failed ec=%u\n", ec);
return xosdBadPipeName;
}
}
return xosdNone;
}
XOSD
TlCreateClientControlPipe(
LPSTR lpHostName,
LPSTR lpPipeName
)
/*++
Routine Description:
This function creates the pipe which will be connected to windbgrm (server).
Arguments:
lpHostName - Supplies name of remote debuggee host
lpPipeName - Supplies remote pipe name
Return Value:
XOSD error code.
--*/
{
SECURITY_DESCRIPTOR securityDescriptor;
SECURITY_ATTRIBUTES lsa;
DWORD timeOut;
DWORD mode;
BYTE buf[256];
LPCONTROLPACKET cp = (LPCONTROLPACKET) &buf[0];
ControlClient(fConnected) = FALSE;
//
// Set a security descriptor
//
InitializeSecurityDescriptor( &securityDescriptor,
SECURITY_DESCRIPTOR_REVISION );
SetSecurityDescriptorDacl( &securityDescriptor, TRUE, NULL, FALSE );
lsa.nLength = sizeof(SECURITY_ATTRIBUTES);
lsa.lpSecurityDescriptor = &securityDescriptor;
lsa.bInheritHandle = TRUE;
if (!lpHostName || !*lpHostName) {
lpHostName = DEFAULT_SERVER;
}
if (!lpPipeName || !*lpPipeName) {
lpPipeName = DEFAULT_PIPE;
}
sprintf( ControlClient(szName), "\\\\%s\\pipe\\%scontrol",
lpHostName, lpPipeName);
mode = sizeof(ControlClient(szClientId));
GetComputerName( ControlClient(szClientId), &mode );
strcpy( cp->ClientId, 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;
}