#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#define NPC_MAX_RETRIES 3
#define NPC_WAIT_TIMEOUT_IN_MS 10000
#define NPC_TRANSFER_BUFFER_SIZE 8192
#define NPC_WAIT_DATA_TIMEOUT 1000
#define NPC_READ_END 0
#define NPC_WRITE_END 1
//
// Structures
//
typedef struct _NPC_CONNECTION
{
HANDLE Peer1;
HANDLE Peer2;
} NPC_CONNECTION, *PNPC_CONNECTION;
typedef enum _NPC_TRANSFER_STATE
{
IoPending,
Idle
} NPC_TRANSFER_STATE, *PNPC_TRANSFER_STATE;
typedef struct _NPC_ENDPOINT
{
OVERLAPPED Overlap;
UCHAR TransferBuffer[4048];
NPC_TRANSFER_STATE State;
HANDLE Pipe;
} NPC_ENDPOINT, *PNPC_ENDPOINT;
//
// Macros
//
#define NpcCreateConnection(Connection, From, To) \
(Connection)->Peer1 = (From); \
(Connection)->Peer2 = (To);
#define NpcPrintError(Message, Code) \
PrintError((Message), (Code), TEXT(__FILE__), __LINE__);
HANDLE Server1, Server2;
HANDLE Mutex;
VOID PrintError(TCHAR *Message, DWORD ErrorCode, TCHAR* File, DWORD Line)
{
LPVOID lpMessageBuffer;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
ErrorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMessageBuffer,
0,
NULL );
_tprintf(TEXT("[ERROR] %ws:%d: %ws: %ws"), File, Line, Message, lpMessageBuffer);
LocalFree( lpMessageBuffer );
}
HANDLE NpcOpenPipe(TCHAR *PipeName)
{
HANDLE Pipe;
do
{
int ConnectionRetries = 0;
BOOL WaitSuccessful = FALSE;
Pipe = CreateFile(
PipeName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
if (Pipe != INVALID_HANDLE_VALUE)
{
return Pipe;
}
if (GetLastError() != ERROR_PIPE_BUSY)
{
NpcPrintError(TEXT("CreateFile"), GetLastError());
return Pipe;
}
// All pipe instances are busy, so wait some time ...
for(ConnectionRetries = 0; ConnectionRetries < NPC_MAX_RETRIES; ConnectionRetries++)
{
WaitSuccessful = WaitNamedPipe(PipeName, NPC_WAIT_TIMEOUT_IN_MS);
if (WaitSuccessful)
{
break;
}
}
if (!WaitSuccessful)
{
NpcPrintError(TEXT("WaitNamedPipe"), GetLastError());
break;
}
} while (TRUE);
return INVALID_HANDLE_VALUE;
}
BOOL NpcInitializeIoEvents(__ecount(EventCount) PHANDLE Events, DWORD EventCount)
{
DWORD i;
for (i = 0; i < EventCount; i++)
{
Events[i] = CreateEvent(
NULL,
TRUE,
FALSE,
NULL);
if (Events[i] == INVALID_HANDLE_VALUE)
{
NpcPrintError(TEXT("CreateEvent"), GetLastError());
return FALSE;
}
}
return TRUE;
}
DWORD WINAPI NpcTransferThread(LPVOID Param)
{
PNPC_CONNECTION Connection = (PNPC_CONNECTION) Param;
UCHAR TransferBuffer[NPC_TRANSFER_BUFFER_SIZE];
DWORD BytesRead = 0, BytesWritten;
BOOL OperationSuccess = FALSE;
PUCHAR NextByte = TransferBuffer;
HANDLE IoEvents[2];
DWORD ThreadId = GetCurrentThreadId();
char * WaitPeer1 = "";
DWORD BulkRead;
PUCHAR ReadOffset;
OVERLAPPED Peer1Overlap, Peer2Overlap;
ZeroMemory(TransferBuffer, sizeof(TransferBuffer));
ZeroMemory(&Peer1Overlap, sizeof(OVERLAPPED));
ZeroMemory(&Peer2Overlap, sizeof(OVERLAPPED));
if (!NpcInitializeIoEvents(IoEvents, 2))
{
NpcPrintError(TEXT("NpcInitializeIoEvents"), GetLastError());
return -1;
}
Peer1Overlap.hEvent = IoEvents[NPC_READ_END];
Peer2Overlap.hEvent = IoEvents[NPC_WRITE_END];
while (1)
{
// Read data from Peer 1
ReadOffset = TransferBuffer;
BulkRead = 0;
OperationSuccess = ReadFile(
Connection->Peer1,
TransferBuffer,
sizeof(TransferBuffer),
&BytesRead,
&Peer1Overlap);
//
// Ignore if there is more data to read since we will continue reading
// data at the next iteration of the loop.
//
if (!OperationSuccess)
{
if (GetLastError() == ERROR_IO_PENDING)
{
//
// Now update how many data we read
//
if (!GetOverlappedResult(
Connection->Peer1,
&Peer1Overlap,
&BytesRead,
TRUE))
{
NpcPrintError(TEXT("GetOverlappedResult"), GetLastError());
return GetLastError();
}
}
else
{
NpcPrintError(TEXT("ReadFile"), GetLastError());
return -1;
}
}
//
// Send data to peer 2, as long as we have data in the buffer ...
//
NextByte = TransferBuffer;
TransferBuffer[BytesRead] = 0;
while (BytesRead)
{
OperationSuccess = WriteFile(
Connection->Peer2,
NextByte,
BytesRead,
&BytesWritten,
&Peer2Overlap);
if (!OperationSuccess)
{
if (GetLastError() == ERROR_IO_PENDING)
{
if (!GetOverlappedResult(Connection->Peer2, &Peer2Overlap, &BytesWritten, TRUE))
{
NpcPrintError(TEXT("GetOverlappedResult"), GetLastError());
return GetLastError();
}
}
else
{
NpcPrintError(TEXT("WriteFile"),GetLastError());
return -1;
}
}
BytesRead -= BytesWritten;
NextByte += BytesWritten;
}
}
return 0;
}
HANDLE NpcConnect(PNPC_CONNECTION Connection)
{
return CreateThread(
NULL,
0,
NpcTransferThread,
Connection,
0,
NULL);
}
int _tmain(int argc, TCHAR *argv[])
{
NPC_CONNECTION Peer1_to_Peer2;
NPC_CONNECTION Peer2_to_Peer1;
HANDLE WaitableResources[4];
PHANDLE Connections = WaitableResources;
PHANDLE Servers = &WaitableResources[2];
if (argc != 3)
{
printf("Usage: %ws <pipe1> <pipe2>\n", argv[0]);
return -1;
}
_tprintf(TEXT("Connecting '%s' <-> '%s' ...\n"), argv[1], argv[2]);
Servers[0] = NpcOpenPipe(argv[1]);
if (Server1 == INVALID_HANDLE_VALUE)
{
NpcPrintError(TEXT("NpcOpenPipe 1"), GetLastError());
return -1;
}
Servers[1] = NpcOpenPipe(argv[2]);
if (Server2 == INVALID_HANDLE_VALUE)
{
NpcPrintError(TEXT("NpcOpenPipe 2"), GetLastError());
return -1;
}
NpcCreateConnection(&Peer1_to_Peer2, Servers[0], Servers[1]);
NpcCreateConnection(&Peer2_to_Peer1, Servers[1], Servers[0]);
Mutex = CreateMutex(
NULL,
FALSE,
NULL);
if (Mutex == INVALID_HANDLE_VALUE)
{
NpcPrintError(TEXT("CreateMutex"), GetLastError());
return -1;
}
Connections[0] = NpcConnect(&Peer1_to_Peer2);
if (Connections[0] == INVALID_HANDLE_VALUE)
{
NpcPrintError(TEXT("NpcConnect"), GetLastError());
return -1;
}
Connections[1] = NpcConnect(&Peer2_to_Peer1);
if (Connections[1] == INVALID_HANDLE_VALUE)
{
NpcPrintError(TEXT("NpcConnect"), GetLastError());
return -1;
}
_tprintf(TEXT("Connection established.\n"));
WaitForMultipleObjects(
4,
WaitableResources,
TRUE,
INFINITE);
return 0;
}