Komodia's Redirector COM framework guide

From Komodia
Jump to: navigation, search

This page covers the programming aspects of Komodia's Redirector product when using the COM interface to extend the Redirector’s functionality, this option is good for you if you don't have experience with C/C++ or want to control the product directly from your application.

There are two extra options to extend the Redirector:

  • Modify the source code directly inside the class: CClientCustomizations (it's similar to the DLL interface), you also need to uncomment this line: #define SERVICE_CUSTOM_EXTENSIONS inside KomodiaProduct.h
  • Use the Komodia's Redirector DLL framework guide to extend the Redirector via a C/C++ DLL.

Overview

To work with the COM interface you need implement the interface PCProxyLib.IParentalControl and tell the Redirector via the API what is your interface, once your interface is registered you will start to receive traffic and be able to control it.

High level overview

The COM framework allows you to receive networking events from the SDK and do processing on them, the flow is:

  1. Register your COM interface.
  2. Receive NewConnection event (called per each new connection).
  3. Tell the SDK if to allow/block the connection, if allow, decide which data processing you want (raw, HTTP Parser, both, none).
  4. Receive data via relevant methods, depending on the processing type.
  5. Receive ConnectionClose when the connection was closed.
  6. End of work, remove your COM interface.

API

SetInterface

SetParentalInterface(IParentalControl* pInt,
		     long lNewConnectionsOnly,
		     long lDisconnectOnError);

This function is used to tell the Redirector what is the class you are going to use:

pInt - The interface class that will receive all the data.

lNewConnectionsOnly - This will tell the Redirector if you want to receive connections that started before you sent your interface to the Redirector, sending true will mean that you will receive connections without seeing the function NewConnection first.

lDisconnectOnError - This will tell the Redirector if to stop calling the interface in case there's an error using it.

How to register the interface:

Dim dt As PCProxyLib.DataController = new PCProxyLib.DataController
dt.SetInterface(your interface class,true,true)

IDataContainer

This class is used to transfer data between the SDK to the application that uses COM, the reason this class is used is because there's a limitation on the size of a string you can transfer using COM.

GetSize

GetSize([out,retval]long* lSize);

Get the size in bytes of the data.

RequestString

RequestString(long lPosition,
	      long lSize,
	      [out,retval]BSTR* pData);

This functions gets the data in a string format.

Parameters

lPosition - Where to start getting the data.

lSize - Size of data to request.

pData - Data received.

If you send both lPosition and lSize as zero the function will give you the full string, unless the string size is greater then 100k, in that case it will throw an error.

RequestString64

RequestString64(long lPosition,
	        long lSize,
	        [out,retval]BSTR* pData);

This functions gets the data in a base64 format, this is used in case the data is binary.

Parameters

lPosition - Where to start getting the data.

lSize - Size of data to request.

pData - Data received (Data received is usually twice the size because of the base64 encodings)

If you send both lPosition and lSize as zero the function will give you the full base64 string, unless the string size is greater then 100k, in that case it will throw an error.

SetDataFromString

SetDataFromString(BSTR bData);

This functions will change the data in the container and set it to the string you provide.

SetDataFromString64

SetDataFromString64(BSTR bData);

This functions will change the data in the container and set it to the data you provide using Base64.

ReplaceData

HRESULT ReplaceData(long lPosition,
                    BSTR bData);

This function will replace a data inside the string, the function only replaces existing data, if it's in the end of the data, it will not do an insert.

Parameters

lPosition - Position to start the replace.

bData - String to replace with.

InjectData

HRESULT InjectData(long lPosition,
                   BSTR bData);

This function will insert a data inside the string and push the existing data after the inserted string.

Parameters

lPosition - Position to start the insert.

bData - String to insert.

RemoveData

RemoveData(long lPosition,
           long lSize);

This function will delete data from the string and move the remaining data back.

Parameters

lPosition - Position to start the delete.

lSize - Number of characters to delete.

SearchString

SearchString(BSTR bStringToSearch,
             long lIgnoreCase,
             [out,retval]long* pPosition);

This function searches for a string inside the data.

Parameters

bStringToSearch - String to search for.

lIgnoreCase - set to 1 if you want the search to be case insensitive.

pPosition - Position of string found, -1 for not found.

SearchHTMLString

SearchHTMLString(BSTR bStringToSearch,
                 [out,retval]long* pPosition);

This function searches for a html string inside the data (it will ignore formatting that is legal to HTML, for example if the text contains < head >, searching for <head> will find it as well, it is case insensitive by default.

Parameters

bStringToSearch - String to search for.

pPosition - Position of string found, -1 for not found.

SearchStringPosition

SearchStringPosition(BSTR bStringToSearch,
                     long lIgnoreCase,
                     long lStartFrom,
                     [out,retval]long* pPosition);

This function searches for a string inside the data starting at a specific position.

Parameters

bStringToSearch - String to search for.

lIgnoreCase - set to 1 if you want the search to be case insensitive.

lStartFrom - Position to start the search from.

pPosition - Position of string found, -1 for not found.

SearchHTMLStringPosition

SearchHTMLStringPosition(BSTR bStringToSearch,
                         long lStartFrom,
                         [out,retval]long* pPosition);

This function searches for a html string inside the data starting at a specific position (it will ignore formatting that is legal to HTML, for example if the text contains < head >, searching for <head> will find it as well, it is case insensitive by default).

Parameters

bStringToSearch - String to search for.

lStartFrom - Position to start the search from.

pPosition - Position of string found, -1 for not found.

SaveToFile

SaveToFile(BSTR bFileName,
           long lAppend,
           long lInThread,
           [out,retval]long* pResult);

This function saves the data into a file.

Parameters

bFileName - File name (full path required)

lAppend - 0 To overwrite the file, 1 to append to it

lInThread - 0 operation will be from current thread and execution in app will resume when file was written, 1 to execute it from a different thread, in this mode you don't have a way to know when the write have ended.

pResult - Result, 0 for failure, 1 for success.

SearchRegex

SearchRegex(BSTR bStringToSearch,
            [out,retval]long* pPosition);

This function searches for a regex inside the data.

Parameters

bStringToSearch - Regex to search for.

pPosition - Position of string found, -1 for not found.

SearchRegexPosition

SearchRegexPosition(BSTR bStringToSearch,
                    long lStartFrom,
                    [out,retval]long* pPosition);

This function searches for a regex inside the data starting at a specific position.

Parameters

bStringToSearch - Regex to search for.

lStartFrom - Position to start the search from.

pPosition - Position of string found, -1 for not found.

IParentalControl

This is the class you need to implement, the language you use will implement the skeleton for you, you just need to embedd your code.

How to implement the class in VB.Net:

Implements PCProxyLib.IParentalControl

NewConnection

NewConnection(long lConnectionID,
	      long lFromEncryption,
	      long lPID,
	      BSTR bProcessName,
              BSTR bUsername,
	      BSTR bDomain,
	      BSTR bIPString,
	      [in,out]long* lIP,
	      [in,out]long* lPort,
	      [in,out]long* lSSL,
	      [in,out]long* lProxyModified,
	      [in,out]IProxyType* lProxyType,
	      [in,out]long* lProxyIP,
	      [in,out]long* lProxyPort,
	      [in,out]BSTR* pUsername,
	      [in,out]BSTR* pPassword,
	      BSTR bDNSEntry,
	      [in,out]FilteringType* pFilteringType,
	      [in,out]BSTR* bStringToStore,
	      [out,retval]long* lAllow);

This function is called whenever a new connection was made to PCProxy.

Parameters

The parameters are:

lConnectionID – A unique ID that will be used to identify the current socket connection, it's important to note that for HTTP connections you can have multiple requests on the same connection.

lFromEncryption – In case the data was originally encrypted with SSL but decrypted with the SSL decryptors, this flag will be true.

lPID - Process ID of the application that created the connection.

bProcessName - Name of the application that created the connection.

bUsername - The username of the application that created the connection.

bDomain - The domain of the application that created the connection.

bIPString - The destination IP that this session is trying to connect to, this in in form of a string, for example: "127.0.0.1".

lIP – The destination IP that this session is trying to connect to. The programmer can change this value and the session will connect to the new IP.

lPort – The destination port that this session is trying to connect to. The programmer can change this value and the session will connect to the new port.

lSSL – Outputs this stream as SSL data (you will probably also need to change the port to 443).

Proxy variables

lProxyModified - Set this flag to true in case you set or modified the proxy settings.

typedef enum _IProxyType
{
    iptHTTP,
    iptHTTPConnect,
    iptHTTPConnectSSL,
    iptHTTPHybrid,
    iptHTTPHybridSSL,
    iptSocks4,
    iptSocks5,
    iptHTTPSSL,
    iptSocks5TCPUDP,
    iptRedirect,
    iptRedirectBypass,
    iptSSLSubst,
    iptNone
} IProxyType;


The proxy variables are in/out which means they will contain the current settings and you will be able to change them:

lProxyType - This will contain the type of proxy (if proxy is set), and you can change it. (you can see description of each proxy type here: Komodia's Redirector installation guide#Proxy_type.

lProxyIP - The IP of the proxy.

lProxyPort - The port of the proxy.

pUsername - Proxy username if applicable.

pPassword - Proxy password if applicable.

More parameters

bDNSEntry - Incase you have the DNS Hijacker, this will contain the DNS entry of the connection.

typedef enum _FilteringType
{
    ftNothing,
    ftNothingDisableParental,
    ftDataOnly,
    ftParentalOnly,
    ftParentalTextOnly,
    ftDataAndParental,
    ftParentalOnlyMinimal
} FilteringType;

pFilteringType - Type of filtering you want for this connection:

  • ftNothing - You don't want to receive any more data on this connection.
  • ftNothingDisableParental - You don't want to receive any more data on this connection and if you have the HTTP Parser, it will disable it for this connection.
  • ftDataOnly - You want to receive only the data without doing HTTP parsing.
  • ftParentalOnly - You want to receive only the parsed HTTP data.
  • ftParentalTextOnly - You want to receive only text based parsed HTTP data (html files).
  • ftDataAndParental - You want to receive both raw data and parsed HTTP data (if possible).
  • ftParentalOnlyMinimal - Same as ftParentalOnly, but will not send the raw header and full URL to conserve CPU.

bStringToStore - You can store any info you want in this string and you will receive it all across the current connection.

On Windows 8/8.1 when using a WFP, in case there's a cascading proxy redirect, there may be an option to get the original application (on top of the proxy process that was redirected), and then bStringToStore contain that process name (you can still modify the string as you would when it was empty).

lAllow - Do you want to allow this connection or block it.

Samples

Samples are written in VB.Net

Redirect to a proxy
lProxyIP = System.Net.IPAddress.Parse("127.0.0.1").Address 'On C#: lProxyIP = (int)System.Net.IPAddress.Parse("127.0.0.1").Address;
lProxyPort = 1080
lProxyModified = 1
lProxyType = PCProxyLib._IProxyType.iptSocks5

This will redirect the connection to a SOCKS5 proxy located at: 127.0.0.1:1080

In VB.net there may be issues with converting signed to unsigned (for the IP address), this is a code that can do the conversion:

Dim bb() As Byte = System.BitConverter.GetBytes(System.Net.IPAddress.Parse("127.0.0.1").Address)
Dim Myint = System.BitConverter.ToInt32(bb, 0)
lProxyIP=Myint

DataBeforeSend

DataBeforeSend(long lConnectionID,
	       [in,out]IDataContainer** pData,
	       [in,out]long* lTerminateSession,
	       [in,out]long* lNewBufferToReceive,
	       [in,out]BSTR* bStringToStore,
	       [out,retval]DataActionType* pAction);

This function is called whenever there's a new outgoing data, and you set the filtering to: ftDataOnly

Parameters

The parameters are:

lConnectionID – A unique ID that will be used to identify the current socket connection, it's important to note that for HTTP connections you can have multiple requests on the same connection.

pData - The outgoing data, you can also modify this data.

lTerminateSession – Set to 1 to terminate the session when this function exits (if you don't want the data to be sent, you need to clear it)

lNewBufferToReceive – Takes the contents of the new data and sends it to the application that initiated the session (used mostly for redirects).

bStringToStore - This string is to save custom data across the connection, you can store any data you want inside.

typedef enum _DataActionType
{
    naAllowData,
    naDataChanged
} DataActionType;

pAction - Action to perform:

  • naAllowData - Data sent as it.
  • naDataChanged - Indicates you have modified the data.

DataBeforeReceive

DataBeforeReceive(long lConnectionID,
	          [in,out]IDataContainer** pData,
	          [in,out]long* lTerminateSession,
	          long lServerClosed,
	          [in,out]BSTR* bStringToStore,
	          [out,retval]DataActionType* pAction);

This function is called whenever there's a new incoming data, and you set the filtering to: ftDataOnly

Parameters

The parameters are:

lConnectionID – A unique ID that will be used to identify the current socket connection, it's important to note that for HTTP connections you can have multiple requests on the same connection.

pData - The outgoing data, you can also modify this data.

lTerminateSession – Set to 1 to terminate the session when this function exits (if you don't want the data to be received, you need to clear it).

lServerClosed – When this flag is set, it means that the remote server closed the connection and at this stage you have last chance to send data to the client side before the Redirector closes the connection.

bStringToStore - This string is to save custom data across the connection, you can store any data you want inside.

typedef enum _DataActionType
{
    naAllowData,
    naDataChanged
} DataActionType;

pAction - Action to perform:

  • naAllowData - Data sent as it.
  • naDataChanged - Indicates you have modified the data.

ConnectionClosed

ConnectionClosed(long lConnectionID,
		 long lError,
		 BSTR bStringToStore);

This function is called when the connection was closed, and you set the filtering to: ftDataOnly or ftParentalOnly

Parameters

The parameters are:

lConnectionID – A unique ID that will be used to identify the current socket connection, it's important to note that for HTTP connections you can have multiple requests on the same connection.

lError - If the connection was closed because of an error, this will contain the Winsock error code.

bStringToStore - This string is to save custom data across the connection, you can store any data you want inside.

HTTP Parser

The following functions are part of the HTTP Parser.

Results enum

This enum is used to define behavior with the data methods.

typedef enum _ActionType
{
    atNothing=0,
    atBlock,
    atRedirect,
    atModifiedHeader,
    atModifyBody,
    atModifyBodyAndHeader,
    atHTMLInString,
    atDontDoParental,
    atDontProcessPost,
    atDontDoParentalAndDontProcessPost,
    atSkip,
    atModifyBodySDKAdjustHeader,
    atBuildButDontAggregate,
    atDefer,
    atModifyDontProcessPostPayload,
    atDontDoParentalAndDontProcessPostModify
} ActionType;

NewRequest

NewRequest(long lConnectionID,
	   [in,out]IDataContainer** pHeader,
	   BSTR bHeaderNoPost,
	   BSTR bHost,
	   BSTR bURL,
	   BSTR bFullRequest,
	   long lPartialPost,
	   [in,out]BSTR* bRedirectTo,
	   [in,out]BSTR* bStringToStore,
	   [out,retval]ActionType* pAction);

This function is called whenever there's a new HTTP request, and you set the filtering to: ftParentalOnly

The function can be called twice per request in case of a post, if the POST data is fragmented then the function will be called and will give your the opportunity to tell the SDK not to accumulate the POST data.

Parameters

The parameters are:

lConnectionID - A unique ID that will be used to identify the current socket connection, it's important to note that for HTTP connections you can have multiple requests on the same connection.

pHeader - The request header data, can be modified, in case the request is post and lPartialPost==0 then the post data will come in this parameter right after the header, the post data will start right after \r\n\r\n that terminates the header.

bHeaderNoPost - Contains the header, in case of a POST, it will not contain the POST data.

bHost - Host field from the request.

bURL - URL part of the request (i.e. /somefolder/somefile.php)

bFullRequest - Full URL request (i.e. www.komodia.com/somefolder/somefile.php)

lPartialPost - If set to 1, it means this is a POST and the full data didn't arrive yet.

bRedirectTo - If you want to redirect the request to another page, set this variable to that page (i.e. http://www.google.com)

bStringToStore - This string is to save custom data across the connection, you can store any data you want inside.

pAction - Action to perform: (this tells the SDK how to process the data)

  • atNothing - Do nothing, data will pass normally, in case of a POST, it will be accumulated.
  • atBlock - Block this request, the data will not be send, browser will timeout or try again.
  • atRedirect - Redirect the browser to the URL specified at bRedirectTo.
  • atModifiedHeader - Indicate the header was modified.
  • atDontDoParental - For this request, turn off the HTTP Parser, this will apply to the receive part, in case of POST, it will accumulate.
  • atDontProcessPost, - For this request, turn off the HTTP Parser, this will apply to the send part only, in case of POST.
  • atDontDoParentalAndDontProcessPost - Turn HTTP Parser off for both request and reply.
  • atSkip - Allow the SDK to decide which action to take (the SDK will only try to accumulate minimal amount of data needed).
  • atModifyDontProcessPostPayload - For this request, modify the POST header and turn off the HTTP Parser for the POST payload.
  • atDontDoParentalAndDontProcessPostModify- For this request, modify the POST header and turn off the HTTP Parser for the POST payload and POST reply.
Samples

Samples are written in VB.Net

Redirect a site

Redirect the site cnn.com to google.com:

If (LCase(bFullRequest).IndexOf("cnn.com"))<>-1 then
    bRedirectTo="http://www.google.com"
    NewRequest=PCProxyLib._ActionType.atRedirect
Else
    NewRequest=PCProxyLib._ActionType.atSkip
End If

NewReply

NewReply(long lConnectionID,
	 BSTR bRequestHeader,
	 BSTR bFullRequestURL,
	 BSTR bReplyHeader,
	 BSTR bContentType,
	 long lReplyOnly,
	 [in,out]IDataContainer** pReplyHeader,
	 [in,out]IDataContainer** pReplyBody,
	 [in,out]BSTR* bRedirectTo,
	 [in,out]BSTR* bStringToStore,
	 [out,retval]ActionType* pAction);

This function is called whenever there's a new HTTP reply, and you set the filtering to: ftParentalOnly

The function can be called twice per reply in case the data was big and didn't arrive in one chunk, if the data is fragmented then the function will be called and will give your the opportunity to tell the SDK not to accumulate the data, in case you don't need to, usually when it's a big movie or a file.

Parameters

The parameters are:

lConnectionID - A unique ID that will be used to identify the current socket connection, it's important to note that for HTTP connections you can have multiple requests on the same connection.

bRequestHeader - Contains the request header, in case of a POST, it will not contain the POST data.

bFullRequestURL - Full URL request (i.e. www.komodia.com/somefolder/somefile.php)

bReplyHeader - Header of the reply.

bContentType - Content type of the reply.

lReplyOnly - If the data was fragmented, and this reply contains the header only, this flag is set to 1.

pReplyHeader - The reply header data, can be modified.

pReplyBody - The reply data (will be set only if lReplyOnly is zero), can be modified.

bRedirectTo - If you want to redirect the request to another page, set this variable to that page (i.e. http://www.google.com)

bStringToStore - This string is to save custom data across the connection, you can store any data you want inside.

pAction - Action to perform: (this tells the SDK how to process the data)

  • atNothing - Do nothing, data will pass normally, in case of a big reply, it will be accumulated.
  • atBlock - Block this request, the data will not be send, browser will timeout or try again.
  • atRedirect - Redirect the browser to the URL specified at bRedirectTo.
  • atModifiedHeader - Indicate that the reply header was modified.
  • atModifyBody - Indicate that the reply body was modified (you are responsible to adjust the HTTP header)
  • atModifyBodyAndHeader - Indicate that the reply header and body was modified.
  • atHTMLInString - You want to reply with a HTML data that was stored in bRedirectTo (the SDK will build the header).
  • atDontDoParental - For this reply only, turn off the HTTP Parser.
  • atSkip - Allow the SDK to decide which action to take (the SDK will only try to accumulate minimal amount of data needed).
  • atModifyBodySDKAdjustHeader - This tells the SDK to adjust the header size field base on a modified body you have provided.

HTTP 101 response code

You can read here: Komodia's Redirector DLL framework guide#HTTP 101 response code on how the HTTP Parser manages 101 response code.

How to get HTML data

The HTTP Parser is built so you control which data to "hold" and which not to, to allow faster load rate, the sequence described here shows how to control the retrieval of HTML data:

  1. NewRequest being called, indicating the browser made a web request, at this point you can either:
    1. Redirect it into another address using the atRedirect option.
    2. Modify the header using the: atModifiedHeader, atHTMLInString options.
    3. If you decide you don't want to inspect this request's reply, you can use: atDontDoParental, atDontDoParentalAndDontProcessPost.
    4. If you do want to inspect the request's reply you need to return the flag atNothing.
  2. NewReply being called, if the payload is big, you will only get the headers and have the flag lReplyOnly==1, at this point you can:
    1. Redirect it into another address using the atRedirect option.
    2. Modify the header and/or body using the: atModifiedHeader, atHTMLInString, atModifyBodyAndHeader options.
    3. If you decide you don't want to inspect this reply, you can use: atDontDoParental.
    4. If you do want to inspect the request's reply you need to return the flag atNothing.
  3. NewReply being called, with full payload, and the flag lReplyOnly==0, at this point you can:
    1. Redirect it into another address using the atRedirect option.
    2. Modify the header and/or body using the: atModifiedHeader, atHTMLInString, atModifyBodyAndHeader options.
    3. Let it go to the browser using thr atNothing flag.

It's possible to only receive one reply with lReplyOnly==0 in case the payload did manage to fit in a single call.

COM errors

The SDK will treat COM errors according to the parameter in SetInterface:

  1. lDisconnectOnError==0 - SDK will keep passing data to the COM interface, even if there are delays or errors on application side, if application is hanged or slowed down, this will affect network speed.
  2. lDisconnectOnError==1 - SDK will remove interface to application on first COM error and will not send anymore data, this is to prevent situation where the application hangs or slows down.

Detecting COM interface removal

To detect the removal of your COM interface (only when lDisconnectOnError==1), you need to poll the COM method GetInstaceID, once the value is changed it means that your COM interface was removed and you need to register it again, preferably a new class.

Interop

The SDK itself does not need .Net to operate, but when using VB.Net or C# to use the COM framework, the compiler generates Interop file for the COM library (it's a .Net requirement), and you might want to control the version of the Interop for compatability reasons.

  1. Set the .Net version at your project setting.
  2. Some VS versions are bugged and can't create lower version Interop, then you should resort to earlier VS versions just to create the Interop (http://stackoverflow.com/questions/2659738/visual-studio-2010-tlbimp-generates-net-4-0-interops-in-2-0-projects)

Common pitfalls

Multi threading

The callbacks calls are multi threaded, make sure that non thread safe code is protected with Mutex or Critical section.

Not understanding what lReplyOnlydoes

lReplyOnly is the core of the HTTP reply processing, not using it will often lead to problems of not seeing HTTP responses.

Modifying HTTP body size

When modifying the HTTP body size, it must be reflected in the header, this can be done by either using a flag that tells the SDK to adjust the header automatically (atModifyBodySDKAdjustHeader), or modify the header in the DLL and use a flag that indicates that the header has been modified (atModify).

Spending too much time inside callbacks

The COM callbacks should not spend more then five seconds inside a callback, more then that can cause unexpected behavior (the true safe limit is 20 seconds, after 5 seconds inside a callback warnings will be written to the log)

COM client failing or errors

Incase of an error or freeze in the client, all traffic will be blocked, the solution can be to set the interface with lDisconnectOnError==1, this will mean that when the COM client freezes/errors out, the SDK removes the interface and the client will need to re register itself to get data.