|
Although the Cisco IOS Gatekeeper provides many functions, there may be the occasion when additional function is desired or needed. For example, an organization may require additional authentication functions, need to implement specific policy controls, or want to use Internet call waiting.
The Gatekeeper Transaction Message Protocol (GKTMP) and the Gatekeeper application programming interface (API) were developed to allow communication between the Cisco IOS Gatekeeper and an external application.
Using the GKTMP and the Gatekeeper API, organizations can supplement the functions of the Cisco IOS Gatekeeper with their own external application.
As part of its normal function, a gatekeeper receives certain RAS registration, admission, location, resource, and disengage messages from H.323 endpoints. Typically, the gatekeeper processes these messages and responds to the request. However, with the Cisco IOS Gatekeeper, the GKTMP, and the Gatekeeper API, you can supplement or offload the processing of the request to an external application.
In general, the process works as follows:
1. You establish triggers for each external application on the Cisco IOS Gatekeeper. These triggers are based on RAS tags and values.
2. When the Cisco IOS Gatekeeper receives a RAS message from an H.323 endpoint, it compares the message to the triggers.
3. If there is a match, the Cisco IOS Gatekeeper repackages the contents of the RAS message and sends it to the appropriate external application.
4. The Gatekeeper API decodes the byte stream and creates a usable C structure.
5. The external application processes the data and sends the results back to the Gatekeeper API.
6. The Gatekeeper API encodes the response message and sends the data to the Cisco IOS Gatekeeper.
7. The Cisco IOS Gatekeeper performs any additional processing, if necessary, and forwards the results to the requesting H.323 endpoint.
The interaction between the Cisco IOS Gatekeeper and the external application is completely transparent to the H.323 endpoint.
Communication between the Cisco IOS Gatekeeper and the external application is over a TCP connection through the Gatekeeper API. Multiple Cisco IOS Gatekeepers can be configured on a single IOS router as logical gatekeepers. The same TCP connection can be used by all the logical Cisco IOS Gatekeepers. The individual Cisco IOS Gatekeepers are identified by their gatekeeper IDs. This ID is included in all messages that the Cisco IOS Gatekeeper sends to the external application and in all responses that the external application sends back to the Cisco IOS Gatekeeper. If there are different external applications running on the same host, messages to the different external applications can also be multiplexed on the same TCP connection. The external applications are identified by their server IDs.
By default, the Cisco IOS Gatekeeper does not forward any RAS messages to any external applications. If an application is interested in receiving certain RAS messages, it must register this interest with the Cisco IOS Gatekeeper. To determine which RAS messages the Cisco IOS Gatekeeper forwards to the external application, you can specify trigger parameters. If the Cisco IOS Gatekeeper receives a message that satisfies the specified trigger conditions, the message is forwarded to the external application.
If multiple trigger conditions are specified in a single registration message, the Cisco IOS Gatekeeper treats the trigger conditions as "OR" conditions. In other words, if a RAS message received by the GateKeeper meets any of the trigger conditions the message is sent to the external application.
Trigger conditions are optional. If the Cisco IOS Gatekeeper receives a registration that contains no trigger conditions, then it will forward all messages of the specified RAS message type to the external application.
If the Cisco IOS Gatekeeper has a registration for a RAS message type and receives another registration for the same RAS message from the same external application with the same priority, the Cisco IOS Gatekeeper will use the new registration and discard the previous one. The Cisco IOS Gatekeeper allows registrations for the same RAS message type with the same priority from multiple servers.
To indicate that the external application is no longer interested in a message, it must unregister its interest. The contents of the unregistration message must match that of the corresponding registration message before the trigger can be removed.
A Cisco IOS Gatekeeper can be statically (through a command line interface) or dynamically (through the Gatekeeper API) configured with trigger parameters.
Statically configured triggers are established on the router using IOS commands. To configure triggers using the IOS command line, do the following:
Step 1 Access the Cisco IOS Gatekeeper configuration mode. Enter the following command:
gatekeeper
Step 2 Enter the trigger configuration mode and specify the RAS message type for the trigger. Enter the following command:
server trigger { arq | lcf | lrj | lrq | rrq | urq | rai | drq } gkid priority
server-id server-ip_address server-port
Step 3 If the trigger is to send qualifying messages on a notification-only basis, enter the following command:
info-only
Step 4 If you want to limit the qualifying messages based on the destination information, enter the following command:
destination-info {e164 | email-id | h323-id} value
You can repeat this command to enter multiple destinations. This command cannot be used with an RAI message trigger.
Step 5 If you want to limit the qualifying messages based on the redirect reason, enter the following command:
redirect-reason value
You can repeat this command to enter multiple redirect reasons. This command cannot be used with an RAI message trigger.
Step 6 If you want to limit the qualifying messages based on the remote extension address, enter the following command:
remote-ext-address value
You can repeat this command to enter multiple remote extension addresses.
Step 7 If you want to limit the qualifying messages based on the endpoint type, enter the following command:
endpoint-type value
You can repeat this command to enter multiple endpoint types. This command cannot be used with a DRQ message trigger.
Step 8 If you want to limit the qualifying messages based on the supported prefix, enter the following command:
supported-prefix value
You can repeat this command to enter multiple supported prefixes. This command cannot be used with a DRQ message trigger.
Step 9 When you have specified all the parameters for this trigger, exit trigger submode by entering the following command:
exit
Step 10 Repeat steps 2 through 9 for each trigger that you want to define.
Step 11 If you want to change the server timeout value for triggers, enter the following command:
timer server timeout value
To remove a trigger, use the no server trigger command. To temporarily suspend a trigger, enter the trigger configuration mode, as described in step 2 and enter the shutdown subcommand.
For more information about the IOS commands for configuring triggers, see "GKTMP Commands".
Dynamically configured triggers are established using the Gatekeeper API and the GKTMP trigger registration messages.
1. The external application creates a trigger and sends it to the Cisco IOS Gatekeeper using the WriteRegisterMessage API function. The triggers are sent in the format for trigger registration messages as prescribed by the GKTMP.
2. In response, the Cisco IOS Gatekeeper sends a message back that indicates whether the registration request has been accepted.
You must send a separate registration message for each message type that you want to be sent to the external application. If you send a registration message that does not contain any trigger definitions, all messages of the specified type will be sent to the external application.
Dynamically configured triggers are removed using the WriteUnregisterMessage API function and GKTMP trigger unregistration messages. Again, the response from the Cisco IOS Gatekeeper indicates whether the unregistration request has been accepted.
There are two API functions that can be used in the dynamic configuration of triggers:
The format of the GKTMP registration/unregistration request and response messages is as follows:
Message line
Message header line 1
Message header line 2
Message header line x
Message body line 1
Message body line 2
Message body line x
For more information about the format of trigger registration and unregistration messages, see "GKTMP Messages".
With dynamically configured triggers, the external application establishes a TCP connection to the gatekeeper and registers its interest in any of the RAS message types. The external application should then leave the connection open for receiving such messages and for sending its responses. If the external application closes the connection, its registrations are considered cancelled. The Cisco IOS Gatekeeper will not attempt to re-establish the connection.
In the following example the trigger registration request indicates that the Cisco IOS Gatekeeper should forward to the external application any RRQ messages from a voice gateway or a gateway with a supported prefix of 1# or 2#:
REGISTER RRQ
Version-id: 100
From: server-12
To: gk-dallas1
Priority: 20
Notification-Only:
Content-Length: 29
t=voice-gateway
p=1#
p=2#
Within a trigger, certain wildcard characters are allowed for an alias-address field that contains an E.164 address. Trigger criteria for E.164 alias-addresses can include trailing wildcard characters as follows:
Examples of legal E164 address patterns are:
1800....... | The digits 1800 followed by seven characters. |
011* | The digits 011 followed by any number of characters. |
Examples of illegal E164 address patterns are:
...4567 | Wildcard characters must be used as trailing characters. They cannot be used at the beginning of a field. |
4802*2 | Wildcard characters cannot be used within a field. In this case, the asterisk is interpreted as a literal character. |
If the application needs to be aware of messages but will not be preforming any processing of the message, you can indicate that the messages should be forwarded on a notification-only basis. If "notification-only" present in a GKTMP registration message (which means that notification-only is set to true at the API), the Cisco IOS Gatekeeper will forward messages that meet the trigger criteria but will not expect a response. If notification-only is not present (which means that notification-only is set to false at the API), the Cisco IOS Gatekeeper will forward messages that meet the trigger criteria and await a corresponding RESPONSE message from the external application.
This header line is typically used for REQUEST RRQ and REQUEST URQ messages, so that the Cisco IOS Gatekeeper can populate an external application's registration database.
When the Cisco IOS Gatekeeper receives an RAS message that meets the specified trigger conditions, it packages the contents of the fields of the RAS message into the message body of a GKTMP REQUEST message. When the external application receives a request, it must package the response into the message body of a GKTMP RESPONSE message.
The GKTMP specifies formats for exchanging the following types of RAS messages:
The URQ, RAI, and DRQ messages are issued as a request from the Cisco IOS Gatekeeper, but do not have a corresponding response. Other messages (RCF, RRJ, ACF, and ARJ) are sent only as responses from the external application.
The general format of the GKTMP RAS messages is:
Message line
Message header line 1
Message header line 2
Message header line x
Message body line 1
Message body line 2
Message body line x
For more information about the format of GKTMP RAS messages, see "GKTMP Messages".
How the external application processes requests from the Cisco IOS Gatekeeper depends on the type of RAS message and how the external application has been configured to respond.
When the external application receives a REQUEST xRQ message from the Cisco IOS Gatekeeper it must take one of the following actions:
An LCF message is sent by a peer gatekeeper to confirm the location of a destination endpoint in its zone. You can configure the Cisco IOS Gatekeeper to forward to the external application any LCF messages that it receives. This gives the application an opportunity to alter any of the fields in the confirmation.
When the external application receives a REQUEST LCF message from the Cisco IOS Gatekeeper it must take one of the following actions:
An LRJ message is sent by a peer gatekeeper to reject the location of a destination endpoint, meaning the endpoint does not exist in the peer gatekeeper's zone. You can configure the Cisco IOS Gatekeeper to forward to the external application any LRJ messages that it receives. This gives the external application an opportunity to recommend an alternate destination.
When the external application receives a REQUEST LRJ message from the Cisco IOS Gatekeeper it must take one of the following actions:
The GKTMP supports the use of CryptoH323Tokens for authentication. The CryptoH323Token is defined in H.225 Version 2 and is used in a "password with hashing" security scheme as described in section 10.3.3 of the H.235 specification.
A cryptoToken can be included in any RAS message and is used to authenticate the sender of the message. The use of cryptoTokens allows you to use a separate database for user ID and password verification.
Cisco gateways support three levels of authentication:
You can configure the level of authentication for the gateway using the Cisco IOS software command line interface.
CryptoTokens for RRQ, URQ, and the terminating side of ARQ messages contain information about the gateway that generated the token, including the gateway ID (which is the H.323 ID configured on the gateway) and the gateway password. CryptoTokens for the originating side ARQ messages contain information about the user that is placing the call, including the user ID and personal identification number (PIN).
Therefore, if you want to use cryptoTokens for authentication, each client in your network must include a cryptoToken in every message that it sends to the Cisco IOS Gatekeeper.
To participate in this authentication scheme, a GKTMP-based application must have the following:
The application should be set up to authenticate the messages that you deem necessary. If you want to authenticate gateways when they register, your application should validate RRQ messages. If you want per-call authentication, your application should validate ARQ messages. Or, you can have you application validate all messages.
To validate a cryptoToken received in a RAS message, the application should:
1. Use the alias in the cryptoToken to look up the associated password.
2. Use the password, the timestamp, and the alias, to ASN.1 encode a ClearToken. The ClearToken is a PwdCertToken. The application should maintain the password and alias as NULL-terminated strings and include the NULL when performing the ASN.1 encoding.
3. Perform an MD5 Hash on the ASN.1 encoded buffer. This will result in a 16-byte Hash.
4. Compare the calculated Hash with the one found in the token field of the cryptoEPPwdHash.
If the hash values match, the application should issue a confirmation message (xCF) to the gatekeeper, which is transmitted to the gateway. Otherwise, the application should send a rejection message (xRJ) with a reject reason of securityDenial.
The cryptoToken message body line contains a type identifier followed by a colon and a sequence of space separated tag=value parameters that are associated with the particular type of cryptoToken.
For example, a message body line containing a cryptoToken could look like the following:
$=E:a=H:gw1-rtp T=940647784 h=FFABCD0067AE12436780167364847343
For more information about the parameters included in cryptoTokens, see "GKTMP Messages".
The following examples show the GKTMP messages that would be generated in some possible uses of the external interface.
An external application might need to maintain a database of active gateways so that it can select gateways for ARQ or LRQ resolution. In this case, triggers can be configured on the Cisco IOS Gatekeepers such that any RRQ or URQ messages will be forwarded to the external application on a Notification-only: basis. Example 3-1 shows an RRQ notification for a gateway. Example 3-2 shows a URQ notification for a gateway.
REQUEST RRQ
Version-id: 100
From: gk1-sj
Notification-only:
Content-Length:90
c=I:171.69.136.205:1720
r=I:171.69.136.205:16523
a=H:gw3-sj
t=voice-gateway
p=2# 99#
REQUEST URQ
Version-id: 100
From: gk1-sj
Notification-only:
Content-Length:23
c=I:171.69.136.205:1720
You might want the Cisco IOS Gatekeeper to forward ARQs to an external application to determine the mapping for an 800 number. Example 3-3 shows an ARQ request from the Cisco IOS Gatekeeper. Example 3-4 shows the corresponding response from the external application.
REQUEST ARQ
Version-id: 100
From: gk1-sj
Transaction-Id: 5de04245
Content-Length: 127
s=E:4085552132
d=E:8005721234
b=560
A=f
m=t
c=f81d4fae-7dec-11d0-a765-00a0c91e6bf6
C=f81d4fae-7dec-11d0-a765-00a0c91e6bf6
RESPONSE ARQ
Version-id: 100
To: gk1-sj
Transaction-Id: 5de04245
Content-Length:14
d=E:4155551212
If you have an internet call-waiting (ICW) server in your network, you might want configure the Cisco IOS Gatekeeper to forward all LRQ requests to the ICW server. Example 3-5 shows the LRQ request from the Cisco IOS Gatekeeper.
REQUEST LRQ
Version-id: 100
From: gk1-sj
Transaction-Id: 5de04246
Content-Length:64
s=H:gk3-la
d=E:4085551111
p=0
c=4085552222
If the ICW server determines that the destination (4085551111) is not a subscriber, it sends back a RESPONSE LRQ with a null message body. The Cisco IOS Gatekeeper then proceeds with normal processing. Example 3-6 shows the response.
RESPONSE LRQ
Version-id: 100
To: gk1-sj
Transaction-Id: 5de04246
If the ICW server determines that the destination (4085551111) is a subscriber and is currently logged on, it pings the subscriber to determine how the call should be handled. Because this can take several seconds, the ICW server first sends a RESPONSE RIP to the Cisco IOS Gatekeeper asking for a 60-second extension to the timeout. Example 3-7 shows the response.
RESPONSE RIP
Version-id: 100
To: gk1-sj
Transaction-Id: 5de04246
Content-Length:7
d=60000
If the subscriber refuses the call, the ICW server sends a rejection to the Cisco IOS Gatekeeper. Example 3-8 shows the response.
RESPONSE LRJ
Version-id: 100
To: gk1-sj
Transaction-Id: 5de04246
Content-Length:15
R=requestDenied
If the subscriber hangs up to accept the call, the ICW server sends a RESPONSE LRQ with a null message body, which instructs the Cisco IOS Gatekeeper to proceed with the call. Example 3-9 shows the response.
RESPONSE LRQ
Version-id: 100
To: gk1-sj
Transaction-Id: 5de04246
If the subscriber chooses to route the call to voicemail (4085553333) and the ICW server knows the IP address of the voicemail gateway (172.45.63.49), the server instructs the Cisco IOS Gatekeeper to route the call to the voicemail system. Example 3-10 shows the response.
RESPONSE LCF
Version-id: 100
To: gk1-sj
Transaction-Id: 5de04246
Content-Length:94
d=E:4085553333
D=I:172.45.63.49:1720
r=I:172.45.63.49:13982
t=voice-gateway
X=4085551111
The Gatekeeper API is offered as a library that contains the API functions, which are designed to work with GKTMP. An external application must link with the GKAPI object code and call the API functions to communicate with the Cisco IOS Gatekeeper.
The GKAPI includes the following files:
The Gatekeeper API provides functions and structures that allow an external application to obtain data from and return information to the Cisco IOS Gatekeeper. Using the API functions and structures, as well as some standard functions, the external client application:
1. Establishes a connection with the Cisco IOS Gatekeeper using the GkapiSetupClient function.
2. Monitors the appropriate socket using a standard function.
3. When a connect complete is detected, the application notifies the Gatekeeper API using the GkapiClientConnected function.
4. When a read message is detected, it allocates memory for the storage of the message using the GetReadMsgBuffer function.
5. Stores the contents of the message in the appropriate structure using the ReadMsgBuffer function.
6. Obtains the data from the structure and performs the processing as designed.
7. Frees the memory allocated for the read message using the FreeReadMsgBuffer function.
8. Writes the resulting data to the appropriate structure using the WriteResponseMsg function.
The external application can repeat these steps as often as necessary. If a read error is encountered or if the external application wants to terminate the connection to the Cisco IOS Gatekeeper, the application should use the CloseGateKeeperConnection function.
The API functions and structures are described in "Gatekeeper API Functions and Structures".
As stated earlier, an external application must be linked with the GKAPI object code and call the API functions in order to communicate with the Cisco IOS Gatekeeper. If you have an external application that you want to make use of the Gatekeeper API and GKTMP, be sure you link you it with the Gatekeeper API library.
The following is an example makefile for building an application using the GNU "C" compiler and linking with the Gatekeeper API library.
This sample makefile is bundled with the GKAPI binary and the header file and can be extracted from the GK software .tar file on the Cisco.com website. The sample file can be modified and used to meet the individual requirements of the end user.
rm -f gkapiver.c
echo \#ifndef GKAPI_MAX_VER_STR_LEN >>gkapiver.c
echo \#define GKAPI_MAX_VER_STR_LEN 128 >>gkapiver.c
echo \#endif >>gkapiver.c
echo char version_string\[GKAPI_MAX_VER_STR_LEN\]= \"Compiled \Qdate +"%a
%d-%h-%y %H:%M"\Q OS target:\Quname -sr\Q \"\; >>gkapiver.c
gcc -g -Wall -c gkapiver.c -o gkapiver
gcc -g gk_api gkapiver gk_application.c
/usr/lib/libintl.a -lsocket -lnsl -ldl -lthread
-lpthread -lposix4 -ogk_application
When you are writing an application that uses the Gatekeeper API, keep the following in mind:
The following examples show how the Gatekeeper API functions can be used in an external application. These examples are meant to illustrate how the API functions can be called. They are not examples of actual implementations of the API. Two examples are included in this section; one in which the application is the client and one in which the application is the server.
#include "gk_api.h" /* API header file */
#include </usr/include/sys/fcntl.h>
#include </usr/include/sys/socket.h>
#include </usr/include/sys/select.h>
#include <signal.h>
#define APP_VER 1
void sig_int(int sigNo);
STATUS_TYPE BuildRRQRegisterMsg(GKAPI_SOCK_INFO_T *clientConnect);
STATUS_TYPE BuildRRQResponse(GK_READ_MSG_TYPE *ptr,
GKAPI_SOCK_INFO_T *connectPtr);
main()
{
GKAPI_SOCK_INFO_T clientConnect;
STATUS_TYPE status;
GK_READ_MSG_TYPE *readMsgPtr;
struct timeval tval;
int conn_handle;
int n;
fd_set wset, rset;
BOOLEAN read_pending = FALSE;
readMsgPtr=NULL;
/* Install signal handler for SIGPIPE */
if (signal(SIGPIPE, sig_int) == SIG_ERR) {
printf("error registering signal \n");
}
/* Open Connection to GateKeeper */
/* Fill in TCP port and IP address of GateKeeper */
clientConnect.IPAddress = inet_addr("111.222.111.222");
clientConnect.TCPPort=2000;
/* Setup the connection for nonblocking I/O */
conn_handle=GkapiSetupClient(&clientConnect, &status, TRUE);
/* Check status for errors */
/* If status == PROCESSING_SUCCESSFUL, no errors were encountered */
/* status == TCP_CONNECT_ERROR, error in connecting to GateKeeper */
/* status == TCP_HANDLE_ERROR, error in handle creation */
/* For error conditions, retry connecting to the GateKeeper */
/* Check for following errors:
* status = MEM_ALLOC_FAIL
* INVALID_MSG_SPECIFIED
* INVALID_ENDPOINT_SPECIFIED
* INVALID_REDIRECT_REASON_SPECIFIED
* HEADER_INFO_INCOMPLETE
* NULL_POINTER_PASSED
*/
/* If status is PROCESSING_SUCCESSFUL or CONNECT_IN_PROGRESS,
wait for connect and read event */
if (status == CONNECT_IN_PROGRESS) {
FD_ZERO(&rset);
FD_SET(conn_handle, &rset);
wset = rset;
tval.tv_sec = 1;
tval.tv_usec = 0;
if ( (n = select(conn_handle + 1, &rset, &wset, NULL,
&tval)) == 0) {
printf("\nApplication connect timed out");
CloseGateKeeperConnection(&clientConnect);
exit(1);
}
if (FD_ISSET(conn_handle, &rset) ||
FD_ISSET(conn_handle, &wset)) {
status = PROCESSING_SUCCESSFUL;
} else {
printf("\nSelect error");
CloseGateKeeperConnection(&clientConnect);
exit(1);
}
}
/* If a connect event has occurred tell GKAPI so */
conn_handle = GkapiClientConnected(&clientConnect, &status, conn_handle);
/* If conn_handle is valid and */
/* If status is PROCESSING_SUCCESSFUL, register triggers if required */
/* Build an RRQ Register message */
status = BuildRRQRegisterMsg(&clientConnect);
/* Check status for errors */
/* If status == PROCESSING_SUCCESSFUL, no errors were encountered */
if ((status == TCP_WRITE_ERROR) || /* TCP error encountered */
(status == TCP_CONNECTION_CLOSED)) { /* TCP connection closed */
/* Close connection to GateKeeper and free system resources */
CloseGateKeeperConnection(&clientConnect);
}
for(;;) {
FD_ZERO(&rset);
FD_SET(conn_handle, &rset);
select(conn_handle + 1, &rset, NULL, NULL, NULL);
printf("Select event occurred \n");
if (FD_ISSET(conn_handle, &rset)) {
/* If a read event has occurred:
* Allocate a read buffer
* Call ReadMsgBuffer
* Process Message
* Build Response if required
*/
if (!read_pending)
readMsgPtr=GetReadMsgBuffer();
/* Check if readMsgPtr is NULL, if NULL, memory allocation failed. */
/* if readMsgPtr != NULL, continue */
read_pending = FALSE;
status=ReadMsgBuffer(&clientConnect, readMsgPtr);
/* Check status for errors */
/* If status == PROCESSING_SUCCESSFUL, no errors were encountered */
if ((status == TCP_READ_ERROR) || /* TCP error encountered */
(status == TCP_CONNECTION_CLOSED) || /* TCP connection closed */
(status == MSG_READ_ERROR)) { /* Message not understood */
/* Free the read buffer
Close connection to GateKeeper and free system resources
*/
FreeReadMsgBuffer(readMsgPtr);
CloseGateKeeperConnection(&clientConnect);
/* Reopen connection to GateKeeper */
}
/* Check for other error conditions:
* status==MEM_ALLOC_FAIL
* status==NULL_POINTER_PASSED
*/
FreeReadMsgBuffer(readMsgPtr);
/* status==INCOMPLETE_MSG_READ */
/* Call ReadMsgBuffer on the next read event */
if (status == INCOMPLETE_MSG_READ)
read_pending = TRUE;
/* status == PROCESSING_SUCCESSFUL */
/* Extract message received */
switch(readMsgPtr->msgType) {
case RRQ_REQUEST_MSG:
status=BuildRRQResponse(readMsgPtr, &clientConnect);
/* Check status for errors.
* If TCP_WRITE_ERROR or TCP_CONNECTION_CLOSED
* call CloseGateKeeperConnection(&clientConnect)
* Reopen connection to GateKeeper.
* Check for other errors.
*/
FreeReadMsgBuffer(readMsgPtr);
break;
case ARQ_REQUEST_MSG:
/* Do processing */
FreeReadMsgBuffer(readMsgPtr);
break;
case MSG_NOT_SUPPORTED:
FreeReadMsgBuffer(readMsgPtr);
break;
default:
FreeReadMsgBuffer(readMsgPtr);
break;
}
}
}
}
STATUS_TYPE BuildRRQResponse(GK_READ_MSG_TYPE *ptr,
GKAPI_SOCK_INFO_T *connectPtr)
{
GK_WRITE_MSG_TYPE *writePtr;
HEADER_INFO_TYPE *headerPtr;
char buffer1[100];
char buffer2[100];
STATUS_TYPE status;
headerPtr=&ptr->MESSAGE_TYPE.rrqReqMsg.headerInfo;
/* allocate memory for writePtr, writePtr=malloc(...) */
/* Fill in msgType and header information */
writePtr->msgType=RRQ_RESPONSE_MSG;
writePtr->WRITE_MESSAGE_TYPE.rrqRespMsg.headerInfo.versionId = APP_VER;
strcpy(writePtr->WRITE_MESSAGE_TYPE.rrqRespMsg.headerInfo.from,
headerPtr->to);
strcpy(writePtr->WRITE_MESSAGE_TYPE.rrqRespMsg.headerInfo.to,
headerPtr->from);
strcpy(writePtr->WRITE_MESSAGE_TYPE.rrqRespMsg.headerInfo.
transactionID, headerPtr->transactionID);
/* Fill in paramters */
strcpy(buffer1, "M:joe_smith");
strcpy(buffer2, "1800");
writePtr->WRITE_MESSAGE_TYPE.rrqRespMsg.terminalAlias=buffer1;
writePtr->WRITE_MESSAGE_TYPE.rrqRespMsg.supportedPrefix=buffer2;
/* Send message to GateKeeper */
status=WriteResponseMsg(connectPtr, writePtr);
/* If memory was allocated for writePtr, free(writePtr) */
return(status);
}
STATUS_TYPE BuildRRQRegisterMsg(GKAPI_SOCK_INFO_T *clientConnect)
{
GK_REGISTER_MSG_TYPE *regPtr;
STATUS_TYPE status;
int i=0;
char buffer1[20];
/* Allocate memory for regPtr, regPtr=malloc(...) */
/* After allocating memory:
* Fill in header info and
* message parameters if needed
*/
/* Fill in message type */
regPtr->msgType = RRQ_REGISTER_MSG;
/* Fill in header info */
regPtr->
REGISTRATION_MESSAGE_TYPE.rrqRegMsg.headerInfo.versionId =
APP_VER;
strcpy(regPtr->REGISTRATION_MESSAGE_TYPE.rrqRegMsg.headerInfo.from,
"APPL 1");
strcpy(regPtr->REGISTRATION_MESSAGE_TYPE.rrqRegMsg.headerInfo.to,
"GK 1");
regPtr->REGISTRATION_MESSAGE_TYPE.rrqRegMsg.headerInfo.notificationOnly
=FALSE;
/* Set priority */
regPtr->REGISTRATION_MESSAGE_TYPE.rrqRegMsg.headerInfo.priority=1;
/* Specify filters for RRQ message */
regPtr->REGISTRATION_MESSAGE_TYPE.rrqRegMsg.terminalType[0] =
VOICEGATEWAY;
regPtr->REGISTRATION_MESSAGE_TYPE.rrqRegMsg.terminalType[1] = MCU;
for (i=2; i<MAX_NUM_ENDPOINT_TYPES; i++) {
regPtr->REGISTRATION_MESSAGE_TYPE.rrqRegMsg.terminalType[i] =
ENDPOINT_INFO_NOT_RCVD;
}
strcpy(buffer1, "1#");
regPtr->REGISTRATION_MESSAGE_TYPE.rrqRegMsg.supportedPrefix[0]=buffer1;
for (i=1; i< MAX_NUM_SUPPORTED_PREFIX; i++) {
regPtr->REGISTRATION_MESSAGE_TYPE.rrqRegMsg.supportedPrefix[i] = NULL;
}
/* Now gatekeeper will only send an RRQ message to the application
* if filter conditions are satisfied.
*/
status=WriteRegisterMessage(clientConnect, regPtr);
/* If memory was allocated for regPtr, free(regPtr) */
return(status);
}
STATUS_TYPE BuildRRQUnRegisterMsg(GKAPI_SOCK_INFO_T *clientConnect)
{
GK_UNREGISTER_MSG_TYPE unRegMsg;
STATUS_TYPE status;
unRegMsg.versionId = APP_VER;
strcpy(unRegMsg.from, "APPL 1");
strcpy(unRegMsg.to, "GK 1");
unRegMsg.unregisterMsg = RRQ_REGISTER_MSG;
/* Set priority */
unRegMsg.priority=1;
status=WriteUnregisterMessage(clientConnect, &unRegMsg);
return(status);
}
void sig_int(int sigNo)
{
switch (sigNo) {
case SIGPIPE:
printf("SIGPIPE received\n");
break;
/* case ... */
default:
}
}
#include "gk_api.h" /* API header file */
#include </usr/include/sys/socket.h>
#include </usr/include/sys/select.h>
#include </usr/include/netinet/in.h>
#include </usr/include/sys/errno.h>
#include <signal.h>
typedef struct client_db_t_ {
int handle;
GK_READ_MSG_TYPE *buf;
GKAPI_SOCK_INFO_T *conn_info;
} client_db_t;
#define MAX_CLIENTS 1024
#define APP_VER 1
void sig_int(int sigNo);
STATUS_TYPE BuildRRQRegisterMsg(GKAPI_SOCK_INFO_T *clientConnect);
STATUS_TYPE BuildRRQResponse(GK_READ_MSG_TYPE *ptr,
GKAPI_SOCK_INFO_T *connectPtr);
main()
{
GKAPI_SOCK_INFO_T ServerInfo;
GKAPI_TCP_ADDR_INFO_T client_addr;
STATUS_TYPE status;
GK_READ_MSG_TYPE *readMsgPtr;
GKAPI_SOCK_INFO_T *connInfo;
client_db_t client[MAX_CLIENTS+1];
int conn_handle, serverHandle, max_fd;
int i, n;
fd_set rset;
BOOLEAN read_pending = FALSE;
readMsgPtr=NULL;
for (i=0; i<=MAX_CLIENTS; i++) {
client[i].handle=0;
client[i].buf=0;
client[i].conn_info=0;
}
/* Install signal handler for SIGPIPE */
if (signal(SIGPIPE, sig_int) == SIG_ERR) {
printf("error registering signal \n");
}
/* Open Connection to GateKeeper */
/* Fill in TCP port and IP address of Application */
ServerInfo.IPAddress = inet_addr("111.222.111.222");
ServerInfo.TCPPort=2000;
/* Setup the connection for nonblocking I/O */
serverHandle=GkapiSetupServer(&ServerInfo, &status, TRUE);
/* Check status for errors */
/* If the serverHandle < 0, there was an error. Check status for
/* for the error code.
/* If status == TCP_CONNECT_ERROR, error in connecting to GateKeeper */
/* status == TCP_BIND_ERROR, error in connecting to Gatekeeper */
/* status == TCP_LISTEN_ERROR, error in connecting to Gatekeeper */
/* status == TCP_NONBLOCK_ERROR, error setting up for */
/* nonblocking connection */
/* status == TCP_HANDLE_ERROR, error in handle creation */
/* For error conditions, quit */
if (serverHandle < 0)
exit(1);
/* Set up select mask with the server's handle to listen for
* incoming connections.
*/
max_fd = serverHandle;
FD_ZERO(&rset);
FD_SET(serverHandle, &rset);
for ( ; ; ) {
/* If status is PROCESSING_SUCCESSFUL wait for incoming connections
* and read events */
n = select(max_fd + 1, &rset, NULL, NULL, NULL);
/* If the select event has occurred on the server handle,
* it is a new incoming connection.
*/
if (FD_ISSET(serverHandle, &rset)) {
connInfo = (GKAPI_SOCK_INFO_T *)malloc(sizeof(GKAPI_SOCK_INFO_T));
connInfo->TCPPort = ServerInfo.TCPPort;
connInfo->IPAddress = ServerInfo.IPAddress;
conn_handle = GkapiAcceptConnection(connInfo, &status,
serverHandle, &client_addr);
/* If conn_handle < 0, there is an error. Ignore and continue to
* to process other select events.
* If conn_handle is valid, add new connection
* to select read list
*/
FD_SET(conn_handle, &rset);
/* Setup the max file descriptor we need to select on */
if (conn_handle > max_fd)
max_fd = conn_handle;
/* Add this new connection to list of active connections */
for (i=0; i<MAX_CLIENTS; i++) {
if (client[i].handle == 0) {
client[i].handle = conn_handle;
client[i].buf = 0;
client[i].conn_info = connInfo;
}
}
/* The application set GK triggers for this connection at
* this point.
*/
}
/*
* Check to see if the select event is a read occurring
* on one of the existing connections. If so, have GKAPI process the
* received buffer.
*/
for (i=0; n>0,i<=MAX_CLIENTS; i++,n--) {
if (client[i].handle <= 0)
continue;
if (FD_ISSET(client[i].handle, &rset)) {
if (client[i].buf == 0) {
readMsgPtr=GetReadMsgBuffer();
} else {
readMsgPtr=client[i].buf;
}
/* If a read event has occurred:
* Allocate a read buffer if it isn't a pending read.
* Call ReadMsgBuffer
* Process Message
* Build Response if required
*/
if(readMsgPtr != NULL) {
status=ReadMsgBuffer(client[i].conn_info, readMsgPtr);
/* Check if readMsgPtr is NULL, if NULL,
* memory allocation failed.
*/
/* if readMsgPtr != NULL, continue */
if(status == PROCESSING_SUCCESSFUL) {
client[i].buf = 0;
/* Process the Message */
/* Extract message received */
switch(readMsgPtr->msgType) {
case RRQ_REQUEST_MSG:
status=BuildRRQResponse(readMsgPtr, &ServerInfo);
/* Check status for errors.
* If TCP_WRITE_ERROR or TCP_CONNECTION_CLOSED
* call CloseGateKeeperConnection(&ServerInfo)
* Reopen connection to GateKeeper.
* Check for other errors.
*/
FreeReadMsgBuffer(readMsgPtr);
break;
case ARQ_REQUEST_MSG:
/* Do processing */
FreeReadMsgBuffer(readMsgPtr);
break;
case MSG_NOT_SUPPORTED:
FreeReadMsgBuffer(readMsgPtr);
break;
default:
FreeReadMsgBuffer(readMsgPtr);
break;
}
} /* End of status == PROCESSING_SUCCESSFUL */
/* Check status for errors */
if ((status == TCP_READ_ERROR) || /* TCP error encountered */
(status == TCP_CONNECTION_CLOSED) || /*connection closed*/
(status == MSG_READ_ERROR)) { /* Message not understood */
/* Free the read buffer
* Close connection to GateKeeper and
* free system resources
*/
FreeReadMsgBuffer(readMsgPtr);
CloseGateKeeperConnection(client[i].conn_info);
/* Reopen connection to GateKeeper */
}
/* Check for other error conditions:
* status==MEM_ALLOC_FAIL
* status==NULL_POINTER_PASSED
*/
FreeReadMsgBuffer(readMsgPtr);
/* status==INCOMPLETE_MSG_READ */
/* Call ReadMsgBuffer on the next read event */
if (status == INCOMPLETE_MSG_READ)
client[i].buf = readMsgPtr;
}
}
}
}
}
STATUS_TYPE BuildRRQResponse(GK_READ_MSG_TYPE *ptr,
GKAPI_SOCK_INFO_T *connectPtr)
{
GK_WRITE_MSG_TYPE *writePtr;
HEADER_INFO_TYPE *headerPtr;
char buffer1[100];
char buffer2[100];
STATUS_TYPE status;
headerPtr=&ptr->MESSAGE_TYPE.rrqReqMsg.headerInfo;
/* allocate memory for writePtr, writePtr=malloc(...) */
/* Fill in msgType and header information */
writePtr->msgType=RRQ_RESPONSE_MSG;
writePtr->WRITE_MESSAGE_TYPE.rrqRespMsg.headerInfo.versionId = APP_VER;
strcpy(writePtr->WRITE_MESSAGE_TYPE.rrqRespMsg.headerInfo.from,
headerPtr->to);
strcpy(writePtr->WRITE_MESSAGE_TYPE.rrqRespMsg.headerInfo.to,
headerPtr->from);
strcpy(writePtr->WRITE_MESSAGE_TYPE.rrqRespMsg.headerInfo.
transactionID, headerPtr->transactionID);
/* Fill in parameters */
strcpy(buffer1, "M:joe_smith");
strcpy(buffer2, "1800");
writePtr->WRITE_MESSAGE_TYPE.rrqRespMsg.terminalAlias=buffer1;
writePtr->WRITE_MESSAGE_TYPE.rrqRespMsg.supportedPrefix=buffer2;
/* Send message to GateKeeper */
status=WriteResponseMsg(connectPtr, writePtr);
/* If memory was allocated for writePtr, free(writePtr) */
return(status);
}
STATUS_TYPE BuildRRQRegisterMsg(GKAPI_SOCK_INFO_T *ServerInfo)
{
GK_REGISTER_MSG_TYPE *regPtr;
STATUS_TYPE status;
int i=0;
char buffer1[20];
/* Allocate memory for regPtr, regPtr=malloc(...) */
/* After allocating memory:
* Fill in header info and
* message parameters if needed
*/
/* Fill in message type */
regPtr->msgType = RRQ_REGISTER_MSG;
/* Fill in header info */
regPtr->
REGISTRATION_MESSAGE_TYPE.rrqRegMsg.headerInfo.versionId =
APP_VER;
strcpy(regPtr->REGISTRATION_MESSAGE_TYPE.rrqRegMsg.headerInfo.from,
"APPL 1");
strcpy(regPtr->REGISTRATION_MESSAGE_TYPE.rrqRegMsg.headerInfo.to,
"GK 1");
regPtr->REGISTRATION_MESSAGE_TYPE.rrqRegMsg.headerInfo.notificationOnly
=FALSE;
/* Set priority */
regPtr->REGISTRATION_MESSAGE_TYPE.rrqRegMsg.headerInfo.priority=1;
/* Specify filters for RRQ message */
regPtr->REGISTRATION_MESSAGE_TYPE.rrqRegMsg.terminalType[0] =
VOICEGATEWAY;
regPtr->REGISTRATION_MESSAGE_TYPE.rrqRegMsg.terminalType[1] = MCU;
for (i=2; i<MAX_NUM_ENDPOINT_TYPES; i++) {
regPtr->REGISTRATION_MESSAGE_TYPE.rrqRegMsg.terminalType[i] =
ENDPOINT_INFO_NOT_RCVD;
}
strcpy(buffer1, "1#");
regPtr->REGISTRATION_MESSAGE_TYPE.rrqRegMsg.supportedPrefix[0]=buffer1;
for (i=1; i< MAX_NUM_SUPPORTED_PREFIX; i++) {
regPtr->REGISTRATION_MESSAGE_TYPE.rrqRegMsg.supportedPrefix[i] = NULL;
}
/* Now gatekeeper will only send an RRQ message to the application
* if filter conditions are satisfied.
*/
status=WriteRegisterMessage(ServerInfo, regPtr);
/* If memory was allocated for regPtr, free(regPtr) */
return(status);
}
STATUS_TYPE BuildRRQUnRegisterMsg(GKAPI_SOCK_INFO_T *ServerInfo)
{
GK_UNREGISTER_MSG_TYPE unRegMsg;
STATUS_TYPE status;
unRegMsg.versionId = APP_VER;
strcpy(unRegMsg.from, "APPL 1");
strcpy(unRegMsg.to, "GK 1");
unRegMsg.unregisterMsg = RRQ_REGISTER_MSG;
/* Set priority */
unRegMsg.priority=1;
status=WriteUnregisterMessage(ServerInfo, &unRegMsg);
return(status);
}
void sig_int(int sigNo)
{
switch (sigNo) {
case SIGPIPE:
printf("SIGPIPE received\n");
break;
/* case ... */
default:
}
}
Posted: Fri Sep 27 04:49:04 PDT 2002
All contents are Copyright © 1992--2002 Cisco Systems, Inc. All rights reserved.
Important Notices and Privacy Statement.