diff --git a/source/Makefile.am b/source/Makefile.am index f6866597..eeeb2c0e 100644 --- a/source/Makefile.am +++ b/source/Makefile.am @@ -70,6 +70,17 @@ telemetry2_0_LDFLAGS += -lwebconfig_framework telemetry2_0_CFLAGS += -DFEATURE_SUPPORT_WEBCONFIG endif +if IS_LIBRDKCONFIG_ENABLED +telemetry2_0_LDFLAGS += -lrdkconfig +telemetry2_0_CFLAGS += $(LIBRDKCONFIG_FLAG) +endif + +if IS_LIBRDKCERTSEL_ENABLED +telemetry2_0_LDFLAGS += -lRdkCertSelector +telemetry2_0_CFLAGS += $(LIBRDKCERTSEL_FLAG) +telemetry2_0_CFLAGS += -DENABLE_RDKB_SUPPORT +endif + if ENABLE_CCSP_SUPPORT telemetry2_0_LDFLAGS+=-lccsp_common telemetry2_0_SOURCES += t2ssp/ssp_main.c t2ssp/ssp_action.c t2ssp/ssp_messagebus_interface.c t2ssp/dm_pack_datamodel.c diff --git a/source/bulkdata/profile.c b/source/bulkdata/profile.c index 6d7c094e..62711b49 100644 --- a/source/bulkdata/profile.c +++ b/source/bulkdata/profile.c @@ -582,7 +582,7 @@ static void* CollectAndReport(void* data) if(n == ETIMEDOUT) { T2Info("TIMEOUT for maxUploadLatency of profile %s\n", profile->name); - ret = sendReportOverHTTP(httpUrl, jsonReport, NULL); + ret = sendReportOverHTTP(httpUrl, jsonReport); } else { @@ -619,7 +619,7 @@ static void* CollectAndReport(void* data) } else { - ret = sendReportOverHTTP(httpUrl, jsonReport, NULL); + ret = sendReportOverHTTP(httpUrl, jsonReport); } } else diff --git a/source/bulkdata/profilexconf.c b/source/bulkdata/profilexconf.c index d101020e..be9a62e2 100644 --- a/source/bulkdata/profilexconf.c +++ b/source/bulkdata/profilexconf.c @@ -49,7 +49,6 @@ static pthread_mutex_t plMutex; /* TODO - we can remove plMutex most likely but static pthread_cond_t reuseThread; static bool reportThreadExits = false; -static pid_t xconfReportPid; static bool isAbortTriggered = false ; static bool isOnDemandReport = false ; @@ -384,7 +383,7 @@ static void* CollectAndReportXconf(void* data) else { T2Debug("Abort upload is not yet set.\n"); - ret = sendReportOverHTTP(profile->t2HTTPDest->URL, jsonReport, &xconfReportPid); + ret = sendReportOverHTTP(profile->t2HTTPDest->URL, jsonReport); } #ifdef PERSIST_LOG_MON_REF @@ -401,7 +400,6 @@ static void* CollectAndReportXconf(void* data) } #endif - xconfReportPid = -1 ; if(ret == T2ERROR_FAILURE) { if(profile->cachedReportList != NULL && Vector_Size(profile->cachedReportList) >= MAX_CACHED_REPORTS) @@ -1003,43 +1001,3 @@ T2ERROR ProfileXConf_storeMarkerEvent(T2Event *eventInfo) return T2ERROR_SUCCESS; } - -T2ERROR ProfileXConf_terminateReport() -{ - - T2ERROR ret = T2ERROR_FAILURE; - - if(!singleProfile) - { - T2Error("Xconf profile is not set.\n"); - return ret; - } - - // Check whether any XconfReport is in progress - if(singleProfile->reportInProgress) - { - isAbortTriggered = true; - // Check if a child pid is still alive - if((xconfReportPid > 0) && !kill(xconfReportPid, 0)) - { - T2Info("Report upload in progress, terminating the forked reporting child : %d \n", xconfReportPid); - if(!kill(xconfReportPid, SIGKILL)) - { - ret = T2ERROR_SUCCESS; - } - } - else - { - T2Info(" Report upload has net yet started, set the abort flag \n"); - ret = T2ERROR_SUCCESS; - } - } - else - { - T2Info("No report generation in progress. No further action required for abort.\n"); - } - - return ret; - -} - diff --git a/source/bulkdata/profilexconf.h b/source/bulkdata/profilexconf.h index 8f46789c..c5065a7d 100644 --- a/source/bulkdata/profilexconf.h +++ b/source/bulkdata/profilexconf.h @@ -80,7 +80,6 @@ void ProfileXConf_updateMarkerComponentMap(); void ProfileXConf_notifyTimeout(bool isClearSeekMap, bool isOnDemand); T2ERROR ProfileXConf_storeMarkerEvent(T2Event *eventInfo); char* ProfileXconf_getName(); -T2ERROR ProfileXConf_terminateReport(); T2ERROR ReportProfiles_setProfileXConf(ProfileXConf *profile); diff --git a/source/bulkdata/reportprofiles.c b/source/bulkdata/reportprofiles.c index dbe48be5..78d473f6 100644 --- a/source/bulkdata/reportprofiles.c +++ b/source/bulkdata/reportprofiles.c @@ -434,7 +434,7 @@ static void* reportOnDemand(void *input) else if(!strncmp(action, ON_DEMAND_ACTION_ABORT, MAX_PROFILENAMES_LENGTH)) { T2Info("Abort report on demand \n"); - ProfileXConf_terminateReport(); + T2Info("Abort of the on-demand report is no longer supported; fork-based report execution and termination have been removed\n"); } else { @@ -704,11 +704,6 @@ T2ERROR ReportProfiles_uninit( ) { getMarkerCompRbusSub(false); // remove Rbus subscription } -#ifdef LIBRDKCERTSEL_BUILD - curlCertSelectorFree(); -#else - uninitMtls(); -#endif T2ER_Uninit(); destroyT2MarkerComponentMap(); uninitScheduler(); diff --git a/source/protocol/http/Makefile.am b/source/protocol/http/Makefile.am index 21ab5382..460bfa2a 100644 --- a/source/protocol/http/Makefile.am +++ b/source/protocol/http/Makefile.am @@ -20,11 +20,11 @@ AM_CFLAGS = lib_LTLIBRARIES = libhttp.la -libhttp_la_SOURCES = curlinterface.c +libhttp_la_SOURCES = curlinterface.c multicurlinterface.c libhttp_la_LDFLAGS = -shared -fPIC -lcurl if IS_LIBRDKCERTSEL_ENABLED libhttp_la_CFLAGS = $(LIBRDKCERTSEL_FLAG) -libhttp_la_LDFLAGS += -lRdkCertSelector +libhttp_la_LDFLAGS += -lRdkCertSelector -lrdkconfig endif libhttp_la_CPPFLAGS = -fPIC -I${PKG_CONFIG_SYSROOT_DIR}$(includedir)/dbus-1.0 \ -I${PKG_CONFIG_SYSROOT_DIR}$(libdir)/dbus-1.0/include \ diff --git a/source/protocol/http/curlinterface.c b/source/protocol/http/curlinterface.c index 98df178c..da7fd682 100644 --- a/source/protocol/http/curlinterface.c +++ b/source/protocol/http/curlinterface.c @@ -36,11 +36,14 @@ #include "reportprofiles.h" #include "t2MtlsUtils.h" #include "t2log_wrapper.h" +#include "multicurlinterface.h" #include "busInterface.h" +#ifndef LIBRDKCERTSEL_BUILD #ifdef LIBRDKCERTSEL_BUILD #include "rdkcertselector.h" #define FILESCHEME "file://" #endif +#endif #ifdef LIBRDKCONFIG_BUILD #include "rdkconfig.h" #endif @@ -60,20 +63,6 @@ typedef struct } childResponse ; -#ifdef LIBRDKCERTSEL_BUILD -static rdkcertselector_h curlCertSelector = NULL; -static rdkcertselector_h curlRcvryCertSelector = NULL; -#endif - -#if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) - -#if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) -static char waninterface[256]; -#endif -#endif -static pthread_once_t curlFileMutexOnce = PTHREAD_ONCE_INIT; -static pthread_mutex_t curlFileMutex; - typedef enum _ADDRESS_TYPE { ADDR_UNKNOWN, @@ -81,537 +70,29 @@ typedef enum _ADDRESS_TYPE ADDR_IPV6 } ADDRESS_TYPE; -static void sendOverHTTPInit() -{ - pthread_mutex_init(&curlFileMutex, NULL); -} - - -static size_t writeToFile(void *ptr, size_t size, size_t nmemb, void *stream) -{ - size_t written = fwrite(ptr, size, nmemb, (FILE *) stream); - return written; -} - -static T2ERROR setHeader(CURL *curl, const char* destURL, struct curl_slist **headerList, childResponse *childCurlResponse) -{ - - //T2Debug("%s ++in\n", __FUNCTION__); - if(curl == NULL || destURL == NULL) - { - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_FAILURE; - } - - //T2Debug("%s DEST URL %s \n", __FUNCTION__, destURL); - CURLcode code = CURLE_OK; - code = curl_easy_setopt(curl, CURLOPT_URL, destURL); - if(code != CURLE_OK) - { - childCurlResponse->curlSetopCode = code; - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_FAILURE; - } - code = curl_easy_setopt(curl, CURLOPT_SSLVERSION, TLSVERSION); - if(code != CURLE_OK) - { - childCurlResponse->curlSetopCode = code; - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_FAILURE; - } - code = curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, HTTP_METHOD); - if(code != CURLE_OK) - { - childCurlResponse->curlSetopCode = code; - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_FAILURE; - } - code = curl_easy_setopt(curl, CURLOPT_TIMEOUT, TIMEOUT); - if(code != CURLE_OK) - { - childCurlResponse->curlSetopCode = code; - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_FAILURE; - } - -#if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) - -#if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) - code = curl_easy_setopt(curl, CURLOPT_INTERFACE, waninterface); - if(code != CURLE_OK) - { - childCurlResponse->curlSetopCode = code; - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_FAILURE; - } -#else - /* CID 125287: Unchecked return value from library */ - code = curl_easy_setopt(curl, CURLOPT_INTERFACE, INTERFACE); - if(code != CURLE_OK) - { - childCurlResponse->curlSetopCode = code; - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_FAILURE; - } - -#endif -#endif - *headerList = curl_slist_append(NULL, "Accept: application/json"); - curl_slist_append(*headerList, "Content-type: application/json"); - - code = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, *headerList); - if(code != CURLE_OK) - { - childCurlResponse->curlSetopCode = code; - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_FAILURE; - } - - code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeToFile); - if(code != CURLE_OK) - { - childCurlResponse->curlSetopCode = code; - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_FAILURE; - } - childCurlResponse->curlSetopCode = code; - childCurlResponse->lineNumber = __LINE__; - //T2Debug("%s --out\n", __FUNCTION__); - return T2ERROR_SUCCESS; -} - -static T2ERROR setMtlsHeaders(CURL *curl, const char* certFile, const char* pPasswd, childResponse *childCurlResponse) -{ - if(curl == NULL || certFile == NULL || pPasswd == NULL) - { - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_FAILURE; - } - CURLcode code = CURLE_OK; - code = curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "P12"); - if(code != CURLE_OK) - { - childCurlResponse->curlSetopCode = code; - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_FAILURE; - } - /* set the cert for client authentication */ - code = curl_easy_setopt(curl, CURLOPT_SSLCERT, certFile); - if(code != CURLE_OK) - { - childCurlResponse->curlSetopCode = code; - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_FAILURE; - } - code = curl_easy_setopt(curl, CURLOPT_KEYPASSWD, pPasswd); - if(code != CURLE_OK) - { - childCurlResponse->curlSetopCode = code; - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_FAILURE; - } - /* disconnect if we cannot authenticate */ - code = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); - if(code != CURLE_OK) - { - childCurlResponse->curlSetopCode = code; - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_FAILURE; - } - childCurlResponse->curlSetopCode = code; - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_SUCCESS; -} - -static T2ERROR setPayload(CURL *curl, const char* payload, childResponse *childCurlResponse) -{ - if(curl == NULL || payload == NULL) - { - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_FAILURE; - } - CURLcode code = CURLE_OK ; - code = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload); - if(code != CURLE_OK) - { - childCurlResponse->curlSetopCode = code; - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_FAILURE; - } - code = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(payload)); - if(code != CURLE_OK) - { - childCurlResponse->curlSetopCode = code; - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_FAILURE; - } - childCurlResponse->curlSetopCode = code; - childCurlResponse->lineNumber = __LINE__; - return T2ERROR_SUCCESS; -} -#ifdef LIBRDKCERTSEL_BUILD -#if defined(ENABLE_RED_RECOVERY_SUPPORT) -bool isStateRedEnabled(void) -{ - return access("/tmp/stateRedEnabled", F_OK) == 0; -} -#endif -void curlCertSelectorFree() -{ - rdkcertselector_free(&curlCertSelector); - rdkcertselector_free(&curlRcvryCertSelector); - if(curlCertSelector == NULL || curlRcvryCertSelector == NULL) - { - T2Info("%s, T2:Cert selector memory free\n", __func__); - } - else - { - T2Info("%s, T2:Cert selector memory free failed\n", __func__); - } -} -static void curlCertSelectorInit() -{ - bool state_red_enable = false; -#if defined(ENABLE_RED_RECOVERY_SUPPORT) - state_red_enable = isStateRedEnabled(); -#endif - if (state_red_enable && curlRcvryCertSelector == NULL ) - { - curlRcvryCertSelector = rdkcertselector_new( NULL, NULL, "RCVRY" ); - if (curlRcvryCertSelector == NULL) - { - T2Error("%s, T2:statered Cert selector initialization failed\n", __func__); - } - else - { - T2Info("%s, T2:statered Cert selector initialization successfully\n", __func__); - } - } - else - { - if (curlCertSelector == NULL) - { - curlCertSelector = rdkcertselector_new( NULL, NULL, "MTLS" ); - if (curlCertSelector == NULL) - { - T2Error("%s, T2:Cert selector initialization failed\n", __func__); - } - else - { - T2Info("%s, T2:Cert selector initialization successfully\n", __func__); - } - } - } -} -#endif - -T2ERROR sendReportOverHTTP(char *httpUrl, char *payload, pid_t* outForkedPid) +T2ERROR sendReportOverHTTP(char *httpUrl, char *payload) { - CURL *curl = NULL; - FILE *fp = NULL; - CURLcode code = CURLE_OK; T2ERROR ret = T2ERROR_FAILURE; - childResponse childCurlResponse = {0}; - struct curl_slist *headerList = NULL; - CURLcode curl_code = CURLE_OK; -#ifdef LIBRDKCERTSEL_BUILD - rdkcertselector_h thisCertSel = NULL; - rdkcertselectorStatus_t curlGetCertStatus; - char *pCertURI = NULL; - bool state_red_enable = false; -#endif - char *pCertFile = NULL; - char *pCertPC = NULL; -#ifdef LIBRDKCONFIG_BUILD - size_t sKey = 0; -#endif - long http_code; - bool mtls_enable = false; - pid_t childPid; - int sharedPipeFds[2]; T2Debug("%s ++in\n", __FUNCTION__); if(httpUrl == NULL || payload == NULL) { return ret; } - if(pipe(sharedPipeFds) != 0) - { - T2Error("Failed to create pipe !!! exiting...\n"); - T2Debug("%s --out\n", __FUNCTION__); - return ret; - } -#ifdef LIBRDKCERTSEL_BUILD - curlCertSelectorInit(); -#if defined(ENABLE_RED_RECOVERY_SUPPORT) - state_red_enable = isStateRedEnabled(); - T2Info("%s: state_red_enable: %d\n", __func__, state_red_enable ); -#endif - if (state_red_enable) - { - thisCertSel = curlRcvryCertSelector; - } - else - { - thisCertSel = curlCertSelector; - } -#endif -#if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) - -#if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) - char *paramVal = NULL; - memset(waninterface, 0, sizeof(waninterface)); - snprintf(waninterface, sizeof(waninterface), "%s", INTERFACE); - - if(T2ERROR_SUCCESS == getParameterValue(TR181_DEVICE_CURRENT_WAN_IFNAME, ¶mVal)) - { - if(strlen(paramVal) > 0) - { - memset(waninterface, 0, sizeof(waninterface)); - snprintf(waninterface, sizeof(waninterface), "%s", paramVal); - } - - free(paramVal); - paramVal = NULL; - } - else - { - T2Error("Failed to get Value for %s\n", TR181_DEVICE_CURRENT_WAN_IFNAME); - } -#endif -#endif - mtls_enable = isMtlsEnabled(); -#ifndef LIBRDKCERTSEL_BUILD - if(mtls_enable == true && T2ERROR_SUCCESS != getMtlsCerts(&pCertFile, &pCertPC)) - { - T2Error("mTLS_cert get failed\n"); - if(NULL != pCertFile) - { - free(pCertFile); - } - if(NULL != pCertPC) - { -#ifdef LIBRDKCONFIG_BUILD - sKey = strlen(pCertPC); - if (rdkconfig_free((unsigned char**)&pCertPC, sKey) == RDKCONFIG_FAIL) - { - return T2ERROR_FAILURE; - } -#else - free(pCertPC); -#endif - } - return ret; - } -#endif - // Block the userdefined signal handlers before fork - pthread_sigmask(SIG_BLOCK, &blocking_signal, NULL); - if((childPid = fork()) < 0) - { - T2Error("Failed to fork !!! exiting...\n"); - // Unblock the userdefined signal handler -#ifndef LIBRDKCERTSEL_BUILD - if(NULL != pCertFile) - { - free(pCertFile); - } - if(NULL != pCertPC) - { -#ifdef LIBRDKCONFIG_BUILD - sKey = strlen(pCertPC); - if (rdkconfig_free((unsigned char**)&pCertPC, sKey) == RDKCONFIG_FAIL) - { - return T2ERROR_FAILURE; - } -#else - free(pCertPC); -#endif - } -#endif - pthread_sigmask(SIG_UNBLOCK, &blocking_signal, NULL); - T2Debug("%s --out\n", __FUNCTION__); - return T2ERROR_FAILURE; - } + // Use new dedicated POST API + ret = http_pool_post(httpUrl, payload); - /** - * Openssl has growing RSS which gets cleaned up only with OPENSSL_cleanup . - * This cleanup is not thread safe and classified as run once per application life cycle. - * Forking the libcurl calls so that it executes and terminates to release memory per execution. - */ - if(childPid == 0) + if(ret == T2ERROR_SUCCESS) { - curl = curl_easy_init(); - if(curl) - { - childCurlResponse.curlStatus = true; - if(setHeader(curl, httpUrl, &headerList, &childCurlResponse) != T2ERROR_SUCCESS) - { - curl_easy_cleanup(curl); - goto child_cleanReturn; - } - if (setPayload(curl, payload, &childCurlResponse) != T2ERROR_SUCCESS) - { - curl_easy_cleanup(curl); // CID 189985: Resource leak - goto child_cleanReturn; - } -#ifdef LIBRDKCERTSEL_BUILD - do - { - pCertFile = NULL; - pCertPC = NULL; - pCertURI = NULL; - curlGetCertStatus = rdkcertselector_getCert(thisCertSel, &pCertURI, &pCertPC); - if(curlGetCertStatus != certselectorOk) - { - T2Error("%s, T2:Failed to retrieve the certificate.\n", __func__); - curlCertSelectorFree(); - curl_easy_cleanup(curl); - goto child_cleanReturn; - } - else - { - // skip past file scheme in URI - pCertFile = pCertURI; - if ( strncmp( pCertFile, FILESCHEME, sizeof(FILESCHEME) - 1 ) == 0 ) - { - pCertFile += (sizeof(FILESCHEME) - 1); - } -#endif - if((mtls_enable == true) && (setMtlsHeaders(curl, pCertFile, pCertPC, &childCurlResponse) != T2ERROR_SUCCESS)) - { - curl_easy_cleanup(curl); // CID 189985: Resource leak - goto child_cleanReturn; - } - pthread_once(&curlFileMutexOnce, sendOverHTTPInit); - pthread_mutex_lock(&curlFileMutex); - - fp = fopen(CURL_OUTPUT_FILE, "wb"); - if(fp) - { - /* CID 143029 Unchecked return value from library */ - code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)fp); - if(code != CURLE_OK) - { - // This might not be working we need to review this - childCurlResponse.curlSetopCode = code; - } - curl_code = curl_easy_perform(curl); - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - if(curl_code != CURLE_OK || http_code != 200) - { -#ifdef LIBRDKCERTSEL_BUILD - T2Error("%s: Failed to establish connection using xPKI certificate: %s, curl failed: %d\n", __func__, pCertFile, curl_code); -#endif - fprintf(stderr, "curl failed: %s\n", curl_easy_strerror(curl_code)); - childCurlResponse.lineNumber = __LINE__; - } - else - { - childCurlResponse.lineNumber = __LINE__; - } - childCurlResponse.curlResponse = curl_code; - childCurlResponse.http_code = http_code; - - fclose(fp); - } - pthread_mutex_unlock(&curlFileMutex); -#ifdef LIBRDKCERTSEL_BUILD - } - } - while(rdkcertselector_setCurlStatus(thisCertSel, curl_code, (const char*)httpUrl) == TRY_ANOTHER); -#endif - curl_slist_free_all(headerList); - curl_easy_cleanup(curl); - } - else - { - childCurlResponse.curlStatus = false; - } - -child_cleanReturn : -#ifndef LIBRDKCERTSEL_BUILD - if(NULL != pCertFile) - { - free(pCertFile); - } - - if(NULL != pCertPC) - { -#ifdef LIBRDKCONFIG_BUILD - sKey = strlen(pCertPC); - if (rdkconfig_free((unsigned char**)&pCertPC, sKey) == RDKCONFIG_FAIL) - { - return T2ERROR_FAILURE; - } -#else - free(pCertPC); -#endif - } -#endif - close(sharedPipeFds[0]); - if( -1 == write(sharedPipeFds[1], &childCurlResponse, sizeof(childResponse))) - { - fprintf(stderr, "unable to write to shared pipe from pid : %d \n", getpid()); - T2Error("unable to write \n"); - } - close(sharedPipeFds[1]); - exit(0); - + T2Debug("Report Sent Successfully over HTTP using connection pool\n"); } else { - T2ERROR ret = T2ERROR_FAILURE; - if(outForkedPid) - { - *outForkedPid = childPid ; - } - // Use waitpid insted of wait we can have multiple child process - waitpid(childPid, NULL, 0); - // Unblock the userdefined signal handlers before fork - pthread_sigmask(SIG_UNBLOCK, &blocking_signal, NULL); - // Get the return status via IPC from child process - if ( -1 == close(sharedPipeFds[1])) - { - T2Error("Failed in close \n"); - } - if( -1 == read(sharedPipeFds[0], &childCurlResponse, sizeof(childResponse))) - { - T2Error("unable to read from the pipe \n"); - } - if ( -1 == close(sharedPipeFds[0])) - { - T2Error("Failed in close the pipe\n"); - } -#ifndef LIBRDKCERTSEL_BUILD - if(NULL != pCertFile) - { - free(pCertFile); - } - if(NULL != pCertPC) - { -#ifdef LIBRDKCONFIG_BUILD - sKey = strlen(pCertPC); - if (rdkconfig_free((unsigned char**)&pCertPC, sKey) == RDKCONFIG_FAIL) - { - return T2ERROR_FAILURE; - } -#else - free(pCertPC); -#endif - } -#endif - T2Info("The return status from the child with pid %d is CurlStatus : %d\n", childPid, childCurlResponse.curlStatus); - //if(childCurlResponse.curlStatus == CURLE_OK) commenting this as we are observing childCurlResponse.curlStatus as 1, from line with CID 143029 Unchecked return value from library - T2Info("The return status from the child with pid %d SetopCode: %s; ResponseCode : %s; HTTP_CODE : %ld; Line Number : %d \n", childPid, curl_easy_strerror(childCurlResponse.curlSetopCode), curl_easy_strerror(childCurlResponse.curlResponse), childCurlResponse.http_code, childCurlResponse.lineNumber); - if (childCurlResponse.http_code == 200 || childCurlResponse.curlResponse == CURLE_OK) - { - ret = T2ERROR_SUCCESS; - T2Info("Report Sent Successfully over HTTP : %ld\n", childCurlResponse.http_code); - } - T2Debug("%s --out\n", __FUNCTION__); - return ret; + T2Error("Failed to send report\n"); } + T2Debug("%s --out\n", __FUNCTION__); + return ret; } T2ERROR sendCachedReportsOverHTTP(char *httpUrl, Vector *reportList) @@ -623,7 +104,7 @@ T2ERROR sendCachedReportsOverHTTP(char *httpUrl, Vector *reportList) while(Vector_Size(reportList) > 0) { char* payload = (char *)Vector_At(reportList, 0); - if(T2ERROR_FAILURE == sendReportOverHTTP(httpUrl, payload, NULL)) + if(T2ERROR_FAILURE == sendReportOverHTTP(httpUrl, payload)) { T2Error("Failed to send cached report, left with %lu reports in cache \n", (unsigned long)Vector_Size(reportList)); return T2ERROR_FAILURE; diff --git a/source/protocol/http/curlinterface.h b/source/protocol/http/curlinterface.h index 71777e63..2d70d11a 100644 --- a/source/protocol/http/curlinterface.h +++ b/source/protocol/http/curlinterface.h @@ -35,11 +35,7 @@ #define HEADER_ACCEPT "Accept: application/json" #define HEADER_CONTENTTYPE "Content-type: application/json" -#if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) -#define TR181_DEVICE_CURRENT_WAN_IFNAME "Device.X_RDK_WanManager.CurrentActiveInterface" -#endif - -T2ERROR sendReportOverHTTP(char *httpUrl, char* payload, pid_t* outForkedPid); +T2ERROR sendReportOverHTTP(char *httpUrl, char* payload); T2ERROR sendCachedReportsOverHTTP(char *httpUrl, Vector *reportList); diff --git a/source/protocol/http/multicurlinterface.c b/source/protocol/http/multicurlinterface.c new file mode 100644 index 00000000..2d0113dc --- /dev/null +++ b/source/protocol/http/multicurlinterface.c @@ -0,0 +1,1149 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2019 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "multicurlinterface.h" +#include "busInterface.h" +#include "t2log_wrapper.h" +#include "reportprofiles.h" +#include "t2MtlsUtils.h" +#ifdef LIBRDKCERTSEL_BUILD +#include "rdkcertselector.h" +#define FILESCHEME "file://" +#endif + +// Macro to check curl_easy_setopt return value and log errors +#define CURL_SETOPT_CHECK(handle, option, value) \ + do { \ + CURLcode _curl_code = curl_easy_setopt(handle, option, value); \ + if(_curl_code != CURLE_OK) { \ + T2Error("%s : Failed to set %s=%s, error: %s\n", __FUNCTION__, #option, #value, curl_easy_strerror(_curl_code)); \ + } \ + } while(0) + +// Macro for string values that need proper formatting +#define CURL_SETOPT_CHECK_STR(handle, option, value) \ + do { \ + CURLcode _curl_code = curl_easy_setopt(handle, option, value); \ + if(_curl_code != CURLE_OK) { \ + const char* _val_str = (const char*)(value); \ + T2Error("%s : Failed to set %s=%s, error: %s\n", __FUNCTION__, #option, _val_str ? _val_str : "NULL", curl_easy_strerror(_curl_code)); \ + } \ + } while(0) + +//Global variables +#define IFINTERFACE "erouter0" +#define DEFAULT_POOL_SIZE 1 +#define MAX_ALLOWED_POOL_SIZE 5 // Maximum allowed pool size +#define MIN_ALLOWED_POOL_SIZE 1 // Minimum allowed pool size +#define HTTP_RESPONSE_FILE "/tmp/httpOutput.txt" +#define POOL_ACQUIRE_TIMEOUT_SEC 35 +#define POOL_ACQUIRE_RETRY_MS 100 +static bool pool_initialized = false; +static bool pool_shutting_down = false; + +// pool_mutex protects pool state and synchronizes access to pool entries +static pthread_mutex_t pool_mutex = PTHREAD_MUTEX_INITIALIZER; + +// Single pool entry structure +typedef struct +{ + CURL *easy_handle; // Single CURL handle for this pool entry + bool handle_available; // Availability flag for this pool entry +#ifdef LIBRDKCERTSEL_BUILD + rdkcertselector_h cert_selector; // Single cert selector for this pool entry + rdkcertselector_h rcvry_cert_selector; // Single recovery cert selector for this pool entry +#endif +} http_pool_entry_t; + +// Global pool variables +static http_pool_entry_t *pool_entries = NULL; // Dynamic array of pool entries +static struct curl_slist *post_headers = NULL; // Shared POST headers +static int pool_size = 0; // Number of pool entries +static unsigned int active_requests = 0; // Number of in-flight curl_easy_perform() calls + +#ifdef LIBRDKCERTSEL_BUILD +#if defined(ENABLE_RED_RECOVERY_SUPPORT) +bool isStateRedEnabled(void) +{ + return access("/tmp/stateRedEnabled", F_OK) == 0; +} +#endif +#endif + +// Helper function to read pool size from environment variable +static int get_configured_pool_size(void) +{ + int configured_size = DEFAULT_POOL_SIZE; + + // Check environment variable T2_CONNECTION_POOL_SIZE to have the size + // of the connection pool based on the device type + const char *env_size = getenv("T2_CONNECTION_POOL_SIZE"); + if (env_size != NULL) + { + int env_value = atoi(env_size); + if (env_value >= MIN_ALLOWED_POOL_SIZE && env_value <= MAX_ALLOWED_POOL_SIZE) + { + configured_size = env_value; + T2Info("Curl connection pool size set from environment: %d\n", configured_size); + } + else + { + T2Error("Invalid pool size in T2_CONNECTION_POOL_SIZE=%s, must be between %d and %d. Using default: %d\n", + env_size, MIN_ALLOWED_POOL_SIZE, MAX_ALLOWED_POOL_SIZE, DEFAULT_POOL_SIZE); + } + } + else + { + T2Info("T2_CONNECTION_POOL_SIZE not set, using default pool size: %d\n", DEFAULT_POOL_SIZE); + } + + return configured_size; +} + +static size_t httpGetCallBack(void *responseBuffer, size_t len, size_t nmemb, + void *stream) +{ + T2Debug("%s ++in\n", __FUNCTION__); + size_t realsize = len * nmemb; + curlResponseData* response = (curlResponseData*) stream; + + if (!response) + { + T2Error("httpGetCallBack: NULL stream parameter\n"); + return 0; + } + + char *ptr = (char*) realloc(response->data, + response->size + realsize + 1); + if (!ptr) + { + T2Error("%s:%u , T2:memory realloc failed\n", __func__, __LINE__); + return 0; + } + response->data = ptr; + memcpy(&(response->data[response->size]), responseBuffer, realsize); + response->size += realsize; + response->data[response->size] = 0; + + T2Debug("%s ++out\n", __FUNCTION__); + return realsize; +} + +static void cleanup_curl_handles(void) +{ + T2Debug("%s ++in\n", __FUNCTION__); + + if(post_headers) + { + curl_slist_free_all(post_headers); + post_headers = NULL; + } + +#ifdef LIBRDKCERTSEL_BUILD + // Clean up per-entry certificate selectors + if(pool_entries) + { + for(int i = 0; i < pool_size; i++) + { + if(pool_entries[i].cert_selector) + { + T2Info("Freeing cert_selector for entry %d\n", i); + rdkcertselector_free(&pool_entries[i].cert_selector); + pool_entries[i].cert_selector = NULL; + } + if(pool_entries[i].rcvry_cert_selector) + { + T2Info("Freeing rcvry_cert_selector for entry %d\n", i); + rdkcertselector_free(&pool_entries[i].rcvry_cert_selector); + pool_entries[i].rcvry_cert_selector = NULL; + } + } + } +#endif + + if(pool_entries) + { + for(int i = 0; i < pool_size; i++) + { + if(pool_entries[i].easy_handle) + { + curl_easy_cleanup(pool_entries[i].easy_handle); + pool_entries[i].easy_handle = NULL; + } + } + free(pool_entries); + pool_entries = NULL; + } + + pool_size = 0; + + T2Debug("%s ++out\n", __FUNCTION__); +} + +T2ERROR init_connection_pool() +{ + T2Debug("%s ++in\n", __FUNCTION__); + + pthread_mutex_lock(&pool_mutex); + + if(pool_initialized) + { + T2Info("Connection pool already initialized with size %d\n", pool_size); + pthread_mutex_unlock(&pool_mutex); + return T2ERROR_SUCCESS; + } + + //Get configured pool size from environment variable for low end devices + pool_size = get_configured_pool_size(); + T2Debug("Initializing connection pool with size: %d\n", pool_size); + pool_entries = (http_pool_entry_t *)calloc(pool_size, sizeof(http_pool_entry_t)); + + if (!pool_entries) + { + T2Error("Failed to allocate memory for connection pool of size %d\n", pool_size); + cleanup_curl_handles(); + pthread_mutex_unlock(&pool_mutex); + return T2ERROR_FAILURE; + } + + // Allocating easy handles and initialize pool entries + for(int i = 0; i < pool_size; i++) + { + pool_entries[i].easy_handle = curl_easy_init(); + if(pool_entries[i].easy_handle == NULL) + { + T2Error("%s : Failed to initialize curl handle %d\n", __FUNCTION__, i); + // Cleanup previously initialized handles to prevent memory leak + cleanup_curl_handles(); + pthread_mutex_unlock(&pool_mutex); + return T2ERROR_FAILURE; + } + pool_entries[i].handle_available = true; + + // Set common options for each handle that persist across requests + CURL_SETOPT_CHECK(pool_entries[i].easy_handle, CURLOPT_TIMEOUT, 30L); + CURL_SETOPT_CHECK(pool_entries[i].easy_handle, CURLOPT_CONNECTTIMEOUT, 10L); + + // TCP keepalive settings + CURL_SETOPT_CHECK(pool_entries[i].easy_handle, CURLOPT_TCP_KEEPALIVE, 1L); + CURL_SETOPT_CHECK(pool_entries[i].easy_handle, CURLOPT_TCP_KEEPIDLE, 50L); + CURL_SETOPT_CHECK(pool_entries[i].easy_handle, CURLOPT_TCP_KEEPINTVL, 30L); + +#ifdef CURLOPT_TCP_KEEPCNT + CURL_SETOPT_CHECK(pool_entries[i].easy_handle, CURLOPT_TCP_KEEPCNT, 15L); +#endif + + // Connection reuse settings + CURL_SETOPT_CHECK(pool_entries[i].easy_handle, CURLOPT_FORBID_REUSE, 0L); + CURL_SETOPT_CHECK(pool_entries[i].easy_handle, CURLOPT_FRESH_CONNECT, 0L); + + // Socket options + CURL_SETOPT_CHECK(pool_entries[i].easy_handle, CURLOPT_NOSIGNAL, 1L); + + // HTTP version and SSL settings + CURL_SETOPT_CHECK(pool_entries[i].easy_handle, CURLOPT_PIPEWAIT, 0L); + CURL_SETOPT_CHECK(pool_entries[i].easy_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + //SSL automatically negotiates the highest SSL/TLS version supported by both client and server + CURL_SETOPT_CHECK(pool_entries[i].easy_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_DEFAULT); + CURL_SETOPT_CHECK(pool_entries[i].easy_handle, CURLOPT_SSL_VERIFYPEER, 1L); + +#ifdef LIBRDKCERTSEL_BUILD + // Initialize certificate selectors for each easy handle + bool state_red_enable = false; +#if defined(ENABLE_RED_RECOVERY_SUPPORT) + state_red_enable = isStateRedEnabled(); +#endif + + if (state_red_enable) + { + // Initialize recovery certificate selector while in state red + pool_entries[i].rcvry_cert_selector = rdkcertselector_new(NULL, NULL, "RCVRY"); + if (pool_entries[i].rcvry_cert_selector == NULL) + { + T2Error("%s: Failed to initialize recovery cert selector for entry %d\n", __func__, i); + cleanup_curl_handles(); + pthread_mutex_unlock(&pool_mutex); + return T2ERROR_FAILURE; + } + else + { + T2Info("%s: Initialized recovery cert selector for entry %d\n", __func__, i); + } + pool_entries[i].cert_selector = NULL; + } + else + { + // Initialize normal certificate selector while not in red state + pool_entries[i].cert_selector = rdkcertselector_new(NULL, NULL, "MTLS"); + if (pool_entries[i].cert_selector == NULL) + { + T2Error("%s: Failed to initialize cert selector for entry %d\n", __func__, i); + cleanup_curl_handles(); + pthread_mutex_unlock(&pool_mutex); + return T2ERROR_FAILURE; + } + else + { + T2Info("%s: Initialized cert selector for entry %d\n", __func__, i); + } + pool_entries[i].rcvry_cert_selector = NULL; + } +#endif + } + + post_headers = curl_slist_append(NULL, "Accept: application/json"); + if (post_headers == NULL) + { + T2Error("%s : Failed to append HTTP header: Accept: application/json\n", __FUNCTION__); + cleanup_curl_handles(); + pthread_mutex_unlock(&pool_mutex); + return T2ERROR_FAILURE; + } + + post_headers = curl_slist_append(post_headers, "Content-type: application/json"); + if (post_headers == NULL) + { + T2Error("%s : Failed to append HTTP header: Content-type: application/json\n", __FUNCTION__); + curl_slist_free_all(post_headers); + post_headers = NULL; + cleanup_curl_handles(); + pthread_mutex_unlock(&pool_mutex); + return T2ERROR_FAILURE; + } + + pool_initialized = true; + pthread_mutex_unlock(&pool_mutex); + T2Debug("%s ++out\n", __FUNCTION__); + return T2ERROR_SUCCESS; +} + + +static T2ERROR acquire_pool_handle(CURL **easy, int *idx) +{ + struct timespec start_time, current_time; + if (clock_gettime(CLOCK_MONOTONIC, &start_time) != 0) + { + T2Error("clock_gettime failed for start_time: %s\n", strerror(errno)); + return T2ERROR_FAILURE; + } + + pthread_mutex_lock(&pool_mutex); + bool need_init = !pool_initialized && !pool_shutting_down; + pthread_mutex_unlock(&pool_mutex); + + if (need_init) + { + T2ERROR ret = init_connection_pool(); + if(ret != T2ERROR_SUCCESS) + { + T2Error("Failed to initialize connection pool\n"); + return ret; + } + } + + // Poll for an available handle with a bounded timeout. + // If no handle is available within POOL_ACQUIRE_TIMEOUT_SEC, treat as upload failure. + while(1) + { + pthread_mutex_lock(&pool_mutex); + + if (pool_shutting_down || !pool_initialized) + { + pthread_mutex_unlock(&pool_mutex); + T2Info("Pool is shutting down or not initialized, aborting handle acquisition\n"); + return T2ERROR_FAILURE; + } + + // Try to find an available handle + for(int i = 0; i < pool_size; i++) + { + if(pool_entries[i].handle_available) + { + T2Info("Acquired handle = %d (pool size: %d)\n", i, pool_size); + *idx = i; + pool_entries[i].handle_available = false; + *easy = pool_entries[i].easy_handle; + + // Guard against overflow + if (active_requests >= (unsigned int)pool_size) + { + T2Error("Warning: active_requests (%u) >= pool_size (%d) before increment\n", active_requests, pool_size); + } + active_requests++; + pthread_mutex_unlock(&pool_mutex); + return T2ERROR_SUCCESS; + } + } + + pthread_mutex_unlock(&pool_mutex); + + // Check elapsed time against timeout + if (clock_gettime(CLOCK_MONOTONIC, ¤t_time) != 0) + { + T2Error("clock_gettime failed for current_time: %s\n", strerror(errno)); + return T2ERROR_FAILURE; + } + long elapsed_sec = current_time.tv_sec - start_time.tv_sec; + + if (elapsed_sec >= POOL_ACQUIRE_TIMEOUT_SEC) + { + T2Error("Timeout waiting for available curl handle after %ld seconds, treating as upload failure\n", elapsed_sec); + return T2ERROR_FAILURE; + } + + // Sleep briefly before retrying + T2Debug("No curl handle available (pool size: %d), retrying in %dms...\n", pool_size, POOL_ACQUIRE_RETRY_MS); + usleep(POOL_ACQUIRE_RETRY_MS * 1000); + } +} + +static void release_pool_handle(int idx) +{ + pthread_mutex_lock(&pool_mutex); + if (idx >= 0 && idx < pool_size) + { + pool_entries[idx].handle_available = true; + active_requests--; + T2Info("Released curl handle = %d, active_requests = %d\n", idx, active_requests); + } + else + { + T2Error("Invalid curl handle index = %d (pool size: %d)\n", idx, pool_size); + } + pthread_mutex_unlock(&pool_mutex); +} + +#if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) +static void configure_wan_interface(CURL *easy) +{ + T2Debug("%s ++in\n", __FUNCTION__); +#if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) + char *paramVal = NULL; + char waninterface[256]; + memset(waninterface, 0, sizeof(waninterface)); + snprintf(waninterface, sizeof(waninterface), "%s", IFINTERFACE); + + if(T2ERROR_SUCCESS == getParameterValue(TR181_DEVICE_CURRENT_WAN_IFNAME, ¶mVal)) + { + if(strlen(paramVal) > 0) + { + memset(waninterface, 0, sizeof(waninterface)); + snprintf(waninterface, sizeof(waninterface), "%s", paramVal); + } + free(paramVal); + paramVal = NULL; + } + else + { + T2Error("Failed to get Value for %s\n", TR181_DEVICE_CURRENT_WAN_IFNAME); + } + + CURL_SETOPT_CHECK_STR(easy, CURLOPT_INTERFACE, waninterface); + T2Debug("TR181_DEVICE_CURRENT_WAN_IFNAME ---- %s\n", waninterface); +#else + CURL_SETOPT_CHECK(easy, CURLOPT_INTERFACE, "erouter0"); +#endif +} +#endif + + +T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_output) +{ + T2Debug("%s ++in\n", __FUNCTION__); + + if (!url) + { + T2Error("Invalid URL parameter\n"); + return T2ERROR_FAILURE; + } + + T2Info("GET url = %s\n", url); + + CURL *easy; + int idx = -1; + + T2ERROR ret = acquire_pool_handle(&easy, &idx); + if(ret != T2ERROR_SUCCESS) + { + T2Error("Failed to acquire pool handle\n"); + return ret; + } + + // Clear any POST-specific settings so that the handle can be used for GET operations + // curl_easy_perform ends up in crash when POST specific options are not cleared + CURL_SETOPT_CHECK(easy, CURLOPT_POSTFIELDS, NULL); + CURL_SETOPT_CHECK(easy, CURLOPT_POSTFIELDSIZE, 0L); + CURL_SETOPT_CHECK(easy, CURLOPT_HTTPHEADER, NULL); + + // Allocate response buffer locally for GET requests only + curlResponseData* response = (curlResponseData *) malloc(sizeof(curlResponseData)); + if (!response) + { + T2Error("Failed to allocate response structure\n"); + release_pool_handle(idx); + return T2ERROR_FAILURE; + } + + response->data = (char*)malloc(1); + if (!response->data) + { + T2Error("Failed to allocate response data buffer\n"); + free(response); + release_pool_handle(idx); + return T2ERROR_FAILURE; + } + + response->data[0] = '\0'; + response->size = 0; + + // Configure request-specific options for GET + CURL_SETOPT_CHECK_STR(easy, CURLOPT_URL, url); + CURL_SETOPT_CHECK(easy, CURLOPT_HTTPGET, 1L); + CURL_SETOPT_CHECK(easy, CURLOPT_WRITEFUNCTION, httpGetCallBack); + CURL_SETOPT_CHECK(easy, CURLOPT_WRITEDATA, (void *) response); + +#if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) + configure_wan_interface(easy); +#endif + bool mtls_enable = isMtlsEnabled(); + char *pCertFile = NULL; + char *pCertPC = NULL; +#ifdef LIBRDKCERTSEL_BUILD + char *pCertURI = NULL; +#endif + CURLcode curl_code = CURLE_OK; + + if(mtls_enable == true) + { +#ifdef LIBRDKCERTSEL_BUILD + rdkcertselector_h handleCertSelector = pool_entries[idx].cert_selector; + rdkcertselectorStatus_t xcGetCertStatus; + + T2Debug("%s: Using cert selector for entry %d\n", __func__, idx); + do + { + pCertURI = NULL; + pCertPC = NULL; + pCertFile = NULL; + + xcGetCertStatus = rdkcertselector_getCert(handleCertSelector, &pCertURI, &pCertPC); + if(xcGetCertStatus != certselectorOk) + { + T2Error("%s, T2:Failed to retrieve the certificate for entry %d.\n", __func__, idx); + if (response) + { + if (response->data) + { + free(response->data); + } + free(response); + } + release_pool_handle(idx); + return T2ERROR_FAILURE; + } + else + { + // skip past file scheme in URI + pCertFile = pCertURI; + if ( strncmp( pCertFile, FILESCHEME, sizeof(FILESCHEME) - 1 ) == 0 ) + { + pCertFile += (sizeof(FILESCHEME) - 1); + } + + // Configure mTLS certificates + CURL_SETOPT_CHECK_STR(easy, CURLOPT_SSLCERTTYPE, "P12"); + CURL_SETOPT_CHECK_STR(easy, CURLOPT_SSLCERT, pCertFile); + CURL_SETOPT_CHECK_STR(easy, CURLOPT_KEYPASSWD, pCertPC); + CURL_SETOPT_CHECK(easy, CURLOPT_SSL_VERIFYPEER, 1L); + + // Execute the request and retry incase of certificate related error + curl_code = curl_easy_perform(easy); + + long http_code; + curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &http_code); + + if(curl_code != CURLE_OK || http_code != 200) + { + T2Error("%s: Failed to establish connection using xPKI certificate: %s, Curl failed : %d\n", __func__, pCertFile, curl_code); + } + else + { + T2Info("%s: Using xpki Certs connection certname : %s \n", __FUNCTION__, pCertFile); + } + } + } + while(rdkcertselector_setCurlStatus(handleCertSelector, curl_code, (const char*)url) == TRY_ANOTHER); +#else + // Fallback to getMtlsCerts if certificate selector not available + if(T2ERROR_SUCCESS == getMtlsCerts(&pCertFile, &pCertPC)) + { + CURL_SETOPT_CHECK_STR(easy, CURLOPT_SSLCERTTYPE, "P12"); + CURL_SETOPT_CHECK_STR(easy, CURLOPT_SSLCERT, pCertFile); + CURL_SETOPT_CHECK_STR(easy, CURLOPT_KEYPASSWD, pCertPC); + CURL_SETOPT_CHECK(easy, CURLOPT_SSL_VERIFYPEER, 1L); + + curl_code = curl_easy_perform(easy); + } + else + { + T2Error("mTLS_get failure\n"); + if (response) + { + if (response->data) + { + free(response->data); + } + free(response); + } + release_pool_handle(idx); + return T2ERROR_FAILURE; + } +#endif + //reset the security options set to curl handle + CURL_SETOPT_CHECK(easy, CURLOPT_SSLCERT, NULL); + CURL_SETOPT_CHECK(easy, CURLOPT_KEYPASSWD, NULL); + } + else + { + T2Info("Attempting curl communication without mtls\n"); + curl_code = curl_easy_perform(easy); + } + + if (curl_code != CURLE_OK) + { + T2Error("curl_easy_perform failed: %s\n", curl_easy_strerror(curl_code)); + if (response) + { + if (response->data) + { + free(response->data); + } + free(response); + } +#ifndef LIBRDKCERTSEL_BUILD + if(NULL != pCertFile) + { + free(pCertFile); + } + if(NULL != pCertPC) + { + free(pCertPC); + } +#endif + release_pool_handle(idx); + return T2ERROR_FAILURE; + } + + long http_code; + curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &http_code); + + T2ERROR result = T2ERROR_FAILURE; + if (http_code == 200) + { + result = T2ERROR_SUCCESS; + T2Info("%s:%d, T2:Telemetry XCONF communication success\n", __func__, __LINE__); + + if (response->data) + { + T2Debug("%s ; Response data size = %zu\n", __FUNCTION__, response->size); + + // Handle file output for GET requests + if (enable_file_output) + { + int fd = open(HTTP_RESPONSE_FILE, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd >= 0) + { + FILE *httpOutput = fdopen(fd, "w"); + if (httpOutput) + { + T2Debug("Update config data in response file %s \n", HTTP_RESPONSE_FILE); + fputs(response->data, httpOutput); + fclose(httpOutput); + } + else + { + T2Error("Unable to associate stream with file descriptor for %s\n", HTTP_RESPONSE_FILE); + close(fd); + } + } + else + { + T2Error("Unable to open %s file \n", HTTP_RESPONSE_FILE); + } + } + + // Copy response data to return to the caller function + if (response_data) + { + *response_data = NULL; + if(response->size <= SIZE_MAX - 1) + { + *response_data = (char*)malloc(response->size + 1); + if(*response_data) + { + memcpy(*response_data, response->data, response->size); + (*response_data)[response->size] = '\0'; + } + else + { + T2Error("Failed to allocate memory for response data of size %zu\n", response->size); + result = T2ERROR_FAILURE; + } + } + else + { + T2Error("Response size %zu exceeds maximum allowable size\n", response->size); + result = T2ERROR_FAILURE; + } + } + } + else + { + T2Error("%s ; No response data received\n", __FUNCTION__); + result = T2ERROR_FAILURE; + } + } + else + { + T2Error("%s:%d, T2:Telemetry XCONF communication Failed with http code : %ld Curl code : %d \n", __func__, __LINE__, http_code, curl_code); + if(http_code == 404) + { + result = T2ERROR_PROFILE_NOT_SET; + } + else + { + result = T2ERROR_FAILURE; + } + } + + // Clean up local response buffer + if (response) + { + if (response->data) + { + free(response->data); + } + free(response); + } + + // Clean up certificates - only for non-LIBRDKCERTSEL_BUILD path + // In case of CertSelector, CertSelector will take care of freeing the certicates +#ifndef LIBRDKCERTSEL_BUILD + if(pCertFile != NULL) + { + free(pCertFile); + pCertFile = NULL; + } + if(pCertPC != NULL) + { +#ifdef LIBRDKCONFIG_BUILD + size_t sKey = strlen(pCertPC); + if (rdkconfig_free((unsigned char**)&pCertPC, sKey) == RDKCONFIG_FAIL) + { + T2Error("Failed to free password using rdkconfig\n"); + } +#else + free(pCertPC); +#endif + pCertPC = NULL; + } +#endif + + // Important Note: When using LIBRDKCERTSEL_BUILD, pCertURI and pCertPC are owned by the + // cert selector object and are freed when rdkcertselector_free() is called + + release_pool_handle(idx); + + T2Debug("%s ++out\n", __FUNCTION__); + return result; +} + +T2ERROR http_pool_post(const char *url, const char *payload) +{ + T2Debug("%s ++in\n", __FUNCTION__); + if (!url || !payload) + { + T2Error("Invalid URL or payload parameter\n"); + return T2ERROR_FAILURE; + } + + T2Info("POST url = %s\n", url); + + // Acquire any available handle (with waiting) + CURL *easy; + int idx = -1; + + T2ERROR ret = acquire_pool_handle(&easy, &idx); + if(ret != T2ERROR_SUCCESS) + { + T2Error("Failed to acquire pool handle\n"); + return ret; + } + + // Clear any GET-specific settings from previous use + CURL_SETOPT_CHECK(easy, CURLOPT_WRITEFUNCTION, NULL); + CURL_SETOPT_CHECK(easy, CURLOPT_WRITEDATA, (void *)stdout); + + // Configure request-specific options for POST + CURL_SETOPT_CHECK_STR(easy, CURLOPT_URL, url); + CURL_SETOPT_CHECK(easy, CURLOPT_POST, 1L); + CURL_SETOPT_CHECK(easy, CURLOPT_HTTPHEADER, post_headers); + CURL_SETOPT_CHECK_STR(easy, CURLOPT_POSTFIELDS, payload); + CURL_SETOPT_CHECK(easy, CURLOPT_POSTFIELDSIZE, strlen(payload)); + +#if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) + configure_wan_interface(easy); +#endif + + // curl_easy_perform crashes without file output configuration. This can be removed once the root cause of the crash is identified and fixed. + // For now, we will set up file output for POST requests to ensure stability. + // Set up file output for POST requests + // If file opens successfully, override the default stdout with the file pointer + int curl_output_fd = open("/tmp/curlOutput.txt", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + FILE *fp = NULL; + if (curl_output_fd >= 0) + { + fp = fdopen(curl_output_fd, "wb"); + if (fp != NULL) + { + CURL_SETOPT_CHECK(easy, CURLOPT_WRITEFUNCTION, writeToFile); + CURL_SETOPT_CHECK(easy, CURLOPT_WRITEDATA, (void *)fp); + } + else + { + T2Error("fdopen failed for /tmp/curlOutput.txt, will use stdout as fallback\n"); + close(curl_output_fd); + } + } + else + { + T2Error("Unable to open /tmp/curlOutput.txt for writing, will use stdout as fallback\n"); + } + + // Certificate handling - check if mTLS is enabled + bool mtls_enable = isMtlsEnabled(); + char *pCertFile = NULL; + char *pCertPC = NULL; +#ifdef LIBRDKCERTSEL_BUILD + char *pCertURI = NULL; +#endif + CURLcode curl_code = CURLE_OK; + + if(mtls_enable == true) + { +#ifdef LIBRDKCERTSEL_BUILD + rdkcertselector_h thisCertSel = NULL; + rdkcertselectorStatus_t curlGetCertStatus; + bool state_red_enable = false; + +#if defined(ENABLE_RED_RECOVERY_SUPPORT) + state_red_enable = (access("/tmp/stateRedEnabled", F_OK) == 0); + T2Info("%s: state_red_enable: %d\n", __func__, state_red_enable); +#endif + + // When status of state red changes, switch cert selectors accordingly + if (state_red_enable) + { + // RED recovery mode - use recovery cert selector + if (pool_entries[idx].rcvry_cert_selector == NULL) + { + T2Info("Initializing recovery cert selector for this easy handle\n"); + pool_entries[idx].rcvry_cert_selector = rdkcertselector_new(NULL, NULL, "RCVRY"); + if (pool_entries[idx].rcvry_cert_selector == NULL) + { + T2Error("Failed to initialize recovery cert selector for this easy handle\n"); + release_pool_handle(idx); + if(fp) + { + fclose(fp); + } + return T2ERROR_FAILURE; + } + } + thisCertSel = pool_entries[idx].rcvry_cert_selector; + T2Info("%s: Using recovery cert selector for entry %d\n", __func__, idx); + + // Clean up normal cert selector if it exists + if (pool_entries[idx].cert_selector != NULL) + { + T2Info("%s: State transition detected, freeing normal cert selector for entry %d\n", __func__, idx); + rdkcertselector_free(&pool_entries[idx].cert_selector); + pool_entries[idx].cert_selector = NULL; + } + } + else + { + // Device not in red recovery - use regular cert selector + if (pool_entries[idx].cert_selector == NULL) + { + T2Info("%s: Initializing normal cert selector for entry %d\n", __func__, idx); + pool_entries[idx].cert_selector = rdkcertselector_new(NULL, NULL, "MTLS"); + if (pool_entries[idx].cert_selector == NULL) + { + T2Error("%s: Failed to initialize cert selector for entry %d\n", __func__, idx); + if(fp) + { + fclose(fp); + } + release_pool_handle(idx); + return T2ERROR_FAILURE; + } + } + thisCertSel = pool_entries[idx].cert_selector; + + // Clean up recovery cert selector if it exists (state transition) + if (pool_entries[idx].rcvry_cert_selector != NULL) + { + T2Info("%s: State transition detected, freeing recovery cert selector for entry %d\n", __func__, idx); + rdkcertselector_free(&pool_entries[idx].rcvry_cert_selector); + pool_entries[idx].rcvry_cert_selector = NULL; + } + } + + do + { + pCertURI = NULL; + pCertPC = NULL; + pCertFile = NULL; + + curlGetCertStatus = rdkcertselector_getCert(thisCertSel, &pCertURI, &pCertPC); + if(curlGetCertStatus != certselectorOk) + { + T2Error("%s, T2:Failed to retrieve the certificate for entry %d.\n", __func__, idx); + if(fp) + { + fclose(fp); + } + release_pool_handle(idx); + return T2ERROR_FAILURE; + } + else + { + // skip past file scheme in URI + pCertFile = pCertURI; + if ( strncmp( pCertFile, FILESCHEME, sizeof(FILESCHEME) - 1 ) == 0 ) + { + pCertFile += (sizeof(FILESCHEME) - 1); + } + + // Configure mTLS certificates + CURL_SETOPT_CHECK_STR(easy, CURLOPT_SSLCERTTYPE, "P12"); + CURL_SETOPT_CHECK_STR(easy, CURLOPT_SSLCERT, pCertFile); + CURL_SETOPT_CHECK_STR(easy, CURLOPT_KEYPASSWD, pCertPC); + CURL_SETOPT_CHECK(easy, CURLOPT_SSL_VERIFYPEER, 1L); + + curl_code = curl_easy_perform(easy); + + long http_code; + curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &http_code); + + if(curl_code != CURLE_OK || http_code != 200) + { + T2Error("%s: Failed to establish connection using xPKI certificate: %s, curl failed: %d (entry %d)\n", __func__, pCertFile, curl_code, idx); + } + else + { + T2Info("%s: Using xpki Certs connection certname : %s (entry %d)\n", __FUNCTION__, pCertFile, idx); + } + } + } + while(rdkcertselector_setCurlStatus(thisCertSel, curl_code, (const char*)url) == TRY_ANOTHER); +#else + // Fallback to getMtlsCerts if certificate selector not available + if(T2ERROR_SUCCESS == getMtlsCerts(&pCertFile, &pCertPC)) + { + // Configure mTLS certificates + CURL_SETOPT_CHECK_STR(easy, CURLOPT_SSLCERTTYPE, "P12"); + CURL_SETOPT_CHECK_STR(easy, CURLOPT_SSLCERT, pCertFile); + CURL_SETOPT_CHECK_STR(easy, CURLOPT_KEYPASSWD, pCertPC); + CURL_SETOPT_CHECK(easy, CURLOPT_SSL_VERIFYPEER, 1L); + + curl_code = curl_easy_perform(easy); + } + else + { + T2Error("mTLS_get failure\n"); + if(fp) + { + fclose(fp); + } + release_pool_handle(idx); + return T2ERROR_FAILURE; + } +#endif + CURL_SETOPT_CHECK(easy, CURLOPT_SSLCERT, NULL); + CURL_SETOPT_CHECK(easy, CURLOPT_KEYPASSWD, NULL); + } + else + { + T2Info("Attempting curl communication without mtls\n"); + curl_code = curl_easy_perform(easy); + } + // Close output file + if(fp) + { + fclose(fp); + } + T2ERROR result = T2ERROR_FAILURE; + if (curl_code == CURLE_OK) + { + long http_code; + curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &http_code); + T2Debug("HTTP response code: %ld\n", http_code); + + if (http_code == 200) + { + result = T2ERROR_SUCCESS; + T2Info("Report Sent Successfully over HTTP : %ld\n", http_code); + } + else + { + T2Error("HTTP request failed with code: %ld\n", http_code); + } + } + else + { + T2Error("curl_easy_perform failed: %s\n", curl_easy_strerror(curl_code)); + } + + // Clean up certificates - only for non-LIBRDKCERTSEL_BUILD path +#ifndef LIBRDKCERTSEL_BUILD + if(pCertFile != NULL) + { + free(pCertFile); + pCertFile = NULL; + } + if(pCertPC != NULL) + { +#ifdef LIBRDKCONFIG_BUILD + size_t sKey = strlen(pCertPC); + if (rdkconfig_free((unsigned char**)&pCertPC, sKey) == RDKCONFIG_FAIL) + { + T2Error("Failed to free password using rdkconfig\n"); + } +#else + free(pCertPC); +#endif + pCertPC = NULL; + } +#endif + // Note: When using LIBRDKCERTSEL_BUILD, pCertURI and pCertPC are owned by the + // cert selector object and are freed when rdkcertselector_free() is called + release_pool_handle(idx); + + T2Debug("%s ++out\n", __FUNCTION__); + return result; +} + +T2ERROR http_pool_cleanup(void) +{ + T2Debug("%s ++in\n", __FUNCTION__); + pthread_mutex_lock(&pool_mutex); + if (!pool_initialized) + { + T2Info("Pool not initialized, nothing to cleanup\n"); + pthread_mutex_unlock(&pool_mutex); + return T2ERROR_SUCCESS; + } + + T2Info("Cleaning up http pool resources\n"); + + // Signal all new acquire attempts to bail out immediately + pool_shutting_down = true; + pthread_mutex_unlock(&pool_mutex); + + // Poll until all in-flight curl_easy_perform() calls complete. + // Max wait = POOL_ACQUIRE_TIMEOUT_SEC (curl timeout 30s + 5s buffer). + // Polling avoids the deadlock where cleanup holds mutex while waiting, + // and release_pool_handle also needs the mutex to decrement active_requests. + struct timespec start_time, current_time; + if (clock_gettime(CLOCK_MONOTONIC, &start_time) != 0) + { + T2Error("clock_gettime failed for start_time: %s\n", strerror(errno)); + return T2ERROR_FAILURE; + } + + while(1) + { + pthread_mutex_lock(&pool_mutex); + unsigned int pending = active_requests; + pthread_mutex_unlock(&pool_mutex); + + if (pending == 0) + { + T2Info("All active requests completed, proceeding with cleanup\n"); + break; + } + + if (clock_gettime(CLOCK_MONOTONIC, ¤t_time) != 0) + { + T2Error("clock_gettime failed for current_time: %s\n", strerror(errno)); + return T2ERROR_FAILURE; + } + long elapsed_sec = current_time.tv_sec - start_time.tv_sec; + + if (elapsed_sec >= POOL_ACQUIRE_TIMEOUT_SEC) + { + T2Error("Cleanup timeout after %ld seconds with %u request(s) still active, forcing cleanup\n", + elapsed_sec, pending); + break; + } + + T2Info("Waiting for %u active request(s) to complete before pool cleanup\n", pending); + usleep(POOL_ACQUIRE_RETRY_MS * 1000); + } + + pthread_mutex_lock(&pool_mutex); + pool_initialized = false; + pool_shutting_down = false; + pthread_mutex_unlock(&pool_mutex); + + // Cleanup all curl handles and per-entry certificate selectors + cleanup_curl_handles(); + +#ifndef LIBRDKCERTSEL_BUILD + uninitMtls(); +#endif + + T2Debug("%s ++out\n", __FUNCTION__); + return T2ERROR_SUCCESS; +} + +// Write callback function for POST file output (from original sendReportOverHTTP) +size_t writeToFile(void *ptr, size_t size, size_t nmemb, void *stream) +{ + size_t written = fwrite(ptr, size, nmemb, (FILE *) stream); + return written; +} diff --git a/source/protocol/http/multicurlinterface.h b/source/protocol/http/multicurlinterface.h new file mode 100644 index 00000000..2a1e506c --- /dev/null +++ b/source/protocol/http/multicurlinterface.h @@ -0,0 +1,49 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2019 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#ifndef _MULTI_CURL_INTERFACE_H_ +#define _MULTI_CURL_INTERFACE_H_ + +#include +#include "telemetry2_0.h" + +#if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) +#define TR181_DEVICE_CURRENT_WAN_IFNAME "Device.X_RDK_WanManager.CurrentActiveInterface" +#endif + +/* + * Structure to store the xconf response, this is to manage large responses + * as curl parses large responses in chunks + */ +typedef struct _curlResponseData +{ + char *data; + size_t size; +} curlResponseData; + +size_t writeToFile(void *ptr, size_t size, size_t nmemb, void *stream); +T2ERROR init_connection_pool(); + +// New dedicated APIs for better separation of concerns +T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_output); +T2ERROR http_pool_post(const char *url, const char *payload); + +T2ERROR http_pool_cleanup(void); + +#endif /* _MULTI_CURL_INTERFACE_H_ */ diff --git a/source/telemetry2_0.c b/source/telemetry2_0.c index c9dce45b..9acd53ee 100644 --- a/source/telemetry2_0.c +++ b/source/telemetry2_0.c @@ -45,6 +45,7 @@ #include "scheduler.h" #include "t2eventreceiver.h" #include "t2common.h" +#include "multicurlinterface.h" #ifdef INCLUDE_BREAKPAD #ifndef ENABLE_RDKC_SUPPORT @@ -72,6 +73,10 @@ T2ERROR initTelemetry() T2Debug("%s ++in\n", __FUNCTION__); initWhoamiSupport(); + if (init_connection_pool() != 0) + { + T2Error("Failed to initialize HTTP connection pool\n"); + } if(T2ERROR_SUCCESS == initReportProfiles()) { #ifndef DEVICE_EXTENDER @@ -124,6 +129,7 @@ static void terminate() uninitXConfClient(); #endif ReportProfiles_uninit(); + http_pool_cleanup(); } } diff --git a/source/utils/t2MtlsUtils.c b/source/utils/t2MtlsUtils.c index b52933b5..a8a8e5b6 100644 --- a/source/utils/t2MtlsUtils.c +++ b/source/utils/t2MtlsUtils.c @@ -30,9 +30,11 @@ #include "t2log_wrapper.h" #include "t2common.h" +#ifndef LIBRDKCERTSEL_BUILD #ifdef LIBRDKCONFIG_BUILD #include "rdkconfig.h" #endif +#endif #if !defined(ENABLE_RDKC_SUPPORT) diff --git a/source/xconf-client/Makefile.am b/source/xconf-client/Makefile.am index 64c7f1e6..e08feed2 100644 --- a/source/xconf-client/Makefile.am +++ b/source/xconf-client/Makefile.am @@ -24,7 +24,7 @@ libxconfclient_la_SOURCES = xconfclient.c libxconfclient_la_LDFLAGS = -shared -fPIC -lcjson -lcurl if IS_LIBRDKCERTSEL_ENABLED libxconfclient_la_CFLAGS = $(LIBRDKCERTSEL_FLAG) -libxconfclient_la_LDFLAGS += -lRdkCertSelector +libxconfclient_la_LDFLAGS += -lRdkCertSelector -lrdkconfig endif libxconfclient_la_LIBADD = ${top_builddir}/source/t2parser/libt2parser.la ${top_builddir}/source/ccspinterface/libccspinterface.la libxconfclient_la_CPPFLAGS = -fPIC -I${PKG_CONFIG_SYSROOT_DIR}$(includedir)/dbus-1.0 \ @@ -37,6 +37,7 @@ libxconfclient_la_CPPFLAGS = -fPIC -I${PKG_CONFIG_SYSROOT_DIR}$(includedir)/dbus -I${top_srcdir}/source/ccspinterface \ -I${top_srcdir}/source/t2parser \ -I${top_srcdir}/source/dcautil \ - -I${top_srcdir}/source/utils + -I${top_srcdir}/source/utils \ + -I${top_srcdir}/source/protocol/http libxconfclient_la_DEPENDENCIES = ${top_builddir}/source/ccspinterface/libccspinterface.la ${top_builddir}/source/t2parser/libt2parser.la diff --git a/source/xconf-client/xconfclient.c b/source/xconf-client/xconfclient.c index 36577ad5..f0e70591 100644 --- a/source/xconf-client/xconfclient.c +++ b/source/xconf-client/xconfclient.c @@ -31,6 +31,7 @@ #include +#include "multicurlinterface.h" #include "t2log_wrapper.h" #include "reportprofiles.h" #include "profilexconf.h" @@ -49,13 +50,14 @@ #include "rdkcertselector.h" #define FILESCHEME "file://" #endif +#ifndef LIBRDKCERTSEL_BUILD #ifdef LIBRDKCONFIG_BUILD #include "rdkconfig.h" #endif +#endif #define RFC_RETRY_TIMEOUT 60 #define XCONF_RETRY_TIMEOUT 180 #define MAX_XCONF_RETRY_COUNT 5 -#define IFINTERFACE "erouter0" #define XCONF_CONFIG_FILE "DCMresponse.txt" #define PROCESS_CONFIG_COMPLETE_FLAG "/tmp/t2DcmComplete" #define HTTP_RESPONSE_FILE "/tmp/httpOutput.txt" @@ -64,12 +66,6 @@ extern sigset_t blocking_signal; -#if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) - -#if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) -static char waninterface[256]; -#endif -#endif static const int MAX_URL_LEN = 1024; static const int MAX_URL_ARG_LEN = 128; static int xConfRetryCount = 0; @@ -87,9 +83,6 @@ static pthread_mutex_t xcMutex; static pthread_mutex_t xcThreadMutex; static pthread_cond_t xcCond; static pthread_cond_t xcThreadCond; -#ifdef LIBRDKCERTSEL_BUILD -static rdkcertselector_h xcCertSelector = NULL; -#endif #if defined(ENABLE_REMOTE_PROFILE_DOWNLOAD) static int retryCount = 0; #endif @@ -529,81 +522,13 @@ T2ERROR appendRequestParams(CURLU *uri) return ret; } -static size_t httpGetCallBack(void *response, size_t len, size_t nmemb, - void *stream) -{ - - size_t realsize = len * nmemb; - curlResponseData* httpResponse = (curlResponseData*) stream; - - char *ptr = (char*) realloc(httpResponse->data, - httpResponse->size + realsize + 1); - if (!ptr) - { - T2Error("%s:%u , T2:memory realloc failed\n", __func__, __LINE__); - return 0; - } - httpResponse->data = ptr; - memcpy(&(httpResponse->data[httpResponse->size]), response, realsize); - httpResponse->size += realsize; - httpResponse->data[httpResponse->size] = 0; - - return realsize; -} - -#ifdef LIBRDKCERTSEL_BUILD -void xcCertSelectorFree() -{ - rdkcertselector_free(&xcCertSelector); - if(xcCertSelector == NULL) - { - T2Info("%s, T2:Cert selector memory free \n", __func__); - } - else - { - T2Info("%s, T2:Cert selector memory free failed \n", __func__); - } -} -static void xcCertSelectorInit() -{ - if(xcCertSelector == NULL) - { - xcCertSelector = rdkcertselector_new( NULL, NULL, "MTLS" ); - if(xcCertSelector == NULL) - { - T2Error("%s, T2:Cert selector initialization failed\n", __func__); - } - else - { - T2Info("%s, T2:Cert selector initialization successfully \n", __func__); - } - } -} -#endif T2ERROR doHttpGet(char* httpsUrl, char **data) { T2Debug("%s ++in\n", __FUNCTION__); T2Info("%s with url %s \n", __FUNCTION__, httpsUrl); - CURL *curl; - CURLcode code = CURLE_OK; - long http_code = 0; - CURLcode curl_code = CURLE_OK; -#ifdef LIBRDKCERTSEL_BUILD - rdkcertselectorStatus_t xcGetCertStatus; - char *pCertURI = NULL; -#endif - char *pCertFile = NULL; - char *pPasswd = NULL; -#ifdef LIBRDKCONFIG_BUILD - size_t sPasswdSize = 0; -#endif - // char *pKeyType = "PEM" ; - bool mtls_enable = false; - pid_t childPid; - int sharedPipeFdStatus[2]; - int sharedPipeFdDataLen[2]; + T2ERROR ret; if(NULL == httpsUrl) { @@ -611,373 +536,17 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) return T2ERROR_FAILURE; } - if(pipe(sharedPipeFdStatus) != 0) + ret = http_pool_get(httpsUrl, data, true); + if(ret == T2ERROR_SUCCESS) { - T2Error("Failed to create pipe for status !!! exiting...\n"); - T2Debug("%s --out\n", __FUNCTION__); - return T2ERROR_FAILURE; - } - - if(pipe(sharedPipeFdDataLen) != 0) - { - T2Error("Failed to create pipe for data length!!! exiting...\n"); - T2Debug("%s --out\n", __FUNCTION__); - return T2ERROR_FAILURE; - } -#if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) - -#if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) - char *paramVal = NULL; - memset(waninterface, 0, sizeof(waninterface)); - snprintf(waninterface, sizeof(waninterface), "%s", IFINTERFACE); - - if(T2ERROR_SUCCESS == getParameterValue(TR181_DEVICE_CURRENT_WAN_IFNAME, ¶mVal)) - { - if(strlen(paramVal) > 0) - { - memset(waninterface, 0, sizeof(waninterface)); - snprintf(waninterface, sizeof(waninterface), "%s", paramVal); - T2Info("TR181_DEVICE_CURRENT_WAN_IFNAME -- %s\n", waninterface); - } - free(paramVal); - paramVal = NULL; + T2Info("HTTP GET request completed successfully using connection pool\n"); } else { - T2Error("Failed to get Value for %s\n", TR181_DEVICE_CURRENT_WAN_IFNAME); + T2Error("Failed to perform HTTP GET request using connection pool\n"); } -#endif -#endif - mtls_enable = isMtlsEnabled(); - // block the userdefined signal handlers before fork - pthread_sigmask(SIG_BLOCK, &blocking_signal, NULL); - if((childPid = fork()) < 0) - { - T2Error("Failed to fork !!! exiting...\n"); - // Unblock the userdefined signal handlers - pthread_sigmask(SIG_UNBLOCK, &blocking_signal, NULL); - T2Debug("%s --out\n", __FUNCTION__); - return T2ERROR_FAILURE; - } - - /** - * Openssl has growing RSS which gets cleaned up only with OPENSSL_cleanup . - * This cleanup is not thread safe and classified as run once per application life cycle. - * Forking the libcurl calls so that it executes and terminates to release memory per execution. - */ - if(childPid == 0) - { - - T2ERROR ret = T2ERROR_FAILURE; - curlResponseData* httpResponse = (curlResponseData *) malloc(sizeof(curlResponseData)); - httpResponse->data = (char*)malloc(1); - httpResponse->data[0] = '\0'; //CID 282084 : Uninitialized scalar variable (UNINIT) - httpResponse->size = 0; - - curl = curl_easy_init(); - - if(curl) - { - - code = curl_easy_setopt(curl, CURLOPT_URL, httpsUrl); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - code = curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - code = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - code = curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, httpGetCallBack); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) httpResponse); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } -#if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) - -#if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) - code = curl_easy_setopt(curl, CURLOPT_INTERFACE, waninterface); - T2Info("TR181_DEVICE_CURRENT_WAN_IFNAME ---- %s\n", waninterface); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } -#else - code = curl_easy_setopt(curl, CURLOPT_INTERFACE, IFINTERFACE); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } -#endif - -#endif - if(mtls_enable == true) - { -#ifdef LIBRDKCERTSEL_BUILD - do - { - pCertFile = NULL; - pPasswd = NULL; - pCertURI = NULL; - xcGetCertStatus = rdkcertselector_getCert(xcCertSelector, &pCertURI, &pPasswd); - if(xcGetCertStatus != certselectorOk) - { - T2Error("%s, T2:Failed to retrieve the certificate.\n", __func__); - xcCertSelectorFree(); - free(httpResponse->data); - free(httpResponse); - curl_easy_cleanup(curl); - ret = T2ERROR_FAILURE; - goto status_return; - } - else - { - // skip past file scheme in URI - pCertFile = pCertURI; - if ( strncmp( pCertFile, FILESCHEME, sizeof(FILESCHEME) - 1 ) == 0 ) - { - pCertFile += (sizeof(FILESCHEME) - 1); - } -#else - if(T2ERROR_SUCCESS == getMtlsCerts(&pCertFile, &pPasswd)) - { -#endif - code = curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "P12"); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - code = curl_easy_setopt(curl, CURLOPT_SSLCERT, pCertFile); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - code = curl_easy_setopt(curl, CURLOPT_KEYPASSWD, pPasswd); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - /* disconnect if authentication fails */ - code = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); - if(code != CURLE_OK) - { - T2Error("%s : Curl set opts failed with error %s \n", __FUNCTION__, curl_easy_strerror(code)); - } - curl_code = curl_easy_perform(curl); -#ifdef LIBRDKCERTSEL_BUILD - if(curl_code != CURLE_OK) - { - T2Error("%s: Failed to establish connection using xPKI certificate: %s, Curl failed : %d\n", __func__, pCertFile, curl_code); - } - else - { - T2Info("%s: Using xpki Certs connection certname : %s \n", __FUNCTION__, pCertFile); - } - } - } - while(rdkcertselector_setCurlStatus(xcCertSelector, curl_code, (const char*)httpsUrl) == TRY_ANOTHER); -#else - } - else - { - free(httpResponse->data); - free(httpResponse); - curl_easy_cleanup(curl); //CID 189986:Resource leak - T2Error("mTLS_get failure\n"); - ret = T2ERROR_FAILURE; - goto status_return; - } -#endif - } - else - { - curl_code = curl_easy_perform(curl); - } - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - - if(http_code == 200 && curl_code == CURLE_OK) - { - T2Info("%s:%d, T2:Telemetry XCONF communication success\n", __func__, __LINE__); - size_t len = strlen(httpResponse->data); - - // Share data with parent - close(sharedPipeFdDataLen[0]); - write(sharedPipeFdDataLen[1], &len, sizeof(size_t)); - close(sharedPipeFdDataLen[1]); - - FILE *httpOutput = fopen(HTTP_RESPONSE_FILE, "w+"); - if(httpOutput) - { - T2Debug("Update config data in response file %s \n", HTTP_RESPONSE_FILE); - fputs(httpResponse->data, httpOutput); - fclose(httpOutput); - } - else - { - T2Error("Unable to open %s file \n", HTTP_RESPONSE_FILE); - } - - free(httpResponse->data); - free(httpResponse); -#ifndef LIBRDKCERTSEL_BUILD - if(NULL != pCertFile) - { - free(pCertFile); - } - - if(NULL != pPasswd) - { -#ifdef LIBRDKCONFIG_BUILD - sPasswdSize = strlen(pPasswd); - if (rdkconfig_free((unsigned char**)&pPasswd, sPasswdSize) == RDKCONFIG_FAIL) - { - return T2ERROR_FAILURE; - } -#else - free(pPasswd); -#endif - } -#endif - curl_easy_cleanup(curl); - } - else - { - T2Error("%s:%d, T2:Telemetry XCONF communication Failed with http code : %ld Curl code : %d \n", __func__, __LINE__, http_code, - curl_code); - T2Error("%s : curl_easy_perform failed with error message %s from curl \n", __FUNCTION__, curl_easy_strerror(curl_code)); - free(httpResponse->data); - free(httpResponse); -#ifndef LIBRDKCERTSEL_BUILD - if(NULL != pCertFile) - { - free(pCertFile); - } - if(NULL != pPasswd) - { -#ifdef LIBRDKCONFIG_BUILD - sPasswdSize = strlen(pPasswd); - if (rdkconfig_free((unsigned char**)&pPasswd, sPasswdSize) == RDKCONFIG_FAIL) - { - return T2ERROR_FAILURE; - } -#else - free(pPasswd); -#endif - } -#endif - curl_easy_cleanup(curl); - if(http_code == 404) - { - ret = T2ERROR_PROFILE_NOT_SET; - } - else - { - ret = T2ERROR_FAILURE; - } - goto status_return ; - } - } - else - { - free(httpResponse->data); - free(httpResponse); - ret = T2ERROR_FAILURE; - goto status_return ; - } - - ret = T2ERROR_SUCCESS ; -status_return : - - close(sharedPipeFdStatus[0]); - write(sharedPipeFdStatus[1], &ret, sizeof(T2ERROR)); - close(sharedPipeFdStatus[1]); - exit(0); - - } - else // Parent - { - T2ERROR ret = T2ERROR_FAILURE; - // Use waitpid insted of wait - waitpid(childPid, NULL, 0); - // Unblock the userdefined signal handlers after wait - pthread_sigmask(SIG_UNBLOCK, &blocking_signal, NULL); - // Get the return status via IPC from child process - close(sharedPipeFdStatus[1]); - ssize_t readBytes = read(sharedPipeFdStatus[0], &ret, sizeof(T2ERROR)); - if(readBytes == -1) - { - T2Error("Failed to read from pipe\n"); - return T2ERROR_FAILURE; - } - close(sharedPipeFdStatus[0]); - - // Get the datas via IPC from child process - if(ret == T2ERROR_SUCCESS) - { - size_t len = 0; - close(sharedPipeFdDataLen[1]); - readBytes = read(sharedPipeFdDataLen[0], &len, sizeof(size_t)); - if(readBytes == -1) - { - T2Error("Failed to read from pipe\n"); - return T2ERROR_FAILURE; - } - close(sharedPipeFdDataLen[0]); - *data = NULL; - if(len <= SIZE_MAX) - { - *data = (char*)malloc(len + 1); - } - if(*data == NULL) - { - T2Error("Unable to allocate memory for XCONF config data \n"); - ret = T2ERROR_FAILURE; - } - else - { - if(len <= SIZE_MAX) - { - memset(*data, '\0', len + 1); - } - FILE *httpOutput = fopen(HTTP_RESPONSE_FILE, "r+"); - if(httpOutput) - { - // Read the whole file content - if(len <= SIZE_MAX) - { - readBytes = fread(*data, len, 1, httpOutput); - } - if(readBytes == -1) - { - T2Error("Failed to read from pipe\n"); - return T2ERROR_FAILURE; - } - T2Debug("Configuration obtained from http server : \n %s \n", *data); - fclose(httpOutput); - } - } - } - T2Debug("%s --out\n", __FUNCTION__); - return ret; - - } - + T2Debug("%s --out\n", __FUNCTION__); + return ret; } #if defined(ENABLE_REMOTE_PROFILE_DOWNLOAD) @@ -1509,9 +1078,6 @@ void uninitXConfClient() #ifdef DCMAGENT bexitDCMThread = false; pthread_join(dcmThread, NULL); -#endif -#ifdef LIBRDKCERTSEL_BUILD - xcCertSelectorFree(); #endif } T2Debug("%s --out\n", __FUNCTION__); @@ -1577,9 +1143,6 @@ T2ERROR initXConfClient() pthread_cond_init(&xcCond, NULL); pthread_cond_init(&xcThreadCond, NULL); isXconfInit = true ; -#ifdef LIBRDKCERTSEL_BUILD - xcCertSelectorInit(); -#endif pthread_create(&xcrThread, NULL, getUpdatedConfigurationThread, NULL); //startXConfClient(); // Removing startXConfClient as getUpdatedConfigurationThread is created in this function itself T2Debug("%s --out\n", __FUNCTION__); diff --git a/source/xconf-client/xconfclient.h b/source/xconf-client/xconfclient.h index 962c6508..9cbeebd3 100644 --- a/source/xconf-client/xconfclient.h +++ b/source/xconf-client/xconfclient.h @@ -108,16 +108,6 @@ typedef struct _rdk_utils_params char iface_v4_ip[DEVICE_IP_MAX_LENGTH]; } rdkParams_struct; -/* - * Structure to store the xconf response, this is to manage large responses - * as curl parses large responses in chunks - */ -typedef struct _curlResponseData -{ - char *data; - size_t size; -} curlResponseData; - T2ERROR getBuildType(char* buildType); T2ERROR appendRequestParams(CURLU *buf);