Named Pipe Client/Server
To provide a realistic implementation, this article will use Named Pipes in blocking mode as the underlying communication. Named Pipes is an interprocess communication mechanism thatch is easy to implement so the reader can focus on the cryptographic functions without the distraction of asynchronous socket programming. Note that Q177696, How To Use Named Pipes in a Visual Basic 32-bit Program is the most concise documentation for using Named Pipes the author has found.
The author chose a single Workspace with two projects: a Client Project and a Sever Project.
[image04.gif]
At times, it is a bit inconvenient because a switch must occur to the 'Active Project' (by way of the Project menu shown below).
[image05.gif]
The first step in the Product Activation Server implementation is developing an echo server based on Named Pipes. The following points should be observed with respect to the following code:
- Only the Server creates the Named Pipe using CreateNamedPipe()
- The Server must be running Windows NT or above
- The Client opens the Named Pipe by using CreateFile()
- The client can be any 32-bit version of the Windows OS
- The pipe is bidirectional by way of PIPE_ACCESS_DUPLEX
- The Server and Client connect to the Named Pipe by Handle
- The Server and Client read data from the pipe using ReadFile()
- The Server and Client write data to the pipe
using WriteFile()
- \\.\Pipe\ specifies the Local Machine
The Named Pipe is created by the Server as follows:
bool PipeCreate( HANDLE& pipe )
{
pipe = CreateNamedPipe( _T("\\\\.\\Pipe\\Product Activation Test"),
PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE |
PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
BUFFER_SIZE, BUFFER_SIZE,
PIPE_TIMEOUT, NULL );
return( INVALID_HANDLE_VALUE != pipe );
}
Once the pipe is created, the Server listens for a connection. Note that PipeRead blocks waiting for a Client connection because the underlying ConnectNamedPipe() is synchronous.
int main(int argc, char* argv[])
{
HANDLE pipe = INVALID_HANDLE_VALUE ;
if( false == PipeCreate( pipe ) )
{ ... }
std::string Recieved;
if( false == PipeRead( pipe, Recieved ) )
{ ... }
return 0;
}
ReadPipe() is shown below. All information sent across the pipe will be Base64 encoded. However, as a safety, cbBuffer[ dwRead ] = _T( '\0' ) is performed to assure a NULL terminated string.
bool PipeRead( const HANDLE& pipe, std::string& Recieved )
{
bool result = ( TRUE == ConnectNamedPipe( pipe, NULL ) );
if( false == result && ERROR_PIPE_CONNECTED != GetLastError() )
{ return false; }
byte cbBuffer[ BUFFER_SIZE ];
DWORD dwRead = -1;
result = ( TRUE == ReadFile( pipe, cbBuffer,
BUFFER_SIZE, &dwRead, NULL ) );
cbBuffer[ dwRead ] = _T( '\0' );
FlushFileBuffers( pipe );
return 0 != dwRead;
}
PipeWrite will be coded as follows. Message.length() + 1 is used to capture the NULL termination.
bool PipeWrite( const HANDLE& pipe, const std::string& Message )
{
bool result = ( TRUE == ConnectNamedPipe( pipe, NULL ) );
if( false == result && ERROR_PIPE_CONNECTED != GetLastError() )
{ return false; }
DWORD dwWritten = -1;
result = ( TRUE == WriteFile( pipe, Message.c_str(),
Message.length() + 1, &dwWritten, NULL ) );
return( dwWritten == Message.length() + 1 );
}
The final Echo Server code is shown following the results of running Server.
[image03.gif]
// Server.cpp
bool PipeCreate( HANDLE& pipe );
bool PipeRead( const HANDLE& pipe, std::string& Recieved );
bool PipeWrite( const HANDLE& pipe, const std::string& Message );
void DebugMessage( const std::string& message, bool ExtraCRLF = false );
int main(int argc, char* argv[])
{
HANDLE pipe = INVALID_HANDLE_VALUE ;
try {
if( false == PipeCreate( pipe ) )
{ throw std::string( "Server: CreateNamedPipe returned
INVALID_HANDLE_VALUE" ); }
std::string Recieved;;
if( false == PipeRead( pipe, Recieved ) )
{ throw( std::string( _T("Server:
PipeRead returned false") ) ); }
if( false == PipeWrite( pipe, Recieved ) )
{ throw( std::string( _T("Server: WriteRead
returned false") ) ); }
// If the program exits, the Client will receive
// ERROR_FILE_NOT_FOUND.
while( true ) {
Sleep( 5 * 1000 );
}
}
catch( CryptoPP::Exception& e ) {
std::cerr << "Error: " << e.what() << std::endl;
}
catch( std::string& s ) {
std::cerr << "Error: " << s << std::endl;
}
catch( TCHAR* s ) {
std::cerr << "Error: " << s << std::endl;
}
catch (...) {
std::cerr << "Unknown Error" << std::endl;
}
if( NULL != pipe ) { CloseHandle( pipe ); }
return 0;
}
bool PipeWrite( const HANDLE& pipe, const std::string& Message )
{
bool result = ( TRUE == ConnectNamedPipe( pipe, NULL ) );
if( false == result && ERROR_PIPE_CONNECTED != GetLastError() )
{ return false; }
DWORD dwWritten = -1;
result = ( TRUE == WriteFile( pipe, Message.c_str(),
Message.length() + 1,
&dwWritten, NULL ) );
FlushFileBuffers( pipe );
// Do not call DisconnectNamedPipe()
// The client will receive ERROR_PIPE_BUSY
// or ERROR_PIPE_NOT_CONNECTED
DebugMessage( "Server: Sent:" );
DebugMessage( Message );
return( dwWritten == Message.length() + 1 );
}
bool PipeRead( const HANDLE& pipe, std::string& Received )
{
DebugMessage( "Server: Waiting for Client Connection" );
bool result = ( TRUE == ConnectNamedPipe( pipe, NULL ) );
if( false == result && ERROR_PIPE_CONNECTED != GetLastError() )
{ return false; }
byte cbBuffer[ BUFFER_SIZE ];
DWORD dwRead = -1;
result = ( TRUE == ReadFile( pipe, cbBuffer,
BUFFER_SIZE, &dwRead, NULL ) );
cbBuffer[ dwRead ] = _T( '\0' );
Received = (char*)cbBuffer;
DebugMessage( _T( "Server: Received:" ) );
DebugMessage( Received );
FlushFileBuffers( pipe );
// Do not call DisconnectNamedPipe()
// The client will receive ERROR_PIPE_BUSY
// or ERROR_PIPE_NOT_CONNECTED
return( 0 != dwRead );
}
bool PipeCreate( HANDLE& pipe )
{
pipe = CreateNamedPipe( _T("\\\\.\\Pipe\\ProductActivationTest"),
PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE |
PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
BUFFER_SIZE, BUFFER_SIZE, PIPE_TIMEOUT,
NULL );
return( INVALID_HANDLE_VALUE != pipe );
}
void DebugMessage( const std::string& message, bool ExtraCRLF )
{
std::cout << message << std::endl;
if( true == ExtraCRLF )
{ std::cout << std::endl; }
}
Comments
There are no comments yet. Be the first to comment!