Previous page

Next page

Locate page in Contents

Print this page

Implementing a Plug-in

This section goes step by step through a sample plug-in implementation and describes each step in detail. This should give you an understanding of how to implement the plug-in and its interfaces. An encryption engine used in this example is a simple XOR cipher. This is a simple algorithm that can be easily broken, so we use it as a demonstration only. When developing your own plug-in, you should use a stronger encryption algorithm according to your needs.

Include directives

A plug-in program must have the following include directives:

#include "PrlTypes.h"

#include "PrlErrors.h"

#include "PrlPluginClasses.h"

All of the above header files are provided in the Parallels Virtualization SDK. You have to make sure that your compiler can find them on your computer. See the Installation chapter for the default SDK installation location.

This sample program also uses the following standard includes:

#include <stdio.h>

#include <stdlib.h>

#include <memory.h>

#include <new>

Plug-in GUID

A plug-in must have its own unique ID (GUID) in order to be properly identified on the Parallels Service side. The following is an example of GUID declaration and initialization. You will have to generate a GUID for your own plug-in.

static PRL_GUID Obj = { 0x801ecba5, 0x909a, 0x42fc, { 0x81, 0x62, 0x1e, 0x7c, 0x51, 0x9e, 0x84, 0x6e } };

Components (classes) array

Declare and populate an array containing the list of GUIDs identifying the available interfaces. The array is terminated with PRL_CLS_NULL, which is a NULL GUID.

static PRL_GUID Classes[] = { PRL_CLS_BASE, PRL_CLS_ENCRYPTION, PRL_CLS_NULL };

Encryption block size

Define the encryption data block size for the XOR cipher.

#define BLOCK_SIZE (16)

Define the encryption interface

PrlCrypt is the interface that contains pointers to the data encryption functions. We will implement individual functions later in this example. Right now we will declare the component as a member of a structure. We will also add two more members to the structure to hold the initialization vector and the encryption key.

typedef struct _IXorCrypt

{

    PrlCrypt I;

    

    // Initialization vector

    PRL_UINT8 m_IV[BLOCK_SIZE];

    // Key

    PRL_UINT8 m_Key[BLOCK_SIZE];

} IXorCrypt;

Provide the plug-in description

Here we populate the plug-in information structure.

static const ICryptInfo PlugInfo =

{

    {

        (PRL_STR)"Sample",

        (PRL_STR)"(c)Sample",

        (PRL_STR)"XOR encryption engine",

        (PRL_STR)"This engine utilizes the XOR cipher",

        1, 0, 0,

        Obj

    },

    BLOCK_SIZE, BLOCK_SIZE

};

Free the memory occupied by the base interface

PrlPlugin is the base interface. It contains an important function that must be implemented. The function frees the memory occupied by the PrlPlugin structure. This has to be done because the caller will have no knowledge of how this structure was allocated. We implement it as a standalone function and will use a reference to it in the PrlPlugin structure declaration.

void PRL_CALL Release(PrlPlugin* _self)

{

    free(_self);

}

Initialize the encryption engine

In this step we implement a function that will initialize the encryption engine. The function prototype is defined in the PrlCrypt structure.

PRL_RESULT PRL_CALL Init(PrlCrypt* _self)

{

    IXorCrypt* I = (IXorCrypt*)_self;

  

    memset(I->m_IV, 0, BLOCK_SIZE);

    memset(I->m_Key, 0, BLOCK_SIZE);

    return PRL_ERR_SUCCESS;

};

Main encryption engine

In this step we create a function that implements the actual encryption algorithm.  The function uses a simple XOR encryption where a bitwise XOR operator is applied to a block of data using the specified key.  

static void XorFunc(PRL_UINT8_PTR a, PRL_UINT8_PTR b)

{

    PRL_UINT64* p1 = (PRL_UINT64*)a;

    PRL_UINT64* p2 = (PRL_UINT64*)b;

  

    p1[0] ^= p2[0];

    p1[1] ^= p2[1];

}

This function encrypts the provided data block.

PRL_RESULT PRL_CALL Encrypt(PrlCrypt* _self, PRL_VOID_PTR Data,

                       const PRL_UINT32 Size, const PRL_UINT8_PTR v)

{

    if ((Size % BLOCK_SIZE) ||

        !Data)

        return PRL_ERR_INVALID_ARG;

  

    IXorCrypt* I = (IXorCrypt*)_self;

    PRL_UINT32 Count = Size / BLOCK_SIZE;

    PRL_UINT8_PTR Cur = (PRL_UINT8_PTR)Data;

  

    for(PRL_UINT32 i = 0; i < Count; i++, Cur += BLOCK_SIZE)

    {

        XorFunc(Cur, I->m_Key);

        XorFunc(Cur, I->m_IV);

        if (v)

            XorFunc(Cur, v);

    }

    return PRL_ERR_SUCCESS;

}

This function decrypts the provided data block.

PRL_RESULT PRL_CALL Decrypt(PrlCrypt* _self, PRL_VOID_PTR Data,

                             const PRL_UINT32 Size, const PRL_UINT8_PTR v)

{

    if ((Size % BLOCK_SIZE) ||

        !Data)

        return PRL_ERR_INVALID_ARG;

    IXorCrypt* I = (IXorCrypt*)_self;

    PRL_UINT32 Count = Size / BLOCK_SIZE;

    PRL_UINT8_PTR Cur = (PRL_UINT8_PTR)Data;

  

    for(PRL_UINT32 i = 0; i < Count; i++, Cur += BLOCK_SIZE)

    {

        if (v)

            XorFunc(Cur, v);

            XorFunc(Cur, I->m_IV);

            XorFunc(Cur, I->m_Key);

    }

    return PRL_ERR_SUCCESS;

}

