On Sat, Nov 29, 2014 at 2:07 PM, Dan Fandrich <[email protected]> wrote: Well, if you only do some of those thing, then you'll have to expect that your program will only fail "some" of the time. In particular, I don't see any code to set OpenSSL locking callbacks, nor do I see a call to curl_global_init(). Perhaps there's more. Please read those entire sections again.
>>> I have made the call curl_global_init() in my application but forgot to put it in the sample code which i attached. I came across the http://curl.haxx.se/libcurl/c/threaded-ssl.html sample multi-threaded application which uses the SSL locking. I have modified this application to suit my needs. Please find the attached one (modified application) I am creating two threads one thread will check the connection (will call waitForHostConnection function) and other thread will post the data (will call postDataToUrl). Please see results of this application in different scenarios: Starting of the application : Both the functions return(waitForHostConnection and postDataToUrl) successful values Pull the Ethernet cable: postDataToUrl returns 7 on curl_easy_perform and waitForHostConnection returns 6 on curl_easy_perform Connect the Ethernet cable back: postDataToUrl function is successful and waitForHostConnection continues to give 6 on curl_easy_perform I thought some problem with my waitForHostConnection function, i have modified my application so that both threads call postDataToUrl function. Please see the results of this modified application in different scenarios: Starting of the application : Both the threads(both are calling postDataToUrl) return successful values Pull the Ethernet cable: Both the threads returns 6 on curl_easy_perform Connect the Ethernet cable back: One thread will always be successful, other thread will be successful sometimes (returns 6 for failure cases) > The problem arises when i pull the ethernet cable and connect it back. I have set CURLOPT_NOSIGNAL to 1 for both handlers and set the SSL locking callback. Am I missing something here?? In my application both threads running simultaneously and posting data parallely are must. I made sure that both threads are having different curl handlers. Could you please let me know where I am going wrong. Your help is greatly appreciated. Thank you for for your help and time.
/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2011, Daniel Stenberg, <[email protected]>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ***************************************************************************/ /* A multi-threaded example that uses pthreads and fetches 4 remote files at * once over HTTPS. The lock callbacks and stuff assume OpenSSL or GnuTLS * (libgcrypt) so far. * * OpenSSL docs for this: * http://www.openssl.org/docs/crypto/threads.html * gcrypt docs for this: * http://gnupg.org/documentation/manuals/gcrypt/Multi_002dThreading.html */ #define USE_OPENSSL #include <stdio.h> #include <string.h> #include <curl/curl.h> #include <curl/easy.h> #include <openssl/crypto.h> #include <openssl/x509.h> #include <openssl/pem.h> #include <openssl/ssl.h> #include <openssl/err.h> #include <syslog.h> #include <pthread.h> #define NUMT 2 #define TRUE 1 #define FALSE 0 # define SUCCESS 0 # define FAILURE -1 typedef enum { PRIMARY_URL = 0, SECONDARY_URL } HOST_URL_ENUM; typedef struct { int iTotSize; int iWritten; char * szMsg; } RESP_DATA_STYPE, * RESP_DATA_PTYPE; #define CA_CERT_FILE "ca-bundle.crt" /* we have this global to let the callback get easy access to it */ static pthread_mutex_t *lockarray; static struct curl_slist * headers = NULL; static int initSSICURLHandle(CURL **, char*); static void commonInit(CURL *, int); static int sendDataToHost(CURL *, char *, int, char **, int *); static size_t saveResponse(void *, size_t, size_t, void *); static int initializeRespData(RESP_DATA_PTYPE); static void waitForHostConnection(void); static int chkConn(CURL *); #ifdef USE_OPENSSL #include <openssl/crypto.h> static void lock_callback(int mode, int type, char *file, int line) { (void)file; (void)line; if (mode & CRYPTO_LOCK) { pthread_mutex_lock(&(lockarray[type])); } else { pthread_mutex_unlock(&(lockarray[type])); } } static unsigned long thread_id(void) { unsigned long ret; ret=(unsigned long)pthread_self(); return(ret); } static void init_locks(void) { int i; lockarray=(pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t)); for (i=0; i<CRYPTO_num_locks(); i++) { pthread_mutex_init(&(lockarray[i]),NULL); } CRYPTO_set_id_callback((unsigned long (*)())thread_id); CRYPTO_set_locking_callback((void (*)())lock_callback); } static void kill_locks(void) { int i; CRYPTO_set_locking_callback(NULL); for (i=0; i<CRYPTO_num_locks(); i++) pthread_mutex_destroy(&(lockarray[i])); OPENSSL_free(lockarray); } #endif #ifdef USE_GNUTLS #include <gcrypt.h> #include <errno.h> GCRY_THREAD_OPTION_PTHREAD_IMPL; void init_locks(void) { gcry_control(GCRYCTL_SET_THREAD_CBS); } #define kill_locks() #endif /* * ============================================================================ * Function Name: commonInit * * Description : This function contains the code for common initialization of * all the three handles needed for communication with the PWC * * Input Params : CURL * handle -> curl handle that needs to be configured * * Output Params: void * ============================================================================ */ static void commonInit(CURL * handle, int certValdReq) { printf("%d: %s: --- enter ---\n", pthread_self(), pthread_self(), __FUNCTION__); /* Set the NO SIGNAL option, This option is very important to set in case * of multi-threaded applications like ours, because otherwise the libcurl * uses signal handling for the communication and that causes the threads * to go crazy and even crash. Fix done for issue 1659 (Church of LDS AmDocs * Case# 130826-3983) */ curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1L); /* Set the HTTP post option */ curl_easy_setopt(handle, CURLOPT_POST, 1L); /* Add the headers */ curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); /*#ifdef DEBUG /* Add the debug function curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, curlDbgFunc); #endif*/ if (certValdReq == TRUE) { printf("%d: %s: Url starts with https. Doing certificate validation\n", pthread_self(), __FUNCTION__); curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 2L); } /* Set the CA certificate */ curl_easy_setopt(handle, CURLOPT_CAINFO, CA_CERT_FILE); /* Set the write function */ curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, saveResponse); /* Set the detection as TRUE for HTTP errors */ curl_easy_setopt(handle, CURLOPT_FAILONERROR, TRUE); printf("%d: %s: --- returning ---\n", pthread_self(), pthread_self(), __FUNCTION__); return; } /* * ============================================================================ * Function Name: initSSICURLHandle * * Description : * * Input Params : * * Output Params: SUCCESS / FAILURE * ============================================================================ */ static int initSSICURLHandle(CURL ** handle, char* pszHostUrl) { int rv = SUCCESS; char szTmpURL[100] = ""; CURL * locHandle = NULL; int certValdReq = TRUE; printf("%d: %s: --- enter ---\n", pthread_self(), __FUNCTION__); while(1) { /* Initialize the handle using the CURL library */ locHandle = curl_easy_init(); if(locHandle == NULL) { printf("%d: %s: Curl initialization for handle failed\n", pthread_self(), __FUNCTION__); rv = FAILURE; break; } curl_easy_setopt(locHandle, CURLOPT_URL, pszHostUrl); printf("%d: %s: Setting PWC URL = [%s]\n", pthread_self(), __FUNCTION__, pszHostUrl); /* Set the connection timeout */ curl_easy_setopt(locHandle, CURLOPT_CONNECTTIMEOUT, 10); /* Set the total timeout */ curl_easy_setopt(locHandle, CURLOPT_TIMEOUT, 20); /* Set the other common features */ commonInit(locHandle, certValdReq); *handle = locHandle; break; } printf("%d: %s: Returning [%d]\n", pthread_self(), __FUNCTION__, rv); return rv; } static void postDataToUrl() { int rv; char* szHostUrl = "URL1"; CURL * curlHandle = NULL; char req[2560] = "Message"; int reqSize = strlen(req); char* resp = NULL; int respSize = 0; rv = initSSICURLHandle(&curlHandle, szHostUrl); rv = sendDataToHost(curlHandle, req, reqSize, &resp, &respSize); if(rv != SUCCESS) { printf("%d: %s: Failed to post data to server\n", pthread_self(), __FUNCTION__); } if (resp != NULL) { printf("%d: %s Response Received: %s \n", pthread_self(), __FUNCTION__, resp); free(resp); resp = NULL; } curl_easy_cleanup(curlHandle); } static void *pull_one_url(void *i) { int iOption = *(int*)i; /* CURL *curl; curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, url); this example doesn't verify the server's certificate, which means we might be downloading stuff from an impostor curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_perform(curl); ignores error curl_easy_cleanup(curl);*/ while(1) { switch(iOption) { case 0: postDataToUrl(); break; case 1: waitForHostConnection(); break; } sleep(3); } return NULL; } /* * ============================================================================ * Function Name: waitForHostConnection * * Description : * * Input Params : * * Output Params: * ============================================================================ */ static void waitForHostConnection() { int iRetVal = SUCCESS; while(1) { if(isHostConnected() == TRUE) { printf( "%d: %s: Host connection is available\n",pthread_self(), __FUNCTION__); } else { printf( "%d: %s: Host connection is not available, checking once more...\n",pthread_self(), __FUNCTION__); } sleep(3); } } /* * ============================================================================ * Function Name: isHostConnected * * Description : * * Input Params : none * * Output Params: TRUE / FALSE * ============================================================================ */ int isHostConnected() { int bRv = TRUE; printf( "%d: %s: --- enter ---\n", pthread_self(), __FUNCTION__); if(SUCCESS == checkHostConn()) { bRv = TRUE; } else { bRv = FALSE; } printf( "%d: %s: Returning [%s]\n", pthread_self(), __FUNCTION__, (bRv == TRUE)? "TRUE" : "FALSE"); return bRv; } /* * ============================================================================ * Function Name: checkHostConn * * Description : * * Input Params : * * Output Params: SUCCESS / FAILURE * ============================================================================ */ int checkHostConn() { int rv = SUCCESS; int iCnt = 0; static int host = PRIMARY_URL; CURL * curlHandle = NULL; char* szHostUrl = "url1"; #ifdef DEBUG char szDbgMsg[512] = ""; #endif printf( "%d: %s: --- enter ---\n", pthread_self(), __FUNCTION__); while(iCnt < 2) { rv = initSSICURLHandle(&curlHandle, szHostUrl); if(rv == SUCCESS) { /* Curl handler initialized successfully * Checking connection */ rv = chkConn(curlHandle); if(rv != SUCCESS) { printf( "%d: %s: FAILED to check Host connection\n", pthread_self(), __FUNCTION__); } else { printf( "%d: %s: Host connection available\n", pthread_self(), __FUNCTION__); } } if(rv == SUCCESS) { printf( "%d: %s: Breaking from the loop\n", pthread_self(), __FUNCTION__); break; } /* If the first host is not working then try the next host */ host ^= 1; iCnt++; printf( "%d: %s: Trying again..\n", pthread_self(), __FUNCTION__); } printf( "%d: %s: Returning [%d]\n", pthread_self(), __FUNCTION__, rv); return rv; } /* * ============================================================================ * Function Name: chkConn * * Description : * * Input Params : * * * Output Params: SUCCESS / FAILURE * ============================================================================ */ static int chkConn(CURL * curlHandle) { int rv = SUCCESS; long lCode = 0L; #ifdef DEBUG char szDbgMsg[256] = ""; #endif printf( "%d: %s: --- enter ---\n", pthread_self(), __FUNCTION__); while(1) { curl_easy_setopt(curlHandle, CURLOPT_POST, 0L); curl_easy_setopt(curlHandle, CURLOPT_NOBODY, 1); curl_easy_setopt(curlHandle, CURLOPT_FOLLOWLOCATION, 1); rv = curl_easy_perform(curlHandle); if(rv == CURLE_OK) { printf( "%d: %s: Perform SUCCESS\n", pthread_self(), __FUNCTION__); } else { printf( "%d: %s: Return Value from PERFORM = [%d]\n", pthread_self(), __FUNCTION__, rv); } rv = curl_easy_getinfo(curlHandle, CURLINFO_RESPONSE_CODE, &lCode); printf( "%d: Return Value RESPONSE CODE = [%d], [%ld]\n", pthread_self(), __FUNCTION__, rv, lCode); if(rv == 0 && lCode == 200) { printf( "%d: Got Successful respcode from host\n", pthread_self(), __FUNCTION__); rv = SUCCESS; } else { printf( "%d: Didnt get the good resp code from host\n" , pthread_self(), __FUNCTION__); rv = FAILURE; } break; } if(curlHandle != NULL) { curl_easy_cleanup(curlHandle); } printf( "%d: %s: Returning [%d]\n", pthread_self(), __FUNCTION__, rv); return rv; } int main(int argc, char **argv) { pthread_t tid[NUMT]; int i; int error; (void)argc; /* we don't use any arguments in this example */ (void)argv; /* Add data to the headers */ headers = curl_slist_append(headers, "Content-Type:text/xml"); /* Must initialize libcurl before any threads are started */ curl_global_init(CURL_GLOBAL_ALL); init_locks(); for(i=0; i< NUMT; i++) { error = pthread_create(&tid[i], NULL, /* default attributes please */ pull_one_url, (void *)(&i)); if(0 != error) fprintf(stderr, "Couldn't run thread number %d, errno %d\n", i, error); else fprintf(stderr, "Thread %d, gets %d\n", i, tid[i]); } /* now wait for all threads to terminate */ for(i=0; i< NUMT; i++) { error = pthread_join(tid[i], NULL); fprintf(stderr, "Thread %d terminated\n", i); } kill_locks(); return 0; } /* * ============================================================================ * Function Name: sendDataToHost * * Description : This function sends the XML data to the PWC server, using the * appropriate CURL handle. * * Input Params : * PWC_URL_TYPE_ENUM urlType -> constant telling which handle to * use * unsigned char * data -> the XML data that needs to be sent * int size -> size of the data * * Output Params: SUCCESS / FAILURE * ============================================================================ */ static int sendDataToHost(CURL * curlHandle, char * req, int reqSize, char ** resp, int * respSize) { int rv = SUCCESS; char szCurlErrBuf[4096] = ""; RESP_DATA_STYPE stRespData; printf("%d: %s: --- enter ---\n", pthread_self(), __FUNCTION__); while(1) { /* Initialize the response data */ rv = initializeRespData(&stRespData); if(rv != SUCCESS) { printf("%d: %s: Initialization of response data FAILED\n" , pthread_self(), __FUNCTION__); break; } /* Set some curl library options before sending the data */ curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, &stRespData); curl_easy_setopt(curlHandle, CURLOPT_POSTFIELDS, (void *) req); curl_easy_setopt(curlHandle, CURLOPT_POSTFIELDSIZE, reqSize); curl_easy_setopt(curlHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf); printf("%d: %s: Posting the data to the server\n", pthread_self(), __FUNCTION__); /* post the data to the server */ rv = curl_easy_perform(curlHandle); printf("%d: %s: curl_easy_perform done, rv = %d\n", pthread_self(), __FUNCTION__, rv); if(rv == CURLE_OK) { *resp = stRespData.szMsg; *respSize = stRespData.iWritten; printf("%d: Data Posted Succesfully\n"); printf("%d: %s: Response Len = [%d]\n", pthread_self(), __FUNCTION__, stRespData.iWritten); } else { switch(rv) { case CURLE_UNSUPPORTED_PROTOCOL: /* 1 */ /* * The URL you passed to libcurl used a protocol that this libcurl does not support. * The support might be a compile-time option that you didn't use, * it can be a misspelled protocol string or just a protocol libcurl has no code for. */ printf("%d: %s: protocol not supported by library\n", pthread_self(), __FUNCTION__); break; case CURLE_URL_MALFORMAT: /* 3 */ /* * The URL was not properly formatted. */ printf("%d: %s: URL not properly formatted\n", pthread_self(), __FUNCTION__); break; case CURLE_COULDNT_RESOLVE_HOST: /* 6 */ /* * Couldn't resolve host. The given remote host was not resolved. */ printf("%d: %s: Couldnt resolve server's address\n", pthread_self(), __FUNCTION__); break; case CURLE_COULDNT_CONNECT: /* 7 */ /* * Failed to connect() to host or proxy. */ printf("%d: %s: Failed to connect server\n", pthread_self(), __FUNCTION__); break; case CURLE_HTTP_RETURNED_ERROR: /* 22 */ /* * This is returned if CURLOPT_FAILONERROR is set TRUE * and the HTTP server returns an error code that is >= 400. */ printf("%d: %s: Got HTTP error while posting\n", pthread_self(), __FUNCTION__); break; case CURLE_WRITE_ERROR: /* 23 */ /* * An error occurred when writing received data to a local file, * or an error was returned to libcurl from a write callback. */ printf("%d: %s: Failed to save the response data\n", pthread_self(), __FUNCTION__); rv = FAILURE; break; case CURLE_OUT_OF_MEMORY: /* 27 */ /* * A memory allocation request failed. * This is serious badness and things are severely screwed up if this ever occurs. */ printf("%d: %s: Facing memory shortage \n", pthread_self(), __FUNCTION__); rv = FAILURE; break; case CURLE_OPERATION_TIMEDOUT: /* 28 */ /* * Operation timeout. * The specified time-out period was reached according to the conditions. */ printf("%d: %s: Timeout happened while receiving\n", pthread_self(), __FUNCTION__); break; case CURLE_SSL_CONNECT_ERROR: /* 35 */ /* * A problem occurred somewhere in the SSL/TLS handshake. * */ printf("%d: %s: SSL/TLS Handshake FAILED\n", pthread_self(), __FUNCTION__); break; case CURLE_PEER_FAILED_VERIFICATION: /* 51 */ /* * The remote server's SSL certificate or SSH md5 fingerprint was deemed not OK. */ printf("%d: %s: Server's SSL certificate verification FAILED\n", pthread_self(), __FUNCTION__); break; case CURLE_SEND_ERROR: /* 55 */ /* * Failed sending network data. */ printf("%d: %s: Failed to post the request data\n", pthread_self(), __FUNCTION__); break; case CURLE_RECV_ERROR: /* 56 */ /* * Failure with receiving network data. */ printf("%d: %s: Failed to receive the response\n", pthread_self(), __FUNCTION__); break; case CURLE_SSL_CACERT: /* 60 */ /* * Peer certificate cannot be authenticated with known CA certificates. */ printf("%d: %s: Couldnt authenticate peer certificate\n", pthread_self(), __FUNCTION__); break; case CURLE_SSL_CACERT_BADFILE: /* 77 */ /* * Problem with reading the SSL CA cert (path? access rights?) */ printf("%d: %s: Cant find SSL CA sertificate [%s]\n", pthread_self(), __FUNCTION__, CA_CERT_FILE); break; default: printf("%d: %s: Error [%d] occured while posting\n", pthread_self(), __FUNCTION__, rv); break; } if(stRespData.szMsg != NULL) { free(stRespData.szMsg); stRespData.szMsg = NULL; } printf("%d: %s: Lib Curl Error [%s]\n",pthread_self(), __FUNCTION__, szCurlErrBuf); } break; } printf("%d: %s: Returning [%d]\n", pthread_self(), __FUNCTION__, rv); return rv; } /* * ============================================================================ * Function Name: initializeRespData * * Description : This function is used for the initial setting of the response * data structure which would then be used for storing XML * response retrieved from the server being contacted. * * Input Params : RESP_DATA_PTYPE stRespPtr -> pointer to response structure * * Output Params: SUCCESS / FAILURE * ============================================================================ */ static int initializeRespData(RESP_DATA_PTYPE stRespPtr) { int rv = SUCCESS; char * cTmpPtr = NULL; printf("%d: %s: --- enter ---\n", pthread_self(), __FUNCTION__); cTmpPtr = (char *) malloc(4096 * sizeof(char)); if(cTmpPtr != NULL) { /* Initialize the memory */ memset(cTmpPtr, 0x00, 4096 * sizeof(char)); memset(stRespPtr, 0x00, sizeof(RESP_DATA_STYPE)); /* Assign the data */ stRespPtr->iTotSize = 4096; stRespPtr->iWritten = 0; stRespPtr->szMsg = cTmpPtr; } else { printf("%d: %s: Memory allocation FAILED\n", pthread_self(), __FUNCTION__); rv = FAILURE; } printf("%d: %s: Returning [%d]\n", pthread_self(), __FUNCTION__, rv); return rv; } /* * ============================================================================ * Function Name: saveResponse * * Description : This function performs the job of saving the XML response * coming from the server into a buffer provided as parameter. * This function is passed to the CURL library, and the * library would call this function, not us. * * Input Params : * void * ptr -> pointer to the data that needs to be copied * size_t size-> size of one element of data * size_t cnt -> count of data members * void * des -> our buffer where the response is saved * * Output Params: totLen -> length of data saved * ============================================================================ */ static size_t saveResponse(void * ptr, size_t size, size_t cnt, void * des) { int rv = SUCCESS; int iSizeLeft = 0; int totLen = 0; int iReqdLen = 0; char * cTmpPtr = NULL; RESP_DATA_PTYPE respPtr = NULL; printf("%d: %s: --- enter ---\n", pthread_self(), __FUNCTION__); while(1) { respPtr = (RESP_DATA_PTYPE) des; totLen = size * cnt; iSizeLeft = respPtr->iTotSize - respPtr->iWritten; if(totLen > iSizeLeft) { iReqdLen = (respPtr->iTotSize + totLen - iSizeLeft) + 1/* one extra buffer for NULL at the end for safety purpose*/; cTmpPtr = (char *) realloc(respPtr->szMsg, iReqdLen); if(cTmpPtr == NULL) { printf("%d: %s: Reallocation of memory FAILED\n", pthread_self(), __FUNCTION__); rv = FAILURE; break; } memset(cTmpPtr + respPtr->iWritten, 0x00, iReqdLen - respPtr->iWritten); respPtr->iTotSize = iReqdLen; respPtr->szMsg = cTmpPtr; } memcpy(respPtr->szMsg + respPtr->iWritten, ptr, totLen); respPtr->iWritten += totLen; rv = totLen; break; } printf("%d: %s: Returning [%d]\n", pthread_self(), __FUNCTION__, rv); return rv; }
------------------------------------------------------------------- List admin: http://cool.haxx.se/list/listinfo/curl-library Etiquette: http://curl.haxx.se/mail/etiquette.html
