Developer's Guide
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
C++ Examples

KSL C++ KMIP Example 1 - Multi-Batch Request

This example demonstrates a request message that contains two Create operations, one Query, and one Discover Version KMIP operations. This is the same example as presented in JNI KMIP Example 1.

ex-kmip-1.cpp

#include <stdio.h>
#include <stdlib.h>
#include <memory>
#include "p6com.h" // P6R specific headers
#include "p6loader.h" // Standalone Component Loader definitions
#include "p6kmipbase.h"
#include "p6config.h"
#include "p6file.h"
#include "p6dir.h"
#include "cconsolestream.h"
#include "p6err.h" // Error and type definitions
#include "p6comdef.h" // [p6]COM definitions
#include "p6errorinfo.h" // Definition only, not supported by p6Loader
#include "p6runtimeapi.h" // Platform runtime API definitions
#include "p6comhlpr.h" // [p6]COM helper macros
#include "p6comptr.h" // Smart COM pointer, ASSERTs disabled
#include "p6runtimeif.h" // P6Platform's runtime COM API interface definitions
#include "p6datastream.h" // p6IDataStream interface definition
#include "p6kmipserverparser.h" // KSL's parser API definition
#include "p6kmipserverresponse.h" // KSL's response message generator API definition
using namespace P6R;
P6DECLARE_IID( p6IRunningIface );
P6DECLARE_CID( p6EntropySource );
P6DECLARE_CID( p6Random );
P6DECLARE_CID( p6IoBufferFactory );
P6DECLARE_CID( p6KMIPServerParser );
P6DECLARE_CID( p6KMIPServerResponse );
P6DECLARE_CID( p6KMIPBatchResponseInit );
P6DECLARE_IID( p6IKMIPBatchResponseInit );
P6DECLARE_IID( p6IKMIPBatchResponse );
namespace {
// This test message was created by the P6R KMIP Client SKC library in TTLV encoding, it contains 4 batch items: 2 Create, one Query, and one Discover Versions, it uses KMIP 1.3 protocol version
static P6UCHAR testMessage[] = {
0x42, 0x00, 0x78, 0x01, 0x00, 0x00, 0x03, 0x98, 0x42, 0x00, 0x77, 0x01, 0x00, 0x00, 0x00, 0x88, 0x42, 0x00, 0x69, 0x01, 0x00, 0x00, 0x00, 0x20, 0x42, 0x00, 0x6a, 0x02, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x6b, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x50, 0x02, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x01, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x07, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x42, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x10, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x42, 0x00, 0x92, 0x09, 0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x00, 0x59, 0xdc, 0x27, 0x66, 0x42, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00, 0xe8,
0x42, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x93, 0x08, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x42, 0x00, 0x79, 0x01, 0x00, 0x00, 0x00, 0xc0, 0x42, 0x00, 0x57, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x91, 0x01, 0x00, 0x00, 0x00, 0xa8,
0x42, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x30, 0x42, 0x00, 0x0a, 0x07, 0x00, 0x00, 0x00, 0x17, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x20, 0x41, 0x6c,
0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x00, 0x42, 0x00, 0x0b, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x30,
0x42, 0x00, 0x0a, 0x07, 0x00, 0x00, 0x00, 0x14, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x20, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00,
0x42, 0x00, 0x0b, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x30, 0x42, 0x00, 0x0a, 0x07, 0x00, 0x00, 0x00, 0x18,
0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x20, 0x55, 0x73, 0x61, 0x67, 0x65, 0x20, 0x4d, 0x61, 0x73, 0x6b, 0x42, 0x00, 0x0b, 0x02, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x01, 0x20, 0x42, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x42, 0x00, 0x93, 0x08, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x79, 0x01, 0x00, 0x00, 0x00, 0xf8, 0x42, 0x00, 0x57, 0x05, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x91, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x30, 0x42, 0x00, 0x0a, 0x07, 0x00, 0x00, 0x00, 0x17,
0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x20, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x00, 0x42, 0x00, 0x0b, 0x05, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x30, 0x42, 0x00, 0x0a, 0x07, 0x00, 0x00, 0x00, 0x14, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x67, 0x72,
0x61, 0x70, 0x68, 0x69, 0x63, 0x20, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0b, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x42, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x30, 0x42, 0x00, 0x0a, 0x07, 0x00, 0x00, 0x00, 0x18, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x20, 0x55, 0x73,
0x61, 0x67, 0x65, 0x20, 0x4d, 0x61, 0x73, 0x6b, 0x42, 0x00, 0x0b, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x30,
0x42, 0x00, 0x0a, 0x07, 0x00, 0x00, 0x00, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0b, 0x07, 0x00, 0x00, 0x00, 0x0b,
0x31, 0x20, 0x32, 0x20, 0x33, 0x20, 0x34, 0x20, 0x35, 0x20, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00, 0xb8, 0x42, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x93, 0x08, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x79, 0x01, 0x00, 0x00, 0x00, 0x90,
0x42, 0x00, 0x74, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x74, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x42, 0x00, 0x74, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x74, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00,
0x42, 0x00, 0x74, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x74, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00,
0x42, 0x00, 0x74, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x74, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
0x42, 0x00, 0x74, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x28, 0x42, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x93, 0x08, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x79, 0x01, 0x00, 0x00, 0x00, 0x00 };
class CKmipExample1
{
public:
P6CLASSMETHOD initialize();
P6CLASSMETHOD run( p6IDataStream* pStreamDebug, const P6UCHAR* pBytes, P6UINT32 bytesLength, P6UINT32 messageFormat );
CKmipExample1() : m_pPool( NULL ), m_pParser( NULL )
{ }
~CKmipExample1()
{
// -> P6R objects are reference counted and delete themselves
if (NULL != m_pPool ) m_pPool->release();
if (NULL != m_pParser) m_pParser->release();
}
protected:
// -> one function for each batch item in the test request above
P6CLASSMETHOD createOperation( P6KMIP_BATCHREQ* pBatchItem, P6UINT32 batchIndex );
P6CLASSMETHOD queryOperation( P6KMIP_BATCHREQ* pBatchItem, P6UINT32 batchIndex );
P6CLASSMETHOD versionsOperation( P6KMIP_BATCHREQ* pBatchItem, P6UINT32 batchIndex );
// -> helper functions used often
P6CLASSMETHOD extractBinaryAlloc( p6IKMIPBinary* pEnum, P6BSTR* pData, P6BOOL bFree );
// -> used in case we need to create a GUID
P6CLASSMETHOD getRNG( p6IRandom **ppRandom );
// -> can use P6R smart pointers or just C++ pointers
p6ComPtr<p6ISafeString> m_cpStr; // -> generic string library
p6ComPtr<p6IRandom> m_cpRandom; // -> used in case we need to create a GUID
p6IIoBufferFactory* m_pPool; // -> KSL parser requires KMIP message input via one of our Buffer objects
p6IKMIPServerParser* m_pParser;
};
// GUID and Key generation requires an entropy source
//
P6CLASSMETHODIMPL CKmipExample1::getRNG( p6IRandom** ppRandom )
{
P6ERR err = eOk;
p6ComPtr<p6IRunningIface> cpRIT( IID_p6IRunningIface, &err );
if (P6SUCCEEDED( err = p6CreateCryptoInstance( CID_p6EntropySource, VALIDATECOMPTR( p6IEntropySource, cpSource ))))
{
if (P6SUCCEEDED( err = cpSource->initialize( P6ENTROPY_HIGH )))
{
if (P6SUCCEEDED( err = p6CreateCryptoInstance( CID_p6Random, VALIDATECOMPTR( p6IRandomInit, cpInit ))))
{
if (P6SUCCEEDED( err = cpInit->initialize( P6RAND_NOFLAGS, cpSource ))) {
err = cpInit.queryInterface( VALIDATEIF( p6IRandom, ppRandom ));
}
}
}
}
return err;
}
// Our parser handles one KMIP request at a time. The parser object maintains the parsed message state until another message if passed to it.
//
P6R::P6ERR CKmipExample1::initialize()
{ // Configuration items for our memory buffer pool
P6UINT32 maxBufferSize = 65555; // -> hard coded here but these are typically from a configuration file
P6UINT32 initialNumberOfBuffers = 3; // -> typically application configuration information
P6UINT32 growBuffersBy = 3; // -> configuration info
P6ERR err = eOk;
// -> our all purpose, portable, string library, you could use your own
if (P6FAILED( err = p6GetRuntimeIface( VALIDATECOMPTR( p6ISafeString, m_cpStr )))) return err;
// -> random number generator for GUID generation
if (P6FAILED( err = getRNG( m_cpRandom.addressof()))) return err;
// -> parser takes our IO buffer object that we copy the incoming KMIP message into
// -> a buffer pool can be shared across multiple parser objects
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6IoBufferFactory, VALIDATEIF( p6IIoBufferFactory, &m_pPool )))) return err;
if (P6FAILED( err = m_pPool->initialize( P6CTEXT("p6KMIPserverlib"), maxBufferSize, initialNumberOfBuffers, growBuffersBy, P6IOBF_NOFLAGS ))) return err;
// -> P6R objects separate object creation and initialization into two steps
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6KMIPServerParser, VALIDATEIF( p6IKMIPServerParser, &m_pParser )))) return err;
if (P6FAILED( err = m_pParser->initialize( P6KMIPFLG_TRACE_VERBOSE, 0, m_cpRandom ))) return err;
return err;
}
// Here we have to allocate a buffer to extract the binary data out of the enumerator.
// Multi-Batch item KMIP messages have a byte string unique identifier for each batch item and these are required in any response.
// So we extract them from the request and pass them back in the response message.
//
P6CLASSMETHODIMPL CKmipExample1::extractBinaryAlloc( p6IKMIPBinary* pEnum, P6BSTR* pData, P6BOOL bFree )
{
P6UCHAR* pBin = NULL;
P6BSTR buffer = { NULL, 0 };
P6ERR err = eOk;
// -> nothing to extract?
pData->pString = NULL;
pData->length = 0;
if (NULL == pEnum) return err;
err = pEnum->reset();
err = pEnum->next( &buffer );
if ( 0 < buffer.length )
{
if (NULL == (pBin = new(std::nothrow) P6UCHAR[buffer.length])) return eNoMemory;
pBin[0] = 0;
buffer.pString = pBin;
if (P6FAILED( err = pEnum->next( &buffer ))) return err;
pData->pString = buffer.pString;
pData->length = buffer.length;
}
else err = eEndOfRecord;
if (bFree) pEnum->release();
return err;
}
// Demonstrate how to extract the parsed info for the request, a real server would then implement and generate a response for this batch item
//
P6R::P6ERR CKmipExample1::createOperation( P6KMIP_BATCHREQ* pBatchItem, P6UINT32 batchIndex )
{
P6UINT32 attribType = 0;
P6UINT32 number = 0;
P6UINT32 cryptoAlg = 0; // see p6kmip.h for valid KMIP constant values allowed for the cryptographic algorithm attribute (e.g., 0x03 is for AES as defined in the KMIP specification)
P6UINT32 cryptoLength = 0;
P6UINT32 cryptoMask = 0; // see p6kmip.h for valid KMIP constant values allowed for the cryptographic usage bit mask attribute
P6INT32 sensitive = -1; // optional, -1 indicates not set by client, 0 is false, 1 is true
P6INT32 extractable = -1; // optional, -1 indicates not set by client, 0 is false, 1 is true
P6ERR err = eOk;
P6ERR err2 = eOk;
// -> type of object to create: Symmetric Key or Secret Data, anything else is an error
if (KMIP_OBJECT_SYMMETRICKEY != pBatchItem->value.create.objectType && KMIP_OBJECT_SECRETDATA != pBatchItem->value.create.objectType) return eFail;
// -> just show how a sample of attributes can be pulled out of the request, others are possible
if (NULL != pBatchItem->value.create.pAttribute)
{
// -> how many attributes in the enumerator?
err = pBatchItem->value.create.pAttribute->count( &number );
pBatchItem->value.create.pAttribute->reset();
while( P6SUCCEEDED( err2 = pBatchItem->value.create.pAttribute->next( &attribType )))
{
m_cpStr->setMem( &objAttribute, 0, sizeof( P6KMIP_OBJECT_ATTRIBUTE ));
err = pBatchItem->value.create.pAttribute->getValue( &objAttribute );
switch( attribType ) {
case KMIP_ATTRIB_CRYPTOALGORITHM: if (P6SUCCEEDED( err )) cryptoAlg = objAttribute.value.cryptoAlgorithm; break;
case KMIP_ATTRIB_CRYPTOLENGTH: if (P6SUCCEEDED( err )) cryptoLength = objAttribute.value.cryptoLength; break;
case KMIP_ATTRIB_CRYPTOUSAGEMASK: if (P6SUCCEEDED( err )) cryptoMask = objAttribute.value.cryptoUsageMask; break;
case KMIP_ATTRIB_SENSITIVE: if (P6SUCCEEDED( err )) sensitive = (P6INT32)objAttribute.value.sensitive; break;
case KMIP_ATTRIB_EXTRACTABLE: if (P6SUCCEEDED( err )) extractable = (P6INT32)objAttribute.value.extractable; break;
default: err = eOk; break;
}
}
// -> when done with an enumerator you have to release it or it will leak
pBatchItem->value.create.pAttribute->release();
}
// *** Note: a real application then takes the parsed data and creates the requested object
// *** NOTE: then sends back a success or failure response to the KMIP client (demonstrated in another example),
// on success it returns the unique identifier of the object created by the server
printf( "KSL create operation crypto algorithm: %d\n", cryptoAlg );
printf( "KSL create operation crypto length: %d\n", cryptoLength );
printf( "KSL create operation crypto maks: %d\n", cryptoMask );
printf( "KSL create operation sensitive: %d\n", sensitive );
printf( "KSL create operation extractable: %d\n", extractable );
return eOk;
}
// The P6KMIP_BATCHREQ is a Union of structures. one structure for each KMIP operation
// Client asks for information about the server
//
P6R::P6ERR CKmipExample1::queryOperation( P6KMIP_BATCHREQ* pBatchItem, P6UINT32 batchIndex )
{
P6UINT32 enumValue = 0;
pBatchItem->value.pQuery->reset();
while( P6SUCCEEDED( pBatchItem->value.pQuery->next2( &enumValue )))
{
switch ( enumValue ) {
case KMIP_QUERY_OPERATIIONS: printf( "KSL example1, query operations supported\n" ); break;
case KMIP_QUERY_OBJECTS: printf( "KSL example1, query objects supported\n" ); break;
case KMIP_QUERY_ATTENSTATIONTYPES: printf( "KSL example1, query attestation types supported\n" ); break;
case KMIP_QUERY_VALIDATIONS: printf( "KSL example1, query validationss supported\n" ); break;
case KMIP_QUERY_PROFILES: printf( "KSL example1, query profiles supported\n" ); break;
case KMIP_QUERY_SERVERINFORMATION: printf( "KSL example1, query server info supported\n" ); break;
case KMIP_QUERY_CAPABILITIES: printf( "KSL example1, query capabilities supported\n" ); break;
case KMIP_QUERY_REGMETHODS: printf( "KSL example1, query client registration methods supported\n" ); break;
case KMIP_QUERY_EXTENSIONLIST: printf( "KSL example1, query extension list supported\n" ); break;
case KMIP_QUERY_EXTENSIONMAP: printf( "KSL example1, query extension definitions supported\n" ); break;
case KMIP_QUERY_APPNAMESPACES: printf( "KSL example1, query application name spaces supported\n" ); break;
case KMIP_QUERY_RNGS: printf( "KSL example1, query random number generator algorithms supported\n" ); break;
default:
// -> server can ignore any query function it does not support
break;
}
}
// *** Note: a real application would generate a query response message for whatever it wishes to share with the client
pBatchItem->value.pQuery->release();
return eOk;
}
// By using enumerators in our API we can handle just about any number of items in a KMIP request message and our API is consistent
// On a Discover Versions operation a client can send the KMIP protocol versions it supports or nothing. The server returns back
// the intersection of what the server and client both support.
//
P6R::P6ERR CKmipExample1::versionsOperation( P6KMIP_BATCHREQ* pBatchItem, P6UINT32 batchIndex )
{
P6KMIP_VERSION clientSupported = {0};
P6UINT32 itemCount = 0; // -> did. the client send any versions with the request?
// -> if client does not provide a list of versions then server just returns all the versions it supports
// -> in this test case there are no client provided versions so pVersions is NULL
if (NULL != pBatchItem->value.pVersions)
{
pBatchItem->value.pVersions->count( &itemCount ); // -> our KMIP enumerators allow you to determine the number of items they contain in one call
pBatchItem->value.pVersions->reset();
while( P6SUCCEEDED( pBatchItem->value.pVersions->next( &clientSupported )))
{
printf( "KSL example1, client supports protocol versions: %d.%d\n", clientSupported.versionMajor, clientSupported.versionMinor );
}
pBatchItem->value.pVersions->release();
}
return eOk;
}
// Main test function
//
// pBytes -- contains the KMIP request message sent from a client
// bytesLength -- number of bytes in pBytes
// messageFormat -- is the KMIP message in TTLV, XML, or JSON, calling application determines by HTTP headers (if just over TLS then its TTLV, otherwise use the MIME type)
// valid values are defined in p6kmip.h
//
P6CLASSMETHODIMPL CKmipExample1::run( p6IDataStream* pStreamDebug, const P6UCHAR* pBytes, P6UINT32 bytesLength, P6UINT32 messageFormat )
{
P6KMIP_REQUEST_HEADER reqHeader; // -> contains the values from the KMIP request message header
P6KMIP_BATCHREQ batchItem;
p6IKMIPBatchItems* pBatchItems = NULL;
p6IIoBuffer* pInBuffer = NULL; // -> the KMIP parser requires an IO buffer as input
P6BSTR inBuffer = {0}; // -> the raw buffer we are going to fill in with the incoming message
P6BSTR uniqueId = {0}; // -> the unique batch identifier
P6UINT32 used = 0;
P6UINT32 batchIndex = 0;
P6ERR err = eOk;
m_cpStr->setMem( &reqHeader, 0, sizeof( P6KMIP_REQUEST_HEADER ));
// [A] Take the raw buffer from the application and copy its contents into one of our IO buffers for the parser to handle
if (P6FAILED( err = m_pPool->alloc( &pInBuffer ))) return err;
// -> all P6R objects are reference counted so to free then call the "release()" function on the object
if (P6FAILED( err = pInBuffer->getBufPtr( &(inBuffer.pString), (P6UINT32*)&(inBuffer.length), &used ))) {
pInBuffer->release();
return err;
}
if (P6FAILED( err = m_cpStr->moveMem( inBuffer.pString, inBuffer.length, pBytes, bytesLength ))) {
pInBuffer->release();
return err;
}
pInBuffer->setUsed( bytesLength );
// [B] setMessageBuffer sets up the parser with the message contents
if (P6FAILED( err = m_pParser->setMessageBuffer( messageFormat, pInBuffer, pStreamDebug ))) return err;
// -> pBatchItems is an enumerator that holds all batch items in the KMIP request message, call that enumerator to iterate over each batch item
if (P6FAILED( err = m_pParser->parseRequest( &reqHeader, &pBatchItems ))) return err;
// [C] Here we can examine the request header which holds the requests wide value including any additional credentials
// -> if credentials are present then a credentials enumerator is in the reqHeader structure, in this example there are no additional credentials
// -> KMIP version used by client: 0 == 1.0, 1 == 1.1, 2 == 1.2, 3 = 1.3, 4 = 1.4, 5 = 2.0
printf( "KSL msg header, protocol version: [ %d ]\n", reqHeader.protocolVersion );
printf( "KSL msg header, barch error option: [ %d ]\n", reqHeader.batchErrorOption );
printf( "KSL msg header, credentials object present : [ %s ]\n", (NULL != reqHeader.pCredentials ? "true" : "false" ));
// [D] Process each batch item in the incoming KMIP request message
m_cpStr->setMem( &batchItem, 0, sizeof( P6KMIP_BATCHREQ ));
for( ; batchIndex < reqHeader.batchCount && P6SUCCEEDED(err = pBatchItems->at(&batchItem, batchIndex)); batchIndex++ )
{
// -> pull the byte string from the enumerator object to get each batch item's unique identifier (not required for requests with just one batch item)
if (NULL != batchItem.pUniqueBatchId) {
err = extractBinaryAlloc( batchItem.pUniqueBatchId, &uniqueId, P6TRUE );
}
switch ( batchItem.type ) {
case KMIP_OP_CREATE:
// -> the test message has 2 create operations, the first for a 128 bit AES key, the second for 256 bit ECC key
err = createOperation( &batchItem, batchIndex );
break;
case KMIP_OP_QUERY:
err = queryOperation( &batchItem, batchIndex );
break;
case KMIP_OP_DISCOVERVERSIONS:
// -> the test message in this example has a DISCOVER VERSIONS operation
break;
// -> none of these are included in the current test message but are defined as a Union Structure in P6KMIP_BATCHREQ
case KMIP_OP_ACTIVATE:
case KMIP_OP_REVOKE:
case KMIP_OP_DESTROY:
case KMIP_OP_ADDATTRIBUTE:
case KMIP_OP_MODIFYATTRIBUTE:
case KMIP_OP_DELETEATTRIBUTE:
case KMIP_OP_GETATTRIBUTES:
case KMIP_OP_GETATTRIBUTELIST:
case KMIP_OP_LOCATE:
case KMIP_OP_CREATEKEYPAIR:
case KMIP_OP_GET:
case KMIP_OP_ENCRYPT:
case KMIP_OP_DECRYPT:
case KMIP_OP_GETUSAGEALLOC:
case KMIP_OP_RNGRETRIEVE:
case KMIP_OP_RNGSEED:
case KMIP_OP_OBTAINLEASE:
case KMIP_OP_REGISTER:
case KMIP_OP_CREATESPLITKEY:
case KMIP_OP_JOINSPLITKEY:
case KMIP_OP_REKEY:
case KMIP_OP_REKEYKEYPAIR:
case KMIP_OP_CERTIFY:
case KMIP_OP_RECERTIFY:
case KMIP_OP_CHECK:
case KMIP_OP_DERIVEKEY:
case KMIP_OP_SIGN:
case KMIP_OP_SIGNATUREVERIFY:
case KMIP_OP_MAC:
case KMIP_OP_MACVERIFY:
case KMIP_OP_HASH:
case KMIP_OP_LOG:
case KMIP_OP_ARCHIVE:
case KMIP_OP_RECOVER:
case KMIP_OP_IMPORT:
case KMIP_OP_EXPORT:
case KMIP_OP_CANCEL:
case KMIP_OP_POLL:
case KMIP_OP_VALIDATE:
default:
// -> your server application would handle each batch case if desired, or returns an error feature not supported
break;
}
// -> for this example we free the batch ids but if we where also building the response we would keep them to place into the response structure (see other examples)
if (NULL != uniqueId.pString) {
delete [] uniqueId.pString;
uniqueId.pString = NULL;
uniqueId.length = 0;
}
m_cpStr->setMem( &batchItem, 0, sizeof( P6KMIP_BATCHREQ ));
}
return err;
}
// Valid message format constants defined at the bottom of p6kmip.h are:
// KMIP_CMP_TTLV, KMIP_CMP_MSGXML, KMIP_CMP_MSGJSON (these are P6R defined constants)
//
P6VOID KMIP_BasicParserTest( p6IDataStream* pDataStream )
{
CKmipExample1 example;
P6CHAR szTmp[32];
P6ERR err = eOk;
if (P6SUCCEEDED(err = example.initialize())) {
err = example.run( pDataStream, testMessage, sizeof(testMessage), KMIP_CMP_TTLV );
}
printf( "KSL result: [ %s ]\n", p6ErrToStr(err, &szTmp[0], P6CHARCNT(szTmp)) );
}
} // namespace
// p6InitializeLoader() must be called before anything else can run.
//
int main(int argc,char *argv[])
{
P6ERR err = eOk;
if ( P6SUCCEEDED( err = P6EXAMPLES::CConsoleStream::createInstance( NULL, VALIDATECOMPTR( p6IDataStream, cpDataStream ))))
{
if ( P6SUCCEEDED( err = p6InitializeLoader( cpDataStream, 9, P6SCLF_ALLLOG )))
{
KMIP_BasicParserTest( cpDataStream );
}
else printf("ERROR: Failed to initialize the loader [ %x ]\n", err );
}
else printf( "ERROR: Failed to create CConsoleStream [ %x ]\n", err );
return err;
}