This function is used to set a key for the encryptor. The key size must be equal to or larger than the one specified in the PlugInfo structure (declared above).

PRL_RESULT PRL_CALL SetKey(PrlCrypt* _self, const PRL_UINT8_PTR Key)

{

    IXorCrypt* I = (IXorCrypt*)_self;

  

    if (!Key)

    {

        memset(I->m_Key, 0, BLOCK_SIZE);

        return PRL_ERR_SUCCESS;

    }

  

    memcpy(I->m_Key, Key, BLOCK_SIZE);

  

    return PRL_ERR_SUCCESS;

}

This function sets the initial initialization vector. The vector size must be equal to the one specified in the PlugInfo structure as the block size.

PRL_RESULT PRL_CALL SetInitIV(PrlCrypt* _self, const PRL_UINT8_PTR v)

{

    IXorCrypt* I = (IXorCrypt*)_self;

  

    if (!v)

    {

        memset(I->m_IV, 0, BLOCK_SIZE);

        return PRL_ERR_SUCCESS;

    }

  

    memcpy(I->m_IV, v, BLOCK_SIZE);

  

    return PRL_ERR_SUCCESS;

}

Functions to obtain the plug-in information

The first function obtains a reference to the structure containing the plug-in description.

PRL_RESULT PRL_CALL GetBaseInfo(PrlPlugin* _self, IPluginInfoPtr Info)

{

    (void)_self;

  

    if (!Info)

        return PRL_ERR_INVALID_ARG;

  

    *Info = PlugInfo.PluginInfo;

  

    return PRL_ERR_SUCCESS;

}

The second function obtains a reference to the structure that contains the plug-in description together with the data encryption key and block block sizes.

PRL_RESULT PRL_CALL GetInfo(PrlCrypt* _self, ICryptInfoPtr Info)

{

    (void)_self;

  

    if (!Info)

        return PRL_ERR_INVALID_ARG;

  

    *Info = PlugInfo;

  

    return PRL_ERR_SUCCESS;

}

The QueryInterface function

This function accepts the GUID of an interface and passes back a reference to the interface instance.

PRL_RESULT PRL_CALL QueryInterface(PrlPlugin* _self, PRL_GUID* Class, PRL_VOID_PTR_PTR _obj)

{

    if (!_self || !_obj)

        return PRL_ERR_INVALID_ARG;

  

    // Want to instantiate the base or encryption interface?

    if (memcmp(Class, &Classes[0], sizeof(PRL_GUID)) &&

        memcmp(Class, &Classes[1], sizeof(PRL_GUID)))

    {

        return PRL_ERR_OBJECT_NOT_FOUND;

    }

  

    // The interface pointer is the same for both objects

    *_obj = (PRL_VOID_PTR)_self;

  

    return PRL_ERR_SUCCESS;

}

Functions that must be exported

All exported functions must have C style declaration to properly resolve names at loading..

extern "C"

{

/*

* This function will be called immediately after the plug-in is loaded.

* You can put any code here that might be needed in your implementation.

* The function is optional.

*/

__attribute__((visibility("default"))) PRL_RESULT PRL_CALL PrlInitPlugin()

{

    return PRL_ERR_SUCCESS;

}

/*

* This function will be called immediately before the plug-in is unloaded.

* You can put any code here that might be needed in your implementation.

* The function is optional.

*/

__attribute__((visibility("default"))) PRL_RESULT PRL_CALL PrlFiniPlugin()

{

    return PRL_ERR_SUCCESS;

}

/*

* This function is used to obtain all available interfaces provided by the plug-in.

* The Number parameter is an interface number; the Uid and Classes parameters

* are object information that should be filled to caller.

*/

__attribute__((visibility("default"))) PRL_RESULT PRL_CALL PrlGetObjectInfo(PRL_UINT32 Number,

                                  PRL_GUID* Uid, PRL_GUID** InterfacesList)

{

    if (Number != 0)

         return PRL_ERR_OBJECT_NOT_FOUND;

  

    if (!Uid || !InterfacesList)

        return PRL_ERR_INVALID_ARG;

  

    *Uid = Obj;

    *InterfacesList = Classes;

  

    return PRL_ERR_SUCCESS;

}

/*

* This function is used to create a specified object and put a

  * reference to it  into the Out variable.

*/

__attribute__((visibility("default"))) PRL_RESULT PRL_CALL PrlCreateObject(PRL_GUID* Uid, PrlPlugin** Out)

{

    if (!Uid || !Out)

        return PRL_ERR_INVALID_ARG;

  

    if (memcmp(Uid, &Obj, sizeof(PRL_GUID)))

        return PRL_ERR_OBJECT_NOT_FOUND;

  

    IXorCrypt* I = (IXorCrypt*)malloc(sizeof(IXorCrypt));

  

    if (!I)

        return PRL_ERR_OUT_OF_MEMORY;

  

    // Cleanup memory

    memset(I, 0, sizeof(IXorCrypt));

  

    /*

     * Fill functions table. If you skip something, you'll get

     * SEGFAULT if there was no memset, or unpredictable errors

     * if function address is equal to NULL.

     */

    PrlPlugin* Base = (PrlPlugin*)I;

    Base->Release = &Release;

    Base->GetInfo = &GetBaseInfo;

    Base->QueryInterface = &QueryInterface;

  

    PrlCrypt* Crypt = (PrlCrypt*)I;

    Crypt->Init = &Init;

    Crypt->Encrypt = &Encrypt;

    Crypt->Decrypt = &Decrypt;

    Crypt->SetKey = &SetKey;

    Crypt->SetInitIV = &SetInitIV;

    Crypt->GetInfo = &GetInfo;

  

    *Out = (PrlPlugin*)I;

  

    return PRL_ERR_SUCCESS;

}

}