CASA/CASA-auth-token/client/core/linux/rpc.c
Juan Carlos Luciani 2c8668479c Splitted the non-java project into client and server projects in order
to be able to deliver the client component onto distributions targeting
desktops without having to deliver the server components. This commit is
for the resulting client project.
2006-11-13 05:20:43 +00:00

567 lines
18 KiB
C

/***********************************************************************
*
* Copyright (C) 2006 Novell, Inc. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, Novell, Inc.
*
* To contact Novell about this file by physical or electronic mail,
* you may find current contact information at www.novell.com.
*
* Author: Juan Carlos Luciani <jluciani@novell.com>
*
***********************************************************************/
//===[ Include files ]=====================================================
#include "internal.h"
//===[ Type definitions ]==================================================
#define MAX_RPC_RETRIES 3
//===[ Function prototypes ]===============================================
//===[ Global variables ]==================================================
//++=======================================================================
size_t
CurlWriteCallback(
IN void *pData,
IN size_t dataItemSz,
IN size_t numDataItems,
IN RpcSession *pSession)
//
// Arguments:
//
// Returns:
//
// Abstract:
//
// Notes:
//
// L2
//=======================================================================--
{
size_t dataConsumed = numDataItems;
DbgTrace(1, "-CurlWriteCallback- Start\n", 0);
// Consume the data by keeping a copy of the data. Note that we may have
// already consumed some data in which case we need to allocate a new
// buffer big enough to hold all of it.
if (pSession->pRecvData == NULL)
{
// We have not yet consumed receive data for the current Rpc
pSession->pRecvData = (char*) malloc(numDataItems * dataItemSz);
if (pSession->pRecvData)
{
// Consume the data
memcpy(pSession->pRecvData, pData, numDataItems * dataItemSz);
pSession->recvDataLen = numDataItems * dataItemSz;
}
else
{
DbgTrace(0, "-CurlWriteCallback- Buffer allocation error\n", 0);
dataConsumed = CURLE_WRITE_ERROR; // To abort RPC
}
}
else
{
// We have already consumed receive data for the current Rpc, append the new data to it.
char *pNewRecvDataBuf = (char*) malloc(pSession->recvDataLen + (numDataItems * dataItemSz));
if (pNewRecvDataBuf)
{
memcpy(pNewRecvDataBuf, pSession->pRecvData, pSession->recvDataLen);
memcpy(pNewRecvDataBuf + pSession->recvDataLen, pData, numDataItems * dataItemSz);
pSession->recvDataLen += numDataItems * dataItemSz;
free(pSession->pRecvData);
pSession->pRecvData = pNewRecvDataBuf;
}
else
{
DbgTrace(0, "-CurlWriteCallback- Buffer allocation error\n", 0);
dataConsumed = CURLE_WRITE_ERROR; // To abort RPC
// Forget about already consumed data
free(pSession->pRecvData);
pSession->pRecvData = NULL;
}
}
DbgTrace(1, "-CurlWriteCallback- End\n", 0);
return dataConsumed;
}
//++=======================================================================
RpcSession*
OpenRpcSession(
IN const char *pHostName,
IN const uint16_t hostPort)
//
// Arguments:
//
// Returns:
//
// Abstract:
//
// Notes:
//
// L2
//=======================================================================--
{
RpcSession *pSession = NULL;
char *pPartialHttpUrl = NULL;
char *pPartialHttpsUrl = NULL;
int hostNameLen = strlen(pHostName);
DbgTrace(1, "-OpenRpcSession- Start\n", 0);
// Build the partial URL strings that may be used with this session.
pPartialHttpUrl = (char*) malloc(7 /*"http://"*/ + hostNameLen + 34 /*":XXXX/CasaAuthTokenSvc/Rpc?method="*/ + 1 /*NULL Terminator*/);
pPartialHttpsUrl = (char*) malloc(8 /*"https://"*/ + hostNameLen + 34 /*":XXXX/CasaAuthTokenSvc/Rpc?method="*/ + 1 /*NULL Terminator*/);
if (pPartialHttpUrl && pPartialHttpsUrl)
{
sprintf(pPartialHttpUrl, "http://%s:%d/CasaAuthTokenSvc/Rpc?method=", pHostName, hostPort);
sprintf(pPartialHttpsUrl, "https://%s:%d/CasaAuthTokenSvc/Rpc?method=", pHostName, hostPort);
// Allocate space for the session
pSession = (RpcSession*) malloc(sizeof(*pSession));
if (pSession)
{
// Zero the session structure
memset(pSession, 0, sizeof(*pSession));
// Get a curl handle
pSession->hCurl = curl_easy_init();
if (pSession->hCurl != NULL)
{
CURLcode result;
bool setOptError = false;
// Set necessary options on the handle
if ((result = curl_easy_setopt(pSession->hCurl, CURLOPT_NOSIGNAL, 0)) != CURLE_OK)
{
DbgTrace(0, "-OpenRpcSession- Error setting CURLOPT_NOSIGNAL, code = %d\n", result);
setOptError = true;
}
if ((result = curl_easy_setopt(pSession->hCurl, CURLOPT_USERAGENT, "CASA Client/1.0")) != CURLE_OK)
{
DbgTrace(0, "-OpenRpcSession- Error setting CURLOPT_USERAGENT, code = %d\n", result);
setOptError = true;
}
if ((result = curl_easy_setopt(pSession->hCurl, CURLOPT_POST, 1)) != CURLE_OK)
{
DbgTrace(0, "-OpenRpcSession- Error setting CURLOPT_POST, code = %d\n", result);
setOptError = true;
}
pSession->headers = curl_slist_append(pSession->headers, "Content-Type: text/html");
pSession->headers = curl_slist_append(pSession->headers, "Expect:");
if ((result = curl_easy_setopt(pSession->hCurl, CURLOPT_HTTPHEADER, pSession->headers)) != CURLE_OK)
{
DbgTrace(0, "-OpenRpcSession- Error setting CURLOPT_HTTPHEADER, code = %d\n", result);
setOptError = true;
}
if ((result = curl_easy_setopt(pSession->hCurl, CURLOPT_WRITEFUNCTION, CurlWriteCallback)) != CURLE_OK)
{
DbgTrace(0, "-OpenRpcSession- Error setting CURLOPT_WRITEFUNCTION, code = %d\n", result);
setOptError = true;
}
if ((result = curl_easy_setopt(pSession->hCurl, CURLOPT_WRITEDATA, pSession)) != CURLE_OK)
{
DbgTrace(0, "-OpenRpcSession- Error setting CURLOPT_WRITEDATA, code = %d\n", result);
setOptError = true;
}
// Now check if we succeded
if (setOptError == false)
{
// Success, finish setting up the session object.
pSession->pPartialHttpUrl = pPartialHttpUrl;
pSession->partialHttpUrlLen = strlen(pPartialHttpUrl);
pSession->pPartialHttpsUrl = pPartialHttpsUrl;
pSession->partialHttpsUrlLen = strlen(pPartialHttpsUrl);
// Forget about the partial URL buffers so that they do not get deleted below
pPartialHttpUrl = NULL;
pPartialHttpsUrl = NULL;
}
else
{
// Failed to set a needed curl option
if (pSession->headers)
curl_slist_free_all(pSession->headers);
curl_easy_cleanup(pSession->hCurl);
free(pSession);
pSession = NULL;
}
}
else
{
DbgTrace(0, "-OpenRpcSession- Error creating curl handle\n", 0);
free(pSession);
pSession = NULL;
}
}
else
{
DbgTrace(0, "-OpenRpcSession- Failed to allocate buffer for rpc session\n", 0);
}
}
else
{
DbgTrace(0, "-OpenRpcSession- Failed to allocate buffer for URL\n", 0);
}
// Free buffers not utilized
if (pPartialHttpUrl)
free(pPartialHttpUrl);
if (pPartialHttpsUrl)
free(pPartialHttpsUrl);
DbgTrace(2, "-OpenRpcSession- End, pSession = %0lX\n", (long) pSession);
return pSession;
}
//++=======================================================================
void
CloseRpcSession(
IN RpcSession *pSession)
//
// Arguments:
//
// Returns:
//
// Abstract:
//
// Notes:
//
// L2
//=======================================================================--
{
DbgTrace(1, "-CloseRpcSession- Start\n", 0);
// Free any HTTP headers associated with the session
if (pSession->headers)
curl_slist_free_all(pSession->headers);
// Close the curl handle associated with this session
curl_easy_cleanup(pSession->hCurl);
// Free the space allocated for the session
if (pSession->pRecvData)
free(pSession->pRecvData);
free(pSession->pPartialHttpUrl);
free(pSession->pPartialHttpsUrl);
free(pSession);
DbgTrace(1, "-CloseRpcSession- End\n", 0);
}
//++=======================================================================
static
CasaStatus
InternalRpc(
IN RpcSession *pSession,
IN char *pMethod,
IN long flags,
IN char *pRequestData,
INOUT char **ppResponseData,
INOUT int *pResponseDataLen)
//
// Arguments:
//
// Returns:
//
// Abstract:
//
// Notes:
//
// L2
//=======================================================================--
{
#define CASA_STATUS_INVALID_SERVER_CERTIFICATE CASA_STATUS_UNSUCCESSFUL // temporary until casa_status.h is updated
CasaStatus retStatus;
char *pPartialUrl;
int partialUrlLen;
char *pUrl;
CURLcode curlResult;
DbgTrace(1, "-InternalRpc- Start\n", 0);
// Initialize output parameters
*ppResponseData = NULL;
*pResponseDataLen = 0;
// Setup the URL using the input parameters
if (flags & SECURE_RPC_FLAG)
{
pPartialUrl = pSession->pPartialHttpsUrl;
partialUrlLen = pSession->partialHttpsUrlLen;
// Check if we need to ignore invalid CERTS
if (flags & ALLOW_INVALID_CERTS_RPC_FLAG)
{
if ((curlResult = curl_easy_setopt(pSession->hCurl, CURLOPT_SSL_VERIFYPEER, 0)) != CURLE_OK)
{
DbgTrace(0, "-InternalRpc- Error setting CURLOPT_SSL_VERIFYPEER, code = %d\n", curlResult);
}
if ((curlResult = curl_easy_setopt(pSession->hCurl, CURLOPT_SSL_VERIFYHOST, 0)) != CURLE_OK)
{
DbgTrace(0, "-InternalRpc- Error setting CURLOPT_SSL_VERIFYHOST, code = %d\n", curlResult);
}
}
else
{
if ((curlResult = curl_easy_setopt(pSession->hCurl, CURLOPT_SSL_VERIFYPEER, 1)) != CURLE_OK)
{
DbgTrace(0, "-InternalRpc- Error setting CURLOPT_SSL_VERIFYPEER, code = %d\n", curlResult);
}
if ((curlResult = curl_easy_setopt(pSession->hCurl, CURLOPT_SSL_VERIFYHOST, 2)) != CURLE_OK)
{
DbgTrace(0, "-InternalRpc- Error setting CURLOPT_SSL_VERIFYHOST, code = %d\n", curlResult);
}
}
}
else
{
pPartialUrl = pSession->pPartialHttpUrl;
partialUrlLen = pSession->partialHttpUrlLen;
}
pUrl = (char*) malloc(partialUrlLen + strlen(pMethod) + 1);
if (pUrl)
{
strcpy(pUrl, pPartialUrl);
strcat(pUrl, pMethod);
// Tell curl about the URL
curlResult = curl_easy_setopt(pSession->hCurl, CURLOPT_URL, pUrl);
if (curlResult == CURLE_OK)
{
// Tell curl about our post data
curlResult = curl_easy_setopt(pSession->hCurl, CURLOPT_POSTFIELDS, pRequestData);
if (curlResult == CURLE_OK)
{
// Tell curl about our post data len
curlResult = curl_easy_setopt(pSession->hCurl, CURLOPT_POSTFIELDSIZE, strlen(pRequestData));
if (curlResult == CURLE_OK)
{
// Now do the HTTP request
curlResult = curl_easy_perform(pSession->hCurl);
if (curlResult == CURLE_OK)
{
// Get the HTTP Response code
long httpCompStatus;
curlResult = curl_easy_getinfo(pSession->hCurl, CURLINFO_RESPONSE_CODE, &httpCompStatus);
if (curlResult == CURLE_OK)
{
// Verify that the HTTP request was successfully completed by the server
if (httpCompStatus == 200)
{
// Success, return the response data to the caller.
retStatus = CASA_STATUS_SUCCESS;
*ppResponseData = pSession->pRecvData;
*pResponseDataLen = pSession->recvDataLen;;
// Forget about the response data buffer to keep from freeing it.
pSession->pRecvData = NULL;
}
else
{
DbgTrace(0, "-InternalRpc- HTTP request did not complete successfully, status = %ld\n", httpCompStatus);
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
CASA_FACILITY_AUTHTOKEN,
CASA_STATUS_UNSUCCESSFUL);
}
}
else
{
DbgTrace(0, "-OpenRpcSession- Curl get info failed, code = %d\n", curlResult);
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
CASA_FACILITY_AUTHTOKEN,
CASA_STATUS_UNSUCCESSFUL);
}
}
else
{
DbgTrace(0, "-OpenRpcSession- Curl perform failed, code = %d\n", curlResult);
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
CASA_FACILITY_AUTHTOKEN,
CASA_STATUS_UNSUCCESSFUL);
}
// Make sure that we never exit with a recv data buffer hanging off the session
if (pSession->pRecvData)
{
free(pSession->pRecvData);
pSession->pRecvData = NULL;
}
}
else
{
DbgTrace(0, "-OpenRpcSession- Error setting CURLOPT_POSTFIELDSIZE, code = %d\n", curlResult);
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
CASA_FACILITY_AUTHTOKEN,
CASA_STATUS_UNSUCCESSFUL);
}
}
else
{
DbgTrace(0, "-OpenRpcSession- Error setting CURLOPT_POSTFIELDS, code = %d\n", curlResult);
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
CASA_FACILITY_AUTHTOKEN,
CASA_STATUS_UNSUCCESSFUL);
}
}
else
{
DbgTrace(0, "-OpenRpcSession- Error setting CURLOPT_URL, code = %d\n", curlResult);
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
CASA_FACILITY_AUTHTOKEN,
CASA_STATUS_UNSUCCESSFUL);
}
// Free the buffer used to hold the URL
free(pUrl);
}
else
{
DbgTrace(0, "-InternalRpc- Buffer allocation failure\n", 0);
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
CASA_FACILITY_AUTHTOKEN,
CASA_STATUS_INSUFFICIENT_RESOURCES);
}
DbgTrace(1, "-InternalRpc- End, retStatus = %d\n", retStatus);
return retStatus;
}
//++=======================================================================
CasaStatus
Rpc(
IN RpcSession *pSession,
IN char *pMethod,
IN long flags,
IN char *pRequestData,
INOUT char **ppResponseData,
INOUT int *pResponseDataLen)
//
// Arguments:
//
// Returns:
//
// Abstract:
//
// Notes:
//
// L2
//=======================================================================--
{
CasaStatus retStatus;
int retries = 0;
DbgTrace(1, "-Rpc- Start\n", 0);
// Retry the RPC as needed
do
{
// Issue the RPC
retStatus = InternalRpc(pSession,
pMethod,
flags,
pRequestData,
ppResponseData,
pResponseDataLen);
// Account for this try
retries ++;
} while (CasaStatusCode(retStatus) == CASA_STATUS_AUTH_SERVER_UNAVAILABLE
&& retries < MAX_RPC_RETRIES);
DbgTrace(1, "-Rpc- End, retStatus = %d\n", retStatus);
return retStatus;
}
//++=======================================================================
CasaStatus
InitializeRpc(void)
//
// Arguments:
//
// Returns:
//
// Abstract:
//
// Notes:
//
// L2
//=======================================================================--
{
CasaStatus retStatus;
DbgTrace(1, "-InitializeRpc- Start\n", 0);
// Perform libcurl initializatoin
CURLcode curlStatus = curl_global_init(CURL_GLOBAL_SSL);
if (curlStatus != 0)
{
DbgTrace(0, "-InitializeRpc- Error initializing libcurl, curlStatus = %08X\n", curlStatus);
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
CASA_FACILITY_AUTHTOKEN,
CASA_STATUS_UNSUCCESSFUL);
}
else
{
// Success
retStatus = CASA_STATUS_SUCCESS;
}
DbgTrace(1, "-InitializeRpc- End, retStatus = %08X\n", retStatus);
return retStatus;
}
//++=======================================================================
//++=======================================================================
//++=======================================================================