/******************************************************************************* * Copyright (C) 2004-2006 Intel Corp. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * - Neither the name of Intel Corp. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Intel Corp. OR THE CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ /** * @author Vadim Revyakin */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #ifdef ENABLE_EVENTING_SUPPORT #include #include #endif #include "u/libu.h" #include "wsman-types.h" #include "wsman-client.h" #include "wsman-soap.h" #include "wsman-xml.h" #include "wsman-debug.h" #include "wsman-client-transport.h" #define DEFAULT_TRANSFER_LEN 32000 #ifndef CURLOPT_CRLFILE #define CURLOPT_CRLFILE 10169 #endif #ifndef CURLE_SSL_CRL_BADFILE #define CURLE_SSL_CRL_BADFILE 82 #endif extern wsman_auth_request_func_t request_func; void wsmc_handler( WsManClient *cl, WsXmlDocH rqstDoc, void* user_data); static pthread_mutex_t curl_mutex = PTHREAD_MUTEX_INITIALIZER; static long reauthenticate(WsManClient *cl, long auth_set, long auth_avail, char **username, char **password) { long choosen_auth = 0; wsman_auth_type_t ws_auth = WS_NO_AUTH; if (auth_avail & CURLAUTH_GSSNEGOTIATE && wsman_is_auth_method(cl, WS_GSSNEGOTIATE_AUTH)) { choosen_auth = CURLAUTH_GSSNEGOTIATE; ws_auth = WS_GSSNEGOTIATE_AUTH; goto REQUEST_PASSWORD; } if (auth_avail & CURLAUTH_DIGEST && wsman_is_auth_method(cl, WS_DIGEST_AUTH)) { choosen_auth = CURLAUTH_DIGEST; ws_auth = WS_DIGEST_AUTH; goto REQUEST_PASSWORD; } if (auth_avail & CURLAUTH_NTLM && wsman_is_auth_method(cl, WS_NTLM_AUTH)) { choosen_auth = CURLAUTH_NTLM; ws_auth = WS_NTLM_AUTH; goto REQUEST_PASSWORD; } if (auth_avail & CURLAUTH_BASIC && wsman_is_auth_method(cl, WS_BASIC_AUTH)) { ws_auth = WS_BASIC_AUTH; choosen_auth = CURLAUTH_BASIC; goto REQUEST_PASSWORD; } debug("Client does not support authentication type 0x%04x" " acceptable by server\n", auth_avail); return 0; REQUEST_PASSWORD: message("%s authentication is used", wsmc_transport_get_auth_name(ws_auth)); if (auth_set == 0 && *username && *password) { // use existing username and password return choosen_auth; } if (cl->authentication.auth_request_func) { debug("Invoking Auth request callback"); /* free the username and password as these are OUT params to the auth_request_func * which the caller is expected to set */ if (*username) { u_free(*username); *username = NULL; } if (*password) { u_free(*password); *password = NULL; } cl->authentication.auth_request_func(cl, ws_auth, username, password); } else return 0; if (!(*username) || strlen(*username) == 0) { debug("No username. Authorization canceled"); return 0; } return choosen_auth; } static WS_LASTERR_Code convert_to_last_error(CURLcode r) { switch (r) { case CURLE_OK: return WS_LASTERR_OK; case CURLE_FAILED_INIT: return WS_LASTERR_FAILED_INIT; case CURLE_UNSUPPORTED_PROTOCOL: return WS_LASTERR_UNSUPPORTED_PROTOCOL; case CURLE_URL_MALFORMAT: return WS_LASTERR_URL_MALFORMAT; case CURLE_COULDNT_RESOLVE_PROXY: return WS_LASTERR_COULDNT_RESOLVE_PROXY; case CURLE_COULDNT_RESOLVE_HOST: return WS_LASTERR_COULDNT_RESOLVE_HOST; case CURLE_COULDNT_CONNECT: return WS_LASTERR_COULDNT_CONNECT; case CURLE_HTTP_RETURNED_ERROR: return WS_LASTERR_HTTP_RETURNED_ERROR; case CURLE_WRITE_ERROR: return WS_LASTERR_WRITE_ERROR; case CURLE_READ_ERROR: return WS_LASTERR_READ_ERROR; case CURLE_OUT_OF_MEMORY: return WS_LASTERR_OUT_OF_MEMORY; case CURLE_OPERATION_TIMEOUTED: return WS_LASTERR_OPERATION_TIMEOUTED; case CURLE_HTTP_POST_ERROR: return WS_LASTERR_HTTP_POST_ERROR; case CURLE_BAD_DOWNLOAD_RESUME: return WS_LASTERR_BAD_DOWNLOAD_RESUME; case CURLE_TOO_MANY_REDIRECTS: return WS_LASTERR_TOO_MANY_REDIRECTS; case CURLE_SSL_CONNECT_ERROR: return WS_LASTERR_SSL_CONNECT_ERROR; case CURLE_BAD_FUNCTION_ARGUMENT: return WS_LASTERR_CURL_BAD_FUNCTION_ARG; case CURLE_SSL_PEER_CERTIFICATE: return WS_LASTERR_SSL_PEER_CERTIFICATE; case CURLE_SSL_ENGINE_NOTFOUND: return WS_LASTERR_SSL_ENGINE_NOTFOUND; case CURLE_SSL_ENGINE_SETFAILED: return WS_LASTERR_SSL_ENGINE_SETFAILED; case CURLE_SSL_CERTPROBLEM: return WS_LASTERR_SSL_CERTPROBLEM; case CURLE_SSL_CACERT: return WS_LASTERR_SSL_CACERT; #if LIBCURL_VERSION_NUM > 0x70C01 case CURLE_SSL_ENGINE_INITFAILED: return WS_LASTERR_SSL_ENGINE_INITFAILED; #endif case CURLE_SEND_ERROR: return WS_LASTERR_SEND_ERROR; case CURLE_RECV_ERROR: return WS_LASTERR_RECV_ERROR; case CURLE_BAD_CONTENT_ENCODING: return WS_LASTERR_BAD_CONTENT_ENCODING; #if LIBCURL_VERSION_NUM >= 0x70D01 case CURLE_LOGIN_DENIED: return WS_LASTERR_LOGIN_DENIED; #else /* Map 67 (same as CURLE_LOGIN_DENIED) got from OS that has lower version of CURL in which * CURLE_LOGIN_DENIED is not defined. This way we get the same error code in case of login failure */ case 67: return WS_LASTERR_LOGIN_DENIED; #endif case CURLE_SSL_CRL_BADFILE: return WS_LASTERR_BAD_CRL_FILE; default: return WS_LASTERR_OTHER_ERROR; } return WS_LASTERR_OTHER_ERROR; } static size_t write_handler( void *ptr, size_t size, size_t nmemb, void *data) { u_buf_t *buf = data; size_t len; len = size * nmemb; u_buf_append(buf, ptr, len); debug("write_handler: recieved %d bytes, all = %d\n", len, u_buf_len(buf)); return len; } #ifdef ENABLE_EVENTING_SUPPORT static int ssl_certificate_thumbprint_verify_callback(X509_STORE_CTX *ctx, void *arg) { unsigned char *thumbprint = (unsigned char *)arg; EVP_MD *tempDigest; unsigned char tempFingerprint[EVP_MAX_MD_SIZE]; unsigned int tempFingerprintLen; tempDigest = (EVP_MD*)EVP_sha1( ); #if OPENSSL_VERSION_NUMBER >= 0x10100000L X509 *cert = X509_STORE_CTX_get_current_cert(ctx); #else X509 *cert = ctx->cert; #endif if(!cert) return 0; if ( X509_digest(cert, tempDigest, tempFingerprint, &tempFingerprintLen ) <= 0) return 0; if(!memcmp(tempFingerprint, thumbprint, tempFingerprintLen)) return 1; return 0; } static CURLcode sslctxfun(CURL *curl, void *sslctx, void *parm) { CURLcode r = CURLE_OK; SSL_CTX * ctx = (SSL_CTX *) sslctx ; SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0 ); SSL_CTX_set_cert_verify_callback(ctx, ssl_certificate_thumbprint_verify_callback, parm); return r; } #endif static void * init_curl_transport(WsManClient *cl) { CURL *curl; CURLcode r = CURLE_OK; char *sslhack; long sslversion; dictionary *ini = iniparser_new(wsmc_get_conffile(cl)); #define curl_err(str) debug("Error = %d (%s); %s", \ r, curl_easy_strerror(r), str); curl = curl_easy_init(); if (curl == NULL) { r = CURLE_FAILED_INIT; debug("Could not init easy curl"); goto DONE; } // client:curlopt_nosignal if (ini) { int nosignal = iniparser_getint(ini, "client:curlopt_nosignal", 0); r = curl_easy_setopt(curl, CURLOPT_NOSIGNAL, nosignal); if (r != 0) { curl_err("curl_easy_setopt(CURLOPT_NOSIGNAL) failed"); goto DONE; } } debug("cl->authentication.verify_peer: %d", cl->authentication.verify_peer ); // verify peer r = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, wsman_transport_get_verify_peer(cl)?1:0); if (r != 0) { curl_err("curl_easy_setopt(CURLOPT_SSL_VERIFYPEER) failed"); goto DONE; } // verify host r = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, wsman_transport_get_verify_host(cl)?2:0); if (r != 0) { curl_err("curl_easy_setopt(CURLOPT_SSL_VERIFYHOST) failed"); goto DONE; } r = curl_easy_setopt(curl, CURLOPT_PROXY, cl->proxy_data.proxy); if (r != 0) { curl_err("Could notcurl_easy_setopt(curl, CURLOPT_PROXY, ...)"); goto DONE; } r = curl_easy_setopt(curl, CURLOPT_TIMEOUT, cl->transport_timeout); if (r != 0) { curl_err("Could notcurl_easy_setopt(curl, CURLOPT_TIMEOUT, ...)"); goto DONE; } r = curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, cl->proxy_data.proxy_auth); if (r != 0) { curl_err("Could notcurl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, ...)"); goto DONE; } if (0 != cl->authentication.verify_peer && 0 != cl->authentication.crl_check) { if (cl->authentication.crl_file == NULL) { if (ini != NULL) { char *crlfile = iniparser_getstr(ini, "client:crlfile"); wsman_transport_set_crlfile(cl, crlfile); } } if (cl->authentication.crl_file != NULL) { debug("wsman-curl-client-transport.c: init_curl_transport() : CRL file = %s\n",cl->authentication.crl_file); r = curl_easy_setopt(curl, CURLOPT_CRLFILE, cl->authentication.crl_file); } } if (cl->authentication.capath) { r = curl_easy_setopt(curl, CURLOPT_CAPATH, cl->authentication.capath); if (r != 0) { curl_err("Could not curl_easy_setopt(curl, CURLOPT_CAPATH, ..)"); goto DONE; } } // cainfo if (cl->authentication.cainfo) { r = curl_easy_setopt(curl, CURLOPT_CAINFO, cl->authentication.cainfo); if (r != 0) { curl_err("Could not curl_easy_setopt(curl, CURLOPT_CAINFO, ..)"); goto DONE; } } // certificate thumbprint #ifdef ENABLE_EVENTING_SUPPORT /* Bug in e.g. Fedora: [ curl-Bugs-1924441 ] SSL callback option with NSS-linked libcurl */ #ifndef NO_SSL_CALLBACK else if (strlen((char *)cl->authentication.certificatethumbprint) > 0 && 0 != cl->authentication.verify_peer) { r = curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun); if(r != 0) { curl_err("Could not curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION)"); goto DONE; } r = curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, (void *)cl->authentication.certificatethumbprint); if(r != 0) { curl_err("Could not curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA)"); goto DONE; } } #endif #endif // sslkey r = curl_easy_setopt(curl, CURLOPT_SSLKEY, cl->authentication.sslkey); if (r != 0) { curl_err("Could not curl_easy_setopt(curl, CURLOPT_SSLKEY, ..)"); goto DONE; } // sslcert r = curl_easy_setopt(curl, CURLOPT_SSLCERT, cl->authentication.sslcert ); if (r != 0) { curl_err("Could not curl_easy_setopt(curl, CURLOPT_SSLCERT, ..)"); goto DONE; } /* enforce specific ssl version if requested */ sslhack = getenv("OPENWSMAN_CURL_TRANSPORT_SSLVERSION"); if (sslhack == NULL) { sslversion = CURL_SSLVERSION_DEFAULT; } else if (!strcmp(sslhack,"tlsv1")) { sslversion = CURL_SSLVERSION_TLSv1; } else if (!strcmp(sslhack,"sslv2")) { sslversion = CURL_SSLVERSION_SSLv2; } else if (!strcmp(sslhack,"sslv3")) { sslversion = CURL_SSLVERSION_SSLv3; #if LIBCURL_VERSION_NUM >= 0x072200 } else if (!strcmp(sslhack,"tlsv1.0")) { sslversion = CURL_SSLVERSION_TLSv1_0; } else if (!strcmp(sslhack,"tlsv1.1")) { sslversion = CURL_SSLVERSION_TLSv1_1; } else if (!strcmp(sslhack,"tlsv1.2")) { sslversion = CURL_SSLVERSION_TLSv1_2; #endif } else { sslversion = CURL_SSLVERSION_DEFAULT; } r = curl_easy_setopt(curl, CURLOPT_SSLVERSION, sslversion ); if (r != 0) { curl_err("Could not curl_easy_setopt(curl, CURLOPT_SSLVERSION, ..)"); goto DONE; } iniparser_free(ini); return (void *)curl; DONE: cl->last_error = convert_to_last_error(r); curl_easy_cleanup(curl); iniparser_free(ini); return NULL; #undef curl_err } void wsmc_handler( WsManClient *cl, WsXmlDocH rqstDoc, void* user_data) { #define curl_err(str) debug("Error = %d (%s); %s", \ r, curl_easy_strerror(r), str); WsManConnection *con = cl->connection; CURL *curl = NULL; CURLcode r; char *upwd = NULL; char *usag = NULL; struct curl_slist *headers=NULL; char *buf = NULL; int len; char *soapact_header = NULL; long http_code; long auth_avail = 0; char *_user = NULL, *_pass = NULL; u_buf_t *response = NULL; //char *soapaction; char *tmp_str = NULL; if (!cl->initialized && wsmc_transport_init(cl, NULL)) { cl->last_error = WS_LASTERR_FAILED_INIT; return; } if (cl->transport == NULL) { cl->transport = init_curl_transport(cl); if (cl->transport == NULL) { return; } } curl = (CURL *)cl->transport; r = curl_easy_setopt(curl, CURLOPT_URL, cl->data.endpoint); if (r != CURLE_OK) { cl->fault_string = u_strdup(curl_easy_strerror(r)); curl_err("Could not curl_easy_setopt(curl, CURLOPT_URL, ...)"); goto DONE; } r = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_handler); if (r != CURLE_OK) { cl->fault_string = u_strdup(curl_easy_strerror(r)); curl_err("Could not curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ..)"); goto DONE; } u_buf_create(&response); r = curl_easy_setopt(curl, CURLOPT_WRITEDATA, response); if (r != CURLE_OK) { cl->fault_string = u_strdup(curl_easy_strerror(r)); curl_err("Could not curl_easy_setopt(curl, CURLOPT_WRITEDATA, ..)"); goto DONE; } char content_type[64]; snprintf(content_type, 64, "Content-Type: application/soap+xml;charset=%s", cl->content_encoding); headers = curl_slist_append(headers, content_type); tmp_str = wsman_transport_get_agent(cl); usag = malloc(12 + strlen(tmp_str) + 1); if (usag == NULL) { r = CURLE_OUT_OF_MEMORY; cl->fault_string = u_strdup("Could not malloc memory"); curl_err("Could not malloc memory"); goto DONE; } sprintf(usag, "User-Agent: %s", tmp_str); free(tmp_str); headers = curl_slist_append(headers, usag); #if 0 soapaction = ws_xml_get_xpath_value(rqstDoc, "/s:Envelope/s:Header/wsa:Action"); if (soapaction) { soapact_header = malloc(12 + strlen(soapaction) + 1); if (soapact_header) { sprintf(soapact_header, "SOAPAction: %s", soapaction); headers = curl_slist_append(headers, soapact_header); } u_free(soapaction); } #endif r = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); if (r != CURLE_OK) { cl->fault_string = u_strdup(curl_easy_strerror(r)); curl_err("Could not curl_easy_setopt(curl, CURLOPT_HTTPHEADER, ..)"); goto DONE; } ws_xml_dump_memory_enc(rqstDoc, &buf, &len, cl->content_encoding); #if 0 int count = 0; while(count < len) { printf("%c",buf[count++]); } #endif debug("*****set post buf len = %d******",len); r = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, buf); if (r != CURLE_OK) { cl->fault_string = u_strdup(curl_easy_strerror(r)); curl_err("Could not curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ..)"); goto DONE; } r = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len); if (r != CURLE_OK) { cl->fault_string = u_strdup(curl_easy_strerror(r)); curl_err("Could not curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, ..)"); goto DONE; } int iDone = 0; while (1) { u_free(_user); u_free(_pass); _user = wsmc_get_user(cl); _pass = wsmc_get_password(cl); if (_user && _pass && cl->data.auth_set) { r = curl_easy_setopt(curl, CURLOPT_HTTPAUTH, cl->data.auth_set); if (r != CURLE_OK) { cl->fault_string = u_strdup(curl_easy_strerror(r)); curl_err("curl_easy_setopt(CURLOPT_HTTPAUTH) failed"); goto DONE; } u_free(upwd); upwd = u_strdup_printf( "%s:%s", _user , _pass); if (!upwd) { r = CURLE_OUT_OF_MEMORY; cl->fault_string = u_strdup("Could not malloc memory"); curl_err("Could not malloc memory"); goto DONE; } r = curl_easy_setopt(curl, CURLOPT_USERPWD, upwd); if (r != CURLE_OK) { cl->fault_string = u_strdup(curl_easy_strerror(r)); curl_err("curl_easy_setopt(curl, CURLOPT_USERPWD, ..) failed"); goto DONE; } } if (wsman_debug_level_debugged(DEBUG_LEVEL_MESSAGE)) { curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); } r = curl_easy_perform(curl); if (r != CURLE_OK) { cl->fault_string = u_strdup(curl_easy_strerror(r)); curl_err("curl_easy_perform failed"); goto DONE; } r = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); if (r != CURLE_OK) { cl->fault_string = u_strdup(curl_easy_strerror(r)); curl_err("curl_easy_getinfo(CURLINFO_RESPONSE_CODE) failed"); goto DONE; } switch (http_code) { case 200: case 400: case 500: // The resource was successfully retrieved or WSMan server // returned a HTTP status code. You can use WinHttpReadData to // read the contents of the server's response. iDone = 1; break; case 401: // The server requires authentication. break; default: // The status code does not indicate success. r = WS_LASTERR_OTHER_ERROR; iDone = 1; break; } if(iDone == 1) { break; } /* we are here because of authentication required */ r = curl_easy_getinfo(curl, CURLINFO_HTTPAUTH_AVAIL, &auth_avail); if (r != CURLE_OK) { cl->fault_string = u_strdup(curl_easy_strerror(r)); curl_err("curl_easy_getinfo(CURLINFO_HTTPAUTH_AVAIL) failed"); goto DONE; } cl->data.auth_set = reauthenticate(cl, cl->data.auth_set, auth_avail, &cl->data.user, &cl->data.pwd); u_buf_clear(response); if (cl->data.auth_set == 0) { /* FIXME: user wants to cancel authentication */ #if LIBCURL_VERSION_NUM >= 0x70D01 r = CURLE_LOGIN_DENIED; #else /* Map the login failure error to CURLE_LOGIN_DENIED (67) so that we * get the same error code in case of login failure */ r = 67; #endif cl->fault_string = u_strdup(curl_easy_strerror(r)); curl_err("user/password wrong or empty."); break; } } #if 0 unsigned char *mbbuf = NULL; iconv_t cd; if(strcmp(cl->content_encoding, "UTF-8")) { cd = iconv_open("UTF-8", cl->content_encoding); if(cd == -1) { cl->last_error = WS_LASTERR_BAD_CONTENT_ENCODING; goto DONE2; } mbbuf = u_zalloc(u_buf_len(response)); size_t outbuf_len = u_buf_len(response); size_t inbuf_len = outbuf_len; char *inbuf = u_buf_ptr(response); char *outbuf = mbbuf; size_t coverted = iconv(cd, &inbuf, &inbuf_len, &outbuf, &outbuf_len); iconv_close(cd); if( coverted == -1) { cl->last_error = WS_LASTERR_BAD_CONTENT_ENCODING; goto DONE2; } u_buf_append(con->response, mbbuf, u_buf_len(response) - inbuf_len); } u_free(mbbuf); #endif u_buf_append(con->response, u_buf_ptr(response), u_buf_len(response)); DONE: curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); cl->response_code = http_code; cl->last_error = convert_to_last_error(r); debug("curl error code: %d.", r); debug("cl->response_code: %d.", cl->response_code); debug("cl->last_error code: %d.", cl->last_error); curl_slist_free_all(headers); u_buf_free(response); u_free(soapact_header); u_free(usag); u_free(upwd); u_free(_pass); u_free(_user); #ifdef _WIN32 ws_xml_free_memory(buf); #else u_free(buf); #endif return; #undef curl_err } int wsmc_transport_init(WsManClient *cl, void *arg) { CURLcode r; pthread_mutex_lock(&curl_mutex); if (cl->initialized) { pthread_mutex_unlock(&curl_mutex); return 0; } r = curl_global_init(CURL_GLOBAL_SSL | CURL_GLOBAL_WIN32); if (r == CURLE_OK) { cl->initialized = 1; } pthread_mutex_unlock(&curl_mutex); if (r != CURLE_OK) { debug("Error = %d (%s); Could not initialize curl globals", r, curl_easy_strerror(r)); } return (r == CURLE_OK ? 0 : 1); } void wsmc_transport_fini(WsManClient *cl) { pthread_mutex_lock(&curl_mutex); if (cl->initialized == 0 ) { pthread_mutex_unlock(&curl_mutex); return; } curl_global_cleanup(); cl->initialized = 0; pthread_mutex_unlock(&curl_mutex); return; } void wsman_transport_close_transport(WsManClient *cl) { if (cl->transport != NULL) { curl_easy_cleanup((CURL *)cl->transport); } cl->transport = NULL; }