57f18cef8c
issue.
625 lines
20 KiB
C
625 lines
20 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
|
|
|
|
//===[ External prototypes ]===============================================
|
|
|
|
extern
|
|
int
|
|
SetupOSSLSupport(void);
|
|
|
|
extern
|
|
void
|
|
CleanupOSSLSupport(void);
|
|
|
|
//===[ Function prototypes ]===============================================
|
|
|
|
|
|
//===[ Global variables ]==================================================
|
|
|
|
static
|
|
bool g_rpcInitialized = false;
|
|
|
|
|
|
//++=======================================================================
|
|
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
|
|
//=======================================================================--
|
|
{
|
|
#ifndef CASA_STATUS_INVALID_SERVER_CERTIFICATE
|
|
#define CASA_STATUS_INVALID_SERVER_CERTIFICATE CASA_STATUS_UNSUCCESSFUL // temporary until casa_status.h is updated
|
|
#endif
|
|
|
|
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 = %0X\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 = %0X\n", retStatus);
|
|
|
|
return retStatus;
|
|
}
|
|
|
|
|
|
//++=======================================================================
|
|
CasaStatus
|
|
InitializeRpc(void)
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Abstract:
|
|
//
|
|
// Notes:
|
|
//
|
|
// L2
|
|
//=======================================================================--
|
|
{
|
|
CasaStatus retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
|
|
CASA_FACILITY_AUTHTOKEN,
|
|
CASA_STATUS_UNSUCCESSFUL);
|
|
|
|
DbgTrace(1, "-InitializeRpc- Start\n", 0);
|
|
|
|
// Initialize OpenSSL support
|
|
if (SetupOSSLSupport() == 0)
|
|
{
|
|
// Perform libcurl initializatoin
|
|
CURLcode curlStatus = curl_global_init(CURL_GLOBAL_SSL);
|
|
if (curlStatus != 0)
|
|
{
|
|
DbgTrace(0, "-InitializeRpc- Error initializing libcurl, curlStatus = %0X\n", curlStatus);
|
|
CleanupOSSLSupport();
|
|
}
|
|
else
|
|
{
|
|
// Success
|
|
g_rpcInitialized = true;
|
|
retStatus = CASA_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DbgTrace(0, "-InitializeRpc- OpenSSL support setup failure\n", 0);
|
|
}
|
|
|
|
DbgTrace(1, "-InitializeRpc- End, retStatus = %0X\n", retStatus);
|
|
|
|
return retStatus;
|
|
}
|
|
|
|
|
|
//++=======================================================================
|
|
void
|
|
UnInitializeRpc(void)
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Abstract:
|
|
//
|
|
// Notes:
|
|
//
|
|
// L2
|
|
//=======================================================================--
|
|
{
|
|
DbgTrace(1, "-UnInitializeRpc- Start\n", 0);
|
|
|
|
// Only try to cleanup if we were initialized
|
|
if (g_rpcInitialized)
|
|
{
|
|
// Cleanup libcurl
|
|
curl_global_cleanup();
|
|
|
|
// Cleanup OpenSSL support
|
|
CleanupOSSLSupport();
|
|
|
|
// Forget about having been initialized
|
|
g_rpcInitialized = false;
|
|
}
|
|
|
|
DbgTrace(1, "-UnInitializeRpc- End\n", 0);
|
|
}
|
|
|
|
|
|
//++=======================================================================
|
|
//++=======================================================================
|
|
//++=======================================================================
|
|
|