/*
- * Copyright 2001-2009 Internet2
+ * Licensed to UCAID under one or more contributor license agreements.
+ * See the NOTICE file distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file to you 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
*
- * 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
+ * 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,
/**
* CURLSOAPTransport.cpp
*
- * libcurl-based SOAPTransport implementation
+ * libcurl-based SOAPTransport implementation.
*/
#include "internal.h"
Category& m_log;
};
- static XMLTOOL_DLLLOCAL CURLPool* g_CURLPool = NULL;
+ static XMLTOOL_DLLLOCAL CURLPool* g_CURLPool = nullptr;
class XMLTOOL_DLLLOCAL CURLSOAPTransport : public HTTPSOAPTransport, public OpenSSLSOAPTransport
{
public:
CURLSOAPTransport(const Address& addr)
: m_sender(addr.m_from ? addr.m_from : ""), m_peerName(addr.m_to ? addr.m_to : ""), m_endpoint(addr.m_endpoint),
- m_handle(NULL), m_headers(NULL),
+ m_handle(nullptr), m_headers(nullptr),
#ifndef XMLTOOLING_NO_XMLSEC
- m_cred(NULL), m_trustEngine(NULL), m_peerResolver(NULL), m_mandatory(false),
+ m_cred(nullptr), m_trustEngine(nullptr), m_peerResolver(nullptr), m_mandatory(false),
#endif
- m_ssl_callback(NULL), m_ssl_userptr(NULL), m_chunked(true), m_authenticated(false) {
+ m_openssl_ops(SSL_OP_ALL|SSL_OP_NO_SSLv2), m_ssl_callback(nullptr), m_ssl_userptr(nullptr),
+ m_chunked(true), m_authenticated(false), m_cacheTag(nullptr) {
m_handle = g_CURLPool->get(addr);
curl_easy_setopt(m_handle,CURLOPT_URL,addr.m_endpoint);
curl_easy_setopt(m_handle,CURLOPT_CONNECTTIMEOUT,15);
curl_easy_setopt(m_handle,CURLOPT_TIMEOUT,30);
curl_easy_setopt(m_handle,CURLOPT_HTTPAUTH,0);
- curl_easy_setopt(m_handle,CURLOPT_USERPWD,NULL);
+ curl_easy_setopt(m_handle,CURLOPT_USERPWD,nullptr);
curl_easy_setopt(m_handle,CURLOPT_SSL_VERIFYHOST,2);
curl_easy_setopt(m_handle,CURLOPT_HEADERDATA,this);
m_headers=curl_slist_append(m_headers,"Content-Type: text/xml");
virtual ~CURLSOAPTransport() {
curl_slist_free_all(m_headers);
- curl_easy_setopt(m_handle,CURLOPT_ERRORBUFFER,NULL);
- curl_easy_setopt(m_handle,CURLOPT_PRIVATE,m_authenticated ? "secure" : NULL); // Save off security "state".
+ curl_easy_setopt(m_handle, CURLOPT_USERAGENT, nullptr);
+ curl_easy_setopt(m_handle, CURLOPT_ERRORBUFFER, nullptr);
+ curl_easy_setopt(m_handle, CURLOPT_PRIVATE, m_authenticated ? "secure" : nullptr); // Save off security "state".
g_CURLPool->put(m_sender.c_str(), m_peerName.c_str(), m_endpoint.c_str(), m_handle);
}
return (curl_easy_setopt(m_handle,CURLOPT_TIMEOUT,timeout)==CURLE_OK);
}
- bool setAuth(transport_auth_t authType, const char* username=NULL, const char* password=NULL);
+ bool setAuth(transport_auth_t authType, const char* username=nullptr, const char* password=nullptr);
bool setVerifyHost(bool verify) {
return (curl_easy_setopt(m_handle,CURLOPT_SSL_VERIFYHOST,verify ? 2 : 0)==CURLE_OK);
}
#ifndef XMLTOOLING_NO_XMLSEC
- bool setCredential(const Credential* cred=NULL) {
+ bool setCredential(const Credential* cred=nullptr) {
const OpenSSLCredential* down = dynamic_cast<const OpenSSLCredential*>(cred);
if (!down) {
- m_cred = NULL;
- return (cred==NULL);
+ m_cred = nullptr;
+ return (cred==nullptr);
}
m_cred = down;
return true;
}
bool setTrustEngine(
- const X509TrustEngine* trustEngine=NULL,
- const CredentialResolver* peerResolver=NULL,
- CredentialCriteria* criteria=NULL,
+ const X509TrustEngine* trustEngine=nullptr,
+ const CredentialResolver* peerResolver=nullptr,
+ CredentialCriteria* criteria=nullptr,
bool mandatory=true
) {
const OpenSSLTrustEngine* down = dynamic_cast<const OpenSSLTrustEngine*>(trustEngine);
if (!down) {
- m_trustEngine = NULL;
- m_peerResolver = NULL;
- m_criteria = NULL;
- return (trustEngine==NULL);
+ m_trustEngine = nullptr;
+ m_peerResolver = nullptr;
+ m_criteria = nullptr;
+ return (trustEngine==nullptr);
}
m_trustEngine = down;
m_peerResolver = peerResolver;
return true;
}
- bool setProviderOption(const char* provider, const char* option, const char* value) {
- if (!provider || strcmp(provider, "CURL"))
- return false;
- // For libcurl, the option is an enum and the value type depends on the option.
- CURLoption opt = static_cast<CURLoption>(strtol(option, NULL, 10));
- if (opt < CURLOPTTYPE_OBJECTPOINT)
- return (curl_easy_setopt(m_handle, opt, strtol(value, NULL, 10)) == CURLE_OK);
-#ifdef CURLOPTTYPE_OFF_T
- else if (opt < CURLOPTTYPE_OFF_T) {
- if (value)
- m_saved_options.push_back(value);
- return (curl_easy_setopt(m_handle, opt, value ? m_saved_options.back().c_str() : NULL) == CURLE_OK);
- }
-# ifdef HAVE_CURL_OFF_T
- else if (sizeof(curl_off_t) == sizeof(long))
- return (curl_easy_setopt(m_handle, opt, strtol(value, NULL, 10)) == CURLE_OK);
-# else
- else if (sizeof(off_t) == sizeof(long))
- return (curl_easy_setopt(m_handle, opt, strtol(value, NULL, 10)) == CURLE_OK);
-# endif
- return false;
-#else
- else {
- if (value)
- m_saved_options.push_back(value);
- return (curl_easy_setopt(m_handle, opt, value ? m_saved_options.back().c_str() : NULL) == CURLE_OK);
- }
-#endif
+ bool followRedirects(bool follow, unsigned int maxRedirs) {
+ return (
+ curl_easy_setopt(m_handle, CURLOPT_FOLLOWLOCATION, (follow ? 1 : 0)) == CURLE_OK &&
+ curl_easy_setopt(m_handle, CURLOPT_MAXREDIRS, (follow ? maxRedirs : 0)) == CURLE_OK
+ );
+ }
+
+ bool setCacheTag(string* cacheTag) {
+ m_cacheTag = cacheTag;
+ return true;
}
+ bool setProviderOption(const char* provider, const char* option, const char* value);
+
void send(istream& in) {
send(&in);
}
- void send(istream* in=NULL);
+ void send(istream* in=nullptr);
istream& receive() {
return m_stream;
}
string getContentType() const;
+ long getStatusCode() const;
bool setRequestHeader(const char* name, const char* val) {
string temp(name);
const vector<string>& getResponseHeader(const char* val) const;
- bool setSSLCallback(ssl_ctx_callback_fn fn, void* userptr=NULL) {
+ bool setSSLCallback(ssl_ctx_callback_fn fn, void* userptr=nullptr) {
m_ssl_callback=fn;
m_ssl_userptr=userptr;
return true;
CURL* m_handle;
stringstream m_stream;
struct curl_slist* m_headers;
+ string m_useragent;
map<string,vector<string> > m_response_headers;
vector<string> m_saved_options;
#ifndef XMLTOOLING_NO_XMLSEC
CredentialCriteria* m_criteria;
bool m_mandatory;
#endif
+ int m_openssl_ops;
ssl_ctx_callback_fn m_ssl_callback;
void* m_ssl_userptr;
bool m_chunked;
bool m_authenticated;
+ string* m_cacheTag;
friend size_t XMLTOOL_DLLLOCAL curl_header_hook(void* ptr, size_t size, size_t nmemb, void* stream);
friend CURLcode XMLTOOL_DLLLOCAL xml_ssl_ctx_callback(CURL* curl, SSL_CTX* ssl_ctx, void* userptr);
void xmltooling::termSOAPTransports()
{
delete g_CURLPool;
- g_CURLPool = NULL;
+ g_CURLPool = nullptr;
}
OpenSSLSOAPTransport::OpenSSLSOAPTransport()
// Create a new connection and set non-varying options.
CURL* handle=curl_easy_init();
if (!handle)
- return NULL;
+ return nullptr;
curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1);
curl_easy_setopt(handle,CURLOPT_NOSIGNAL,1);
curl_easy_setopt(handle,CURLOPT_FAILONERROR,1);
curl_easy_setopt(handle,CURLOPT_SSL_CIPHER_LIST,"ALL:!aNULL:!LOW:!EXPORT:!SSLv2");
// Verification of the peer is via TrustEngine only.
curl_easy_setopt(handle,CURLOPT_SSL_VERIFYPEER,0);
- curl_easy_setopt(handle,CURLOPT_CAINFO,NULL);
+ curl_easy_setopt(handle,CURLOPT_CAINFO,nullptr);
curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,&curl_header_hook);
curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,&curl_write_hook);
curl_easy_setopt(handle,CURLOPT_DEBUGFUNCTION,&curl_debug_hook);
-
return handle;
}
else
i->second.push_back(handle);
- CURL* killit=NULL;
+ CURL* killit=nullptr;
if (++m_size > 256) {
// Kick a handle out from the back of the bus.
while (true) {
if (authType==transport_auth_none) {
if (curl_easy_setopt(m_handle,CURLOPT_HTTPAUTH,0)!=CURLE_OK)
return false;
- return (curl_easy_setopt(m_handle,CURLOPT_USERPWD,NULL)==CURLE_OK);
+ return (curl_easy_setopt(m_handle,CURLOPT_USERPWD,nullptr)==CURLE_OK);
}
long flag=0;
switch (authType) {
return (curl_easy_setopt(m_handle,CURLOPT_USERPWD,m_simplecreds.c_str())==CURLE_OK);
}
+bool CURLSOAPTransport::setProviderOption(const char* provider, const char* option, const char* value)
+{
+ if (!provider || !option || !value) {
+ return false;
+ }
+ else if (!strcmp(provider, "OpenSSL")) {
+ if (!strcmp(option, "SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION") && (*value=='1' || *value=='t')) {
+ // If the new option to enable buggy rengotiation is available, set it.
+ // Otherwise, signal false if this is newer than 0.9.8k, because that
+ // means it's 0.9.8l, which blocks renegotiation, and therefore will
+ // not honor this request. Older versions are buggy, so behave as though
+ // the flag was set anyway, so we signal true.
+#if defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)
+ m_openssl_ops |= SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
+ return true;
+#elif (OPENSSL_VERSION_NUMBER > 0x009080bfL)
+ return false;
+#else
+ return true;
+#endif
+ }
+ return false;
+ }
+ else if (strcmp(provider, "CURL")) {
+ return false;
+ }
+
+ // For libcurl, the option is an enum and the value type depends on the option.
+ CURLoption opt = static_cast<CURLoption>(strtol(option, nullptr, 10));
+ if (opt < CURLOPTTYPE_OBJECTPOINT)
+ return (curl_easy_setopt(m_handle, opt, strtol(value, nullptr, 10)) == CURLE_OK);
+#ifdef CURLOPTTYPE_OFF_T
+ else if (opt < CURLOPTTYPE_OFF_T) {
+ if (value)
+ m_saved_options.push_back(value);
+ return (curl_easy_setopt(m_handle, opt, value ? m_saved_options.back().c_str() : nullptr) == CURLE_OK);
+ }
+# ifdef HAVE_CURL_OFF_T
+ else if (sizeof(curl_off_t) == sizeof(long))
+ return (curl_easy_setopt(m_handle, opt, strtol(value, nullptr, 10)) == CURLE_OK);
+# else
+ else if (sizeof(off_t) == sizeof(long))
+ return (curl_easy_setopt(m_handle, opt, strtol(value, nullptr, 10)) == CURLE_OK);
+# endif
+ return false;
+#else
+ else {
+ if (value)
+ m_saved_options.push_back(value);
+ return (curl_easy_setopt(m_handle, opt, value ? m_saved_options.back().c_str() : nullptr) == CURLE_OK);
+ }
+#endif
+}
+
const vector<string>& CURLSOAPTransport::getResponseHeader(const char* name) const
{
static vector<string> emptyVector;
string CURLSOAPTransport::getContentType() const
{
- char* content_type=NULL;
+ char* content_type=nullptr;
curl_easy_getinfo(m_handle,CURLINFO_CONTENT_TYPE,&content_type);
return content_type ? content_type : "";
}
+long CURLSOAPTransport::getStatusCode() const
+{
+ long code=200;
+ if (curl_easy_getinfo(m_handle, CURLINFO_RESPONSE_CODE, &code) != CURLE_OK)
+ code = 200;
+ return code;
+}
+
void CURLSOAPTransport::send(istream* in)
{
#ifdef _DEBUG
msg.append(buf,in->gcount());
}
curl_easy_setopt(m_handle,CURLOPT_POST,1);
- curl_easy_setopt(m_handle,CURLOPT_READFUNCTION,NULL);
+ curl_easy_setopt(m_handle,CURLOPT_READFUNCTION,nullptr);
curl_easy_setopt(m_handle,CURLOPT_POSTFIELDS,msg.c_str());
curl_easy_setopt(m_handle,CURLOPT_POSTFIELDSIZE,msg.length());
}
else {
curl_easy_setopt(m_handle,CURLOPT_HTTPGET,1);
+ curl_easy_setopt(m_handle,CURLOPT_FOLLOWLOCATION,1);
+ curl_easy_setopt(m_handle,CURLOPT_MAXREDIRS,6);
}
char curl_errorbuf[CURL_ERROR_SIZE];
if (log_curl.isDebugEnabled())
curl_easy_setopt(m_handle,CURLOPT_VERBOSE,1);
+ // Check for cache tag.
+ if (m_cacheTag && !m_cacheTag->empty()) {
+ string hdr("If-None-Match: ");
+ hdr += *m_cacheTag;
+ m_headers = curl_slist_append(m_headers, hdr.c_str());
+ }
+
+ m_useragent = XMLToolingConfig::getConfig().user_agent;
+ if (!m_useragent.empty()) {
+ curl_version_info_data* curlver = curl_version_info(CURLVERSION_NOW);
+ m_useragent += " libcurl/";
+ if (curlver)
+ m_useragent = m_useragent + curlver->version + ' ' + curlver->ssl_version;
+ else
+ m_useragent = m_useragent + LIBCURL_VERSION + ' ' + OPENSSL_VERSION_TEXT;
+ curl_easy_setopt(m_handle, CURLOPT_USERAGENT, m_useragent.c_str());
+ }
+
// Set request headers.
curl_easy_setopt(m_handle,CURLOPT_HTTPHEADER,m_headers);
// Restore security "state". Necessary because the callback only runs
// when handshakes occur. Even new TCP connections won't execute it.
- char* priv=NULL;
+ char* priv=nullptr;
curl_easy_getinfo(m_handle,CURLINFO_PRIVATE,&priv);
if (priv)
m_authenticated=true;
}
else {
- curl_easy_setopt(m_handle,CURLOPT_SSL_CTX_FUNCTION,NULL);
- curl_easy_setopt(m_handle,CURLOPT_SSL_CTX_DATA,NULL);
+ curl_easy_setopt(m_handle,CURLOPT_SSL_CTX_FUNCTION,nullptr);
+ curl_easy_setopt(m_handle,CURLOPT_SSL_CTX_DATA,nullptr);
}
// Make the call.
string("CURLSOAPTransport failed while contacting SOAP endpoint (") + m_endpoint + "): " +
(curl_errorbuf[0] ? curl_errorbuf : "no further information available"));
}
+
+ // Check for outgoing cache tag.
+ if (m_cacheTag) {
+ const vector<string>& tags = getResponseHeader("ETag");
+ if (!tags.empty())
+ *m_cacheTag = tags.front();
+ }
}
// callback to buffer headers from server
if (ctx->m_criteria) {
ctx->m_criteria->setUsage(Credential::TLS_CREDENTIAL);
// Bypass name check (handled for us by curl).
- ctx->m_criteria->setPeerName(NULL);
+ ctx->m_criteria->setPeerName(nullptr);
success = ctx->m_trustEngine->validate(x509_ctx->cert,x509_ctx->untrusted,*(ctx->m_peerResolver),ctx->m_criteria);
}
else {
{
CURLSOAPTransport* conf = reinterpret_cast<CURLSOAPTransport*>(userptr);
- // Manually disable SSLv2 so we're not dependent on libcurl to do it.
+ // Default flags manually disable SSLv2 so we're not dependent on libcurl to do it.
// Also disable the ticket option where implemented, since this breaks a variety
// of servers. Newer libcurl also does this for us.
#ifdef SSL_OP_NO_TICKET
- SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_TICKET);
+ SSL_CTX_set_options(ssl_ctx, conf->m_openssl_ops|SSL_OP_NO_TICKET);
#else
- SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
+ SSL_CTX_set_options(ssl_ctx, conf->m_openssl_ops);
#endif
#ifndef XMLTOOLING_NO_XMLSEC
conf->m_cred->attach(ssl_ctx);
if (conf->m_trustEngine) {
- SSL_CTX_set_verify(ssl_ctx,SSL_VERIFY_PEER,NULL);
+ SSL_CTX_set_verify(ssl_ctx,SSL_VERIFY_PEER,nullptr);
#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
// With 0.9.7, we can pass a callback argument directly.
SSL_CTX_set_cert_verify_callback(ssl_ctx,verify_callback,userptr);
// With 0.9.6, there's no argument, so we're going to use a really embarrassing hack and
// stuff the argument in the depth property where it will get copied to the context object
// that's handed to the callback.
- SSL_CTX_set_cert_verify_callback(ssl_ctx,reinterpret_cast<int (*)()>(verify_callback),NULL);
+ SSL_CTX_set_cert_verify_callback(ssl_ctx,reinterpret_cast<int (*)()>(verify_callback),nullptr);
SSL_CTX_set_verify_depth(ssl_ctx,reinterpret_cast<int>(userptr));
#endif
}