#include #include #include #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 \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; }