Protocol initialization
index
- About protocol initialization
- Requirements
- Passing protocol names example
- Start protocol example
About Protocol initialization
One plug-in (a DLL-file) is able to provide more than one protocol, these protocols need to be identified somehow. At first, when the plug-in gets loaded into the memory, the plug-in hands over a list of names with protocols this plug-in is providing. TV's server will create an unique number (ProtocolSession) for every protocol and will pass this number to the plug-in. The plug-in will be requested to load the plug-in . On success, this unique number will be able to used for the services (like data storage) TV's server has to offer.
Requirements
The plug-in should at least export the functions GetProtocolNames and StartProtocol.
GetProtocolNames function
The function GetProtocolNames needs to pass a list of names of protocols this plug-in is providing.
StartProtocol function
The function StartProtocol needs to initiate the protocol. A ProtocolSession, uniqe for every protocol, will be created and will be passed through this function, therefore it will be called for every protocol separately. This function should at least make a call to the following functions:
- SetProtocolInformation -- This function is required to call because this function will let you set the information about the protocol and will check if the API version is still supported.
- AcceptProtocol -- This function is required to activate the protocol and make it visible to the user.
Optionally it can call function (before calling the AcceptProtocol function):
- SetControlPanelOptions -- This function is optional and lets you add security-, menu- and user changeable-options to the control panel.
Passing protocol names example
This example will show how to implement the function GetProtocolNames. This example exports the protocol TCPIP and HTTP. Below there is an example of function StartProtocol.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tvsserver_base.h"
#include "tvs_redefine_api_names.h"
int GetNullTerminatedListSize(char* list);
char* supportedProtocols = "C_HTTP\0C_TCPIP\0\0";
/*This function should return a list of protocols it supports*/
int __stdcall GetProtocolNames(char *buf, int *len) {
int size;
if(len==0) return 0;
size = GetNullTerminatedListSize(supportedProtocols); //calculate the size of the list
//if the buffer is not big enough, return the size that is needed
if(*len==0 || *len<size) {
*len = size;
return 1;
}
//copy the list to the buffer and return
memcpy(buf, supportedProtocols, size);
return 1;
}
/*This function will calculate the size, in bytes, of a null-terminated list*/
int GetNullTerminatedListSize(char* list) {
int cnt=0;
int len;
if(list==0) return 0;
while(*list!=0) { //The terminating null byte is found, this is the end of the list
len = strlen(list);
list += len + 1; //add the size of the item +1 to get the next item
cnt += len + 1;
}
cnt++; //add the last zero byte
return cnt; //return the size of the list
}
Start protocol example
The next example will show how to implement the function
StartProtocol.
#include <stdio.h>
#include <string.h>
#include <winsock2.h>
#include "tvsserver_base.h"
#include "tvs_redefine_api_names.h"
#include <stdlib.h>
// Need to link with Ws2_32.lib
#pragma comment(lib, "ws2_32.lib")
#define HTTP_AMMOUNT_OF_SUB_OPTIONS 1
#define TCPIP_AMMOUNT_OF_SUB_OPTIONS 2
typedef struct MyTCPIPstruct{
int socket;
} MyTCPIPstruct;
typedef struct MyHTTPstruct{
char* serverPath;
} MyHTTPstruct;
ProtocolSession *http_psess;
ProtocolSession *tcpip_psess;
#define OPT_TCP_PORT 1
#define OPT_TCP_VERSION6 2
#define HTTP_AMMOUNT_OF_SUPPPORTED_COMMANDS 3
char* httpSupportedCommands[HTTP_AMMOUNT_OF_SUPPPORTED_COMMANDS] = {"GET", "POST", "HEAD"};
char* createProtocolSignatureFromArray(char* p_array[], int ammountItems);
int __stdcall StartProtocol(ProtocolSession *psess) {
ControlPanelOptions *cpo;
WORD wVersionRequested;
WSADATA wsaData;
int retv=0;
char* tempProtocolSignatureStructure=0;
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
wVersionRequested = MAKEWORD(2, 2);
if(!strcmp(psess->protocolName, "C_HTTP")) { //init the HTTP protocol
http_psess = psess;
tempProtocolSignatureStructure = createProtocolSignatureFromArray(httpSupportedCommands, HTTP_AMMOUNT_OF_SUPPPORTED_COMMANDS);
//Set the protocol type to PT_LABORER, make room for the MyHTTPstruct structure in the ListenSession and ClientSession and pass the supported API version.
if(!SetProtocolInformation(
psess,
PT_LABORER | PT_SUPPORT_PSIGN,
(ProtocolSignature*) tempProtocolSignatureStructure,
sizeof(MyHTTPstruct),
V_TV_SERVER_1)
)
return false;
//Create a ControlPanelOptions strcuture with room for 1 suboption.
cpo = (ControlPanelOptions*) malloc(
sizeof(ControlPanelOptions) +
sizeof(ControlPanelOption)*HTTP_AMMOUNT_OF_SUB_OPTIONS // +1 subOption
);
/*Empty memory and fill in the size, in bytes*/
memset(cpo, 0, sizeof(ControlPanelOptions) + sizeof(ControlPanelOption)*HTTP_AMMOUNT_OF_SUB_OPTIONS);
cpo->size = sizeof(ControlPanelOptions) + sizeof(ControlPanelOption)*HTTP_AMMOUNT_OF_SUB_OPTIONS;
//The name of this protocol, communicated to the user, will be "HTTP-plugin"
cpo->main.size = sizeof(ControlPanelOption);
cpo->main.optionName = "HTTP-C-plugin"; //the name of this protocol
//suboption number 1
cpo->subOptions[0].size = sizeof(ControlPanelOption);
cpo->subOptions[0].name = "opt1"; //identifier name used by the function GetPluginData and SavePluginData
cpo->subOptions[0].optionName = "Server-path"; //label of this option
cpo->subOptions[0].userValue = (UserValueOption*) malloc(sizeof(UserValueOption));
cpo->subOptions[0].userValue->type = TD_ASCII_STRING; //the pointer on defaultValue is a ascii string.
cpo->subOptions[0].userValue->defaultValue = "c:\\WWWroot\\"; //default value of this option
cpo->subOptions[0].userValue->defaultValueSize = strlen((char*) cpo->subOptions[0].userValue->defaultValue);
cpo->subOptions[0].flags = CPO_GEN_LISTENSESSION_OPTION; //a valid option for the listensession
SetControlPanelOptions(psess, cpo);
retv = AcceptProtocol(psess); //retv=false the protocol is rejected, retv=true the protocol is loaded
free(cpo->subOptions[0].userValue);
free(cpo);
} else if(!strcmp(psess->protocolName, "C_TCPIP")) { //init the TCP/IP protocol
tcpip_psess = psess;
if(!SetProtocolInformation(
psess, //ProtocolSession
PT_RECEIVER, //Set the type to receiver protocol
0, //No Protcol signature for for a PT_RECEIVER protocol
sizeof(MyTCPIPstruct), //In every ListenSession and ClientSession I want enough room in the data member for my structure
V_TV_SERVER_1) //This protocol supports API version 1
)
return false;
//Create a ControlPanelOptions strcuture with room for 1 suboption.
cpo = (ControlPanelOptions*) malloc(
sizeof(ControlPanelOptions) +
sizeof(ControlPanelOption)*TCPIP_AMMOUNT_OF_SUB_OPTIONS // +2 subOption
);
/*Empty memory and fill in the size, in bytes*/
memset(cpo, 0, sizeof(ControlPanelOptions) + sizeof(ControlPanelOption)*TCPIP_AMMOUNT_OF_SUB_OPTIONS);
cpo->size = sizeof(ControlPanelOptions) + sizeof(ControlPanelOption)*TCPIP_AMMOUNT_OF_SUB_OPTIONS;
//The name of this protocol, communicated to the user, will be "HTTP-plugin"
cpo->main.size = sizeof(ControlPanelOption);
cpo->main.optionName = "TCP/IP-C-plugin"; //the name of this protocol
//suboption number 1
cpo->subOptions[0].size = sizeof(ControlPanelOption);
cpo->subOptions[0].name = (char*) OPT_TCP_PORT; //identifier integer, instead of a name used by the function GetPluginData and SavePluginData
cpo->subOptions[0].optionName = "Port"; //label of this option
cpo->subOptions[0].userValue = (UserValueOption*) malloc(sizeof(UserValueOption));
cpo->subOptions[0].userValue->type = TD_SHORT_INT; //the defaultValue is an integer
cpo->subOptions[0].userValue->defaultValue = (void*) 80; //default value of this option
cpo->subOptions[0].userValue->defaultValueSize = 0;
cpo->subOptions[0].flags = CPO_GEN_LISTENSESSION_OPTION | GDPD_NAME_IS_INTEGER | CPO_GEN_LISTENSESSION_READONLY; //a valid option for the listensession, member name is an integer and cannot be modified on a running service
//suboption number 2
cpo->subOptions[1].size = sizeof(ControlPanelOption);
cpo->subOptions[1].name = (char*) OPT_TCP_VERSION6; //identifier integer, instead of a name used by the function GetPluginData and SavePluginData
cpo->subOptions[1].optionName = "Use version6"; //label of this option
cpo->subOptions[1].userValue = (UserValueOption*) malloc(sizeof(UserValueOption));
cpo->subOptions[1].userValue->type = TD_BOOLEAN; //the defaultValue is an boolean
cpo->subOptions[1].userValue->defaultValue = (void*) 0; //default value of this option is false
cpo->subOptions[1].userValue->defaultValueSize = 0;
cpo->subOptions[0].flags = CPO_GEN_LISTENSESSION_OPTION | GDPD_NAME_IS_INTEGER | CPO_GEN_LISTENSESSION_READONLY; //a valid option for the listensession, member name is an integer and cannot be modified on a running service
SetControlPanelOptions(psess, cpo);
//Startup the Socket stack
if(WSAStartup(wVersionRequested, &wsaData))
return 0;
//We will not make a call to SetControlPanelOptions and immediately call AcceptProtocol
retv = AcceptProtocol(psess); //retv=false the protocol is rejected, retv=true the protocol is loaded
free(cpo->subOptions[0].userValue);
free(cpo);
}
//Free the ProtocolSignatureStructure if allocated
if(tempProtocolSignatureStructure)
free(tempProtocolSignatureStructure);
return retv;
}
char* createProtocolSignatureFromArray(char* p_array[], int ammountItems) {
int bufSize, i, tmpLen;
char* tmpBuf, *retBuf;
//Calculate the size of every integer that will contain the size of the command (that last one is zero)
bufSize = (ammountItems + 1) * sizeof(int);
//Calculate the size of the buf members for the ProtocolSignature structure
for(i=0; i<ammountItems; i++) {
bufSize += strlen(p_array[i]);
}
//Allocate size for the ProtocolSignature structure
retBuf = (char*) malloc(bufSize);
tmpBuf = retBuf;
//Copy every item with there size to the array
for(i=0; i<ammountItems; i++) {
tmpLen = strlen(p_array[i]);
*(int*)tmpBuf = tmpLen;
tmpBuf += sizeof(int);
strcpy(tmpBuf, p_array[i]);
tmpBuf += tmpLen;
}
*(int*)tmpBuf = 0;
return retBuf;
}