KSL C++ KMIP Example 2 - Multi-Batch Response Generation

This example demonstrates how to generate a KMIP response message to a multi-batch request in TTLV, XML, and JSON. This is a similar example as presented in JNI KMIP Example 7.

ex-kmip-2.cpp

#include <stdio.h>
#include <stdlib.h>
#include <memory>
#include "p6com.h" // P6R specific headers
#include "p6loader.h" // Standalone Component Loader definitions
#include "p6kmipbase.h"
#include "p6config.h"
#include "p6file.h"
#include "p6dir.h"
#include "cconsolestream.h"
#include "p6err.h" // Error and type definitions
#include "p6comdef.h" // [p6]COM definitions
#include "p6errorinfo.h" // Definition only, not supported by p6Loader
#include "p6runtimeapi.h" // Platform runtime API definitions
#include "p6comhlpr.h" // [p6]COM helper macros
#include "p6comptr.h" // Smart COM pointer, ASSERTs disabled
#include "p6runtimeif.h" // P6Platform's runtime COM API interface definitions
#include "p6datastream.h" // p6IDataStream interface definition
#include "p6kmipserverparser.h" // KSL's parser API definition
#include "p6kmipserverresponse.h" // KSL's response message generator API definition
#include "p6kmipbatchresponse.h"
using namespace P6R;
P6DECLARE_IID( p6IRunningIface );
P6DECLARE_CID( p6EntropySource );
P6DECLARE_CID( p6Random );
P6DECLARE_CID( p6IoBufferFactory );
P6DECLARE_CID( p6KMIPServerParser );
P6DECLARE_CID( p6KMIPServerResponse );
P6DECLARE_CID( p6KMIPBatchResponseInit );
P6DECLARE_IID( p6IKMIPBatchResponseInit );
P6DECLARE_IID( p6IKMIPBatchResponse );
namespace {
class CKmipExample2
{
public:
P6CLASSMETHOD initialize();
P6CLASSMETHOD run( p6IDataStream* pStreamDebug, P6UINT32 protocolVersion, P6UINT32 messageFormat, P6UCHAR** pBytes, P6UINT32* pBytesLength );
CKmipExample2() : m_pPool( NULL ), m_pAltPool( NULL ), m_pResponse( NULL )
{ }
~CKmipExample2()
{
// -> P6R objects are reference counted and delete themselves
if (NULL != m_pPool ) m_pPool->release();
if (NULL != m_pAltPool ) m_pAltPool->release();
if (NULL != m_pResponse) m_pResponse->release();
}
protected:
P6CLASSMETHOD allocateResponseBatchItems( P6UINT32 batchCount, P6KMIP_OUTGOING_RESPONSE*& pForClient );
P6CLASSMETHOD returnAttributes( P6KMIP_TEMPLATEATTRIBUTE*& pAttributes );
P6CLASSMETHOD freeResponseInfo( P6KMIP_OUTGOING_RESPONSE* pForClient );
// -> can use P6R smart pointers or just C++ pointers
p6ComPtr<p6ISafeString> m_cpStr; // -> generic string library
p6IIoBufferFactory* m_pPool; // -> KSL generates TTLV messages into
p6IIoBufferFactory* m_pAltPool; // -> KSL generates XML or JSON messages into (which are larger than TTLV)
p6IKMIPServerResponse* m_pResponse;
};
// Our message generator creates one KMIP response at a time. The parser object maintains the parsed message state until another message if passed to it.
//
P6R::P6ERR CKmipExample2::initialize()
{ // Configuration items for our memory buffer pool
P6UINT32 maxBufferSize = 65555; // -> hard coded here but these are typically from a configuration file
P6UINT32 altBufferSize = maxBufferSize*2; // -> hard coded here but these are typically from a configuration file
P6UINT32 initialNumberOfBuffers = 3; // -> typically application configuration information
P6UINT32 growBuffersBy = 3; // -> configuration info
P6ERR err = eOk;
// -> our all purpose, portable, string library, you could use your own
if (P6FAILED( err = p6GetRuntimeIface( VALIDATECOMPTR( p6ISafeString, m_cpStr )))) return err;
// -> We use two memory pools based on the type of responses we plan on generating
// -> a buffer pool can be shared across multiple response objects
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6IoBufferFactory, VALIDATEIF( p6IIoBufferFactory, &m_pPool )))) return err;
if (P6FAILED( err = m_pPool->initialize( P6CTEXT("p6KMIPserverlib-resp"), maxBufferSize, initialNumberOfBuffers, growBuffersBy, P6IOBF_NOFLAGS ))) return err;
// -> if the server will only support TTLV then the alt pool is not necessary and a NULL can be passed in for m_pAltPool
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6IoBufferFactory, VALIDATEIF( p6IIoBufferFactory, &m_pAltPool )))) return err;
if (P6FAILED( err = m_pAltPool->initialize( P6CTEXT("p6KMIPserverlib-altresp"), altBufferSize, initialNumberOfBuffers, growBuffersBy, P6IOBF_NOFLAGS ))) return err;
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6KMIPServerResponse, VALIDATEIF( p6IKMIPServerResponse, &m_pResponse )))) return err;
// -> the response generator has functions to change the protocol version and message format at any time, we just initialize with version 1.2 and TTLV
if (P6FAILED( err = m_pResponse->initialize( P6KMIPRESP_NOFLAGS, 2, KMIP_CMP_TTLV, (P6WCHAR*)P6TEXT("ksl_error_stream"), m_pPool, m_pAltPool ))) return err;
return err;
}
// P6KMIP_OUTGOING_RESPONSE is the response message definition that the server uses to pass what KMIP response it wants to be created
//
P6CLASSMETHODIMPL CKmipExample2::allocateResponseBatchItems( P6UINT32 batchCount, P6KMIP_OUTGOING_RESPONSE*& pForClient )
{
P6ERR err = eOk;
if (NULL == (pForClient = new (std::nothrow) P6KMIP_OUTGOING_RESPONSE())) return eNoMemory;
m_cpStr->setMem( pForClient, 0, sizeof( P6KMIP_OUTGOING_RESPONSE ));
pForClient->batchCount = batchCount;
if (NULL == (pForClient->pItems = new (std::nothrow) P6KMIP_BATCHRESP[ batchCount ])) {
delete pForClient;
pForClient = NULL;
return eNoMemory;
}
for( P6UINT32 i=0; i < batchCount && P6SUCCEEDED(err); i++ ) {
err = m_cpStr->setMem( &(pForClient->pItems[i]), 0, sizeof( P6KMIP_BATCHRESP ));
}
return err;
}
// Show how attributes can be constructed for response generation
// There are many possible attributes (around 80), we can provide example code for all of them, this is just a simple example
//
P6CLASSMETHODIMPL CKmipExample2::returnAttributes( P6KMIP_TEMPLATEATTRIBUTE*& pAttributes )
{
P6KMIP_ATTRIBUTE* pAttributeList = NULL;
P6ERR err = eOk;
if (NULL == (pAttributes = new (std::nothrow) P6KMIP_TEMPLATEATTRIBUTE())) return eNoMemory;
m_cpStr->setMem( pAttributes, 0, sizeof( P6KMIP_TEMPLATEATTRIBUTE ));
pAttributeList = NULL;
if (NULL == (pAttributeList = new (std::nothrow) P6KMIP_ATTRIBUTE[ 3 ])) {
delete pAttributes;
pAttributes = NULL;
return eNoMemory;
}
// -> some attributes return strings and some are complex structures (e.g., Cryptographic Parameters)
// -> all possible attribute types are defined by the P6KMIP_ATTRIBUTE type which again is a Union of Structures
pAttributeList[0].type = KMIP_ATTRIB_CRYPTOLENGTH;
pAttributeList[0].index = 0;
pAttributeList[0].value.cryptoLength = 256;
pAttributeList[1].type = KMIP_ATTRIB_CRYPTOUSAGEMASK;
pAttributeList[1].index = 0;
pAttributeList[1].value.cryptoUsageMask = KMIP_USE_ENCRYPT | KMIP_USE_DECRYPT;
pAttributeList[2].type = KMIP_ATTRIB_CRYPTOALGORITHM;
pAttributeList[2].index = 0;
pAttributeList[2].value.cryptoAlgorithm = KMIP_AES;
pAttributes->attribCount = 3;
pAttributes->pAttributeList = pAttributeList;
return err;
}
// The p6IKMIPBatchResponse object is designed to free all the parts of the P6KMIP_OUTGOING_RESPONSE structure.
//
P6CLASSMETHODIMPL CKmipExample2::freeResponseInfo( P6KMIP_OUTGOING_RESPONSE* pForClient )
{
p6IKMIPBatchResponseInit* pInst = NULL;
p6IKMIPBatchResponse* pResponse = NULL;
P6ERR err = eOk;
if (NULL == pForClient) return err;
if (P6SUCCEEDED( err = p6CreateInstance( NULL, CID_p6KMIPBatchResponseInit, VALIDATEIF( p6IKMIPBatchResponseInit, &pInst ))))
{
if (P6SUCCEEDED( err = pInst->initialize( pForClient ))) {
err = pInst->queryInterface( VALIDATEIF( p6IKMIPBatchResponse, &pResponse ));
}
pInst->release();
}
// -> prevents any leaks due to complex response messages
if ( NULL != pResponse ) {
err = pResponse->release();
}
return err;
}
// Main test function
//
// protocolVersion -- in: KMIP protocol version of the message to be generated, {0 == 1.0, 1 == 1.1, 2 == 1.2, 3 == 1.3, 4 == 1.4)
// messageFormat -- in: is the KMIP message in TTLV, XML, or JSON, calling application determines by HTTP headers (if just over TLS then its TTLV, otherwise use the MIME type)
// valid values are defined in p6kmip.h
// pBytes -- out: allocated buffer containing the newly generated KMIP response message
// pBytesLength -- out: number of bytes in pBytes
//
P6CLASSMETHODIMPL CKmipExample2::run( p6IDataStream* pStreamDebug, P6UINT32 protocolVersion, P6UINT32 messageFormat, P6UCHAR** pBytes, P6UINT32* pBytesLength )
{
P6KMIP_OUTGOING_RESPONSE* pForClient = NULL;
P6KMIP_TEMPLATEATTRIBUTE* pAttributes = NULL;
P6KMIP_VERSION* pVersionList = NULL;
P6UCHAR* pBID1 = NULL;
P6UCHAR* pBID2 = NULL;
P6UCHAR* pBID3 = NULL;
P6CHAR* pUID1 = NULL;
P6CHAR* pUID2 = NULL;
P6SIZE copied = 0;
p6IIoBuffer* pResponse = NULL;
P6UINT8* pRawBuffer = NULL;
P6UINT32 dontCare = 0;
P6UINT32 msgSize = 0;
P6ERR err = eOk;
// -> these will depend on what the KMIP request from the client has sent, respond back with the same values
err = m_pResponse->setMsgFormat( messageFormat );
err = m_pResponse->setProtocolVersion( protocolVersion );
// -> for this test case we will create a 3 batch response
if (P6FAILED( err = allocateResponseBatchItems( 3, pForClient ))) return err;
// [A] First response is for a create batch item
// -> the batch id (for a response with multiple batch items) MUST come from the request message (but here we will just fake them)
if (NULL == (pBID1 = new (std::nothrow) P6UCHAR[ 1 ])) return eNoMemory;
pBID1[0] = 0x01;
// -> unique identifiers are KMIP text strings, the P6R API requires them to be NULL terminated strings (so length is 37 not 36)
// -> typical GUID that can be used as a managed object's unique identifier
if (NULL == (pUID1 = new (std::nothrow) P6CHAR[ 37 ])) return eNoMemory;
pUID1[0] = 0;
m_cpStr->strlcat( pUID1, 37, "A36E641F-A590-4153-9CAB-129B0AD4A04F", &copied );
err = returnAttributes( pAttributes );
pForClient->pItems[ 0 ].uniqueBatchId.pString = pBID1;
pForClient->pItems[ 0 ].uniqueBatchId.length = 1;
pForClient->pItems[ 0 ].status = KMIP_RESULT_SUCCESS;
pForClient->pItems[ 0 ].pError = NULL;
pForClient->pItems[ 0 ].type = KMIP_OP_CREATE;
pForClient->pItems[ 0 ].value.create.objectType = KMIP_OBJECT_SYMMETRICKEY;
pForClient->pItems[ 0 ].value.create.pAttributes = pAttributes;
pForClient->pItems[ 0 ].value.create.uniqueId.pString = pUID1;
pForClient->pItems[ 0 ].value.create.uniqueId.length = 36;
// [B] Response for a create of a secret data object
// -> the batch id (for a response with multiple batch items) MUST come from the request message (but here we will just fake them)
if (NULL == (pBID2 = new (std::nothrow) P6UCHAR[ 1 ])) return eNoMemory;
pBID2[0] = 0x02;
// -> typical GUID that can be used as a managed object's unique identifier
if (NULL == (pUID2 = new (std::nothrow) P6CHAR[ 37 ])) return eNoMemory;
pUID2[0] = 0;
m_cpStr->strlcat( pUID2, 37, "EFD6A04B-C8F5-4F03-95BE-03D43AA03106", &copied );
pForClient->pItems[ 1 ].uniqueBatchId.pString = pBID2;
pForClient->pItems[ 1 ].uniqueBatchId.length = 1;
pForClient->pItems[ 1 ].status = KMIP_RESULT_SUCCESS;
pForClient->pItems[ 1 ].pError = NULL;
pForClient->pItems[ 1 ].type = KMIP_OP_CREATE;
pForClient->pItems[ 1 ].value.create.objectType = KMIP_OBJECT_SECRETDATA;
pForClient->pItems[ 1 ].value.create.pAttributes = NULL;
pForClient->pItems[ 1 ].value.create.uniqueId.pString = pUID2;
pForClient->pItems[ 1 ].value.create.uniqueId.length = 36;
// [C] Response for a Discover Versions operation
// -> the batch id only has to be unique in a message
if (NULL == (pBID3 = new (std::nothrow) P6UCHAR[ 1 ])) return eNoMemory;
pBID3[0] = 0x03;
if (NULL == (pVersionList = new (std::nothrow) P6KMIP_VERSION[ 5 ])) return eNoMemory;
pVersionList[0].versionMajor = 1; pVersionList[0].versionMinor = 0;
pVersionList[1].versionMajor = 1; pVersionList[1].versionMinor = 1;
pVersionList[2].versionMajor = 1; pVersionList[2].versionMinor = 2;
pVersionList[3].versionMajor = 1; pVersionList[3].versionMinor = 3;
pVersionList[4].versionMajor = 1; pVersionList[4].versionMinor = 4;
pForClient->pItems[ 2 ].uniqueBatchId.pString = pBID3;
pForClient->pItems[ 2 ].uniqueBatchId.length = 1;
pForClient->pItems[ 2 ].status = KMIP_RESULT_SUCCESS;
pForClient->pItems[ 2 ].pError = NULL;
pForClient->pItems[ 2 ].type = KMIP_OP_DISCOVERVERSIONS;
pForClient->pItems[ 2 ].value.discoverVersions.verCount = 5;
pForClient->pItems[ 2 ].value.discoverVersions.pVersions = pVersionList;
// [D] Generate the response message in the protocol and format defined by the parameters to the run() function above
if (P6SUCCEEDED( err = m_pResponse->generateBatchResponse( pForClient, &pResponse )))
{
if (P6SUCCEEDED( err = pResponse->getBufPtr( &pRawBuffer, &dontCare, &msgSize )))
{
if ( NULL != ((*pBytes) = new (std::nothrow) P6UCHAR[msgSize+1]))
{
err = m_cpStr->moveMem((*pBytes), msgSize, pRawBuffer, msgSize );
(*pBytesLength) = msgSize;
}
else err = eNoMemory;
}
pResponse->release();
}
freeResponseInfo( pForClient );
return err;
}
// First generate the fake response message in TTLV, and then the same response message in XML
//
P6VOID KMIP_BasicResponseTest( p6IDataStream* pDataStream )
{
CKmipExample2 example;
P6UCHAR* pBytes = NULL;
P6UINT32 bytesLength = 0;
P6CHAR szTmp[32];
P6ERR err1 = eOk;
P6ERR err2 = eOk;
P6ERR err3 = eOk;
if (P6SUCCEEDED( err1 = example.initialize()))
{
if (P6SUCCEEDED( err1 = example.run( pDataStream, 4, KMIP_CMP_TTLV, &pBytes, &bytesLength )))
{
// -> *** NOTE: a real application would then send this generated KMIP TTLV response message back to the client over the same TLS session the request was read on
printf("\n");
for( P6UINT32 i=0; i < bytesLength; i++ ) printf( "%x", pBytes[i] );
printf("\n\n");
if (NULL != pBytes) {
delete [] pBytes;
pBytes = NULL;
bytesLength = 0;
}
}
// -> now do the same thing but in XML
if (P6SUCCEEDED( err2 = example.run( pDataStream, 4, KMIP_CMP_MSGXML, &pBytes, &bytesLength )))
{
printf( "KSL result XML msg: %s\n", pBytes );
// -> *** NOTE: a real application would then send this generated KMIP XML response message back to the client over the same TLS session the request was read on
if (NULL != pBytes) {
delete [] pBytes;
pBytes = NULL;
bytesLength = 0;
}
}
// -> now do the same thing but in JSON
if (P6SUCCEEDED( err3 = example.run( pDataStream, 4, KMIP_CMP_MSGJSON, &pBytes, &bytesLength )))
{
printf( "\n\nKSL result JSON msg: %s\n", pBytes );
// -> *** NOTE: a real application would then send this generated KMIP JSON response message back to the client over the same TLS session the request was read on
if (NULL != pBytes) {
delete [] pBytes;
pBytes = NULL;
bytesLength = 0;
}
}
}
printf( "KSL result 1: [ %s ]\n", p6ErrToStr(err1, &szTmp[0], P6CHARCNT(szTmp)) );
printf( "KSL result 2: [ %s ]\n", p6ErrToStr(err2, &szTmp[0], P6CHARCNT(szTmp)) );
printf( "KSL result 3: [ %s ]\n", p6ErrToStr(err3, &szTmp[0], P6CHARCNT(szTmp)) );
}
} // namespace
// p6InitializeLoader() must be called before anything else can run.
//
int main(int argc,char *argv[])
{
P6ERR err = eOk;
if ( P6SUCCEEDED( err = P6EXAMPLES::CConsoleStream::createInstance( NULL, VALIDATECOMPTR( p6IDataStream, cpDataStream ))))
{
if ( P6SUCCEEDED( err = p6InitializeLoader( cpDataStream, 9, P6SCLF_ALLLOG )))
{
KMIP_BasicResponseTest( cpDataStream );
}
else printf("ERROR: Failed to initialize the loader [ %x ]\n", err );
}
else printf( "ERROR: Failed to create CConsoleStream [ %x ]\n", err );
return err;
}