PageRenderTime 117ms CodeModel.GetById 30ms app.highlight 63ms RepoModel.GetById 18ms app.codeStats 0ms

/indra/newview/llxmlrpctransaction.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 654 lines | 473 code | 111 blank | 70 comment | 31 complexity | 4dcbafbed2bfab92d182705d30fecfdd MD5 | raw file
  1/** 
  2 * @file llxmlrpctransaction.cpp
  3 * @brief LLXMLRPCTransaction and related class implementations 
  4 *
  5 * $LicenseInfo:firstyear=2006&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "llviewerprecompiledheaders.h"
 28#include <openssl/x509_vfy.h>
 29#include <openssl/ssl.h>
 30#include "llsecapi.h"
 31
 32#include "llxmlrpctransaction.h"
 33#include "llxmlrpclistener.h"
 34
 35#include "llcurl.h"
 36#include "llviewercontrol.h"
 37
 38// Have to include these last to avoid queue redefinition!
 39#include <xmlrpc-epi/xmlrpc.h>
 40
 41#include "llappviewer.h"
 42#include "lltrans.h"
 43
 44// Static instance of LLXMLRPCListener declared here so that every time we
 45// bring in this code, we instantiate a listener. If we put the static
 46// instance of LLXMLRPCListener into llxmlrpclistener.cpp, the linker would
 47// simply omit llxmlrpclistener.o, and shouting on the LLEventPump would do
 48// nothing.
 49static LLXMLRPCListener listener("LLXMLRPCTransaction");
 50
 51LLXMLRPCValue LLXMLRPCValue::operator[](const char* id) const
 52{
 53	return LLXMLRPCValue(XMLRPC_VectorGetValueWithID(mV, id));
 54}
 55
 56std::string LLXMLRPCValue::asString() const
 57{
 58	const char* s = XMLRPC_GetValueString(mV);
 59	return s ? s : "";
 60}
 61
 62int		LLXMLRPCValue::asInt() const	{ return XMLRPC_GetValueInt(mV); }
 63bool	LLXMLRPCValue::asBool() const	{ return XMLRPC_GetValueBoolean(mV) != 0; }
 64double	LLXMLRPCValue::asDouble() const	{ return XMLRPC_GetValueDouble(mV); }
 65
 66LLXMLRPCValue LLXMLRPCValue::rewind()
 67{
 68	return LLXMLRPCValue(XMLRPC_VectorRewind(mV));
 69}
 70
 71LLXMLRPCValue LLXMLRPCValue::next()
 72{
 73	return LLXMLRPCValue(XMLRPC_VectorNext(mV));
 74}
 75
 76bool LLXMLRPCValue::isValid() const
 77{
 78	return mV != NULL;
 79}
 80
 81LLXMLRPCValue LLXMLRPCValue::createArray()
 82{
 83	return LLXMLRPCValue(XMLRPC_CreateVector(NULL, xmlrpc_vector_array));
 84}
 85
 86LLXMLRPCValue LLXMLRPCValue::createStruct()
 87{
 88	return LLXMLRPCValue(XMLRPC_CreateVector(NULL, xmlrpc_vector_struct));
 89}
 90
 91
 92void LLXMLRPCValue::append(LLXMLRPCValue& v)
 93{
 94	XMLRPC_AddValueToVector(mV, v.mV);
 95}
 96
 97void LLXMLRPCValue::appendString(const std::string& v)
 98{
 99	XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueString(NULL, v.c_str(), 0));
100}
101
102void LLXMLRPCValue::appendInt(int v)
103{
104	XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueInt(NULL, v));
105}
106
107void LLXMLRPCValue::appendBool(bool v)
108{
109	XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueBoolean(NULL, v));
110}
111
112void LLXMLRPCValue::appendDouble(double v)
113{
114	XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueDouble(NULL, v));
115}
116
117
118void LLXMLRPCValue::append(const char* id, LLXMLRPCValue& v)
119{
120	XMLRPC_SetValueID(v.mV, id, 0);
121	XMLRPC_AddValueToVector(mV, v.mV);
122}
123
124void LLXMLRPCValue::appendString(const char* id, const std::string& v)
125{
126	XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueString(id, v.c_str(), 0));
127}
128
129void LLXMLRPCValue::appendInt(const char* id, int v)
130{
131	XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueInt(id, v));
132}
133
134void LLXMLRPCValue::appendBool(const char* id, bool v)
135{
136	XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueBoolean(id, v));
137}
138
139void LLXMLRPCValue::appendDouble(const char* id, double v)
140{
141	XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueDouble(id, v));
142}
143
144void LLXMLRPCValue::cleanup()
145{
146	XMLRPC_CleanupValue(mV);
147	mV = NULL;
148}
149
150XMLRPC_VALUE LLXMLRPCValue::getValue() const
151{
152	return mV;
153}
154
155
156class LLXMLRPCTransaction::Impl
157{
158public:
159	typedef LLXMLRPCTransaction::EStatus	EStatus;
160
161	LLCurlEasyRequest* mCurlRequest;
162
163	EStatus		mStatus;
164	CURLcode	mCurlCode;
165	std::string	mStatusMessage;
166	std::string	mStatusURI;
167	LLCurl::TransferInfo mTransferInfo;
168	
169	std::string			mURI;
170	char*				mRequestText;
171	int					mRequestTextSize;
172	
173	std::string			mProxyAddress;
174
175	std::string			mResponseText;
176	XMLRPC_REQUEST		mResponse;
177	std::string         mCertStore;
178	LLPointer<LLCertificate> mErrorCert;
179	
180	Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip);
181	Impl(const std::string& uri,
182		 const std::string& method, LLXMLRPCValue params, bool useGzip);
183	~Impl();
184	
185	bool process();
186	
187	void setStatus(EStatus code,
188				   const std::string& message = "", const std::string& uri = "");
189	void setCurlStatus(CURLcode);
190
191private:
192	void init(XMLRPC_REQUEST request, bool useGzip);
193	static int _sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param);
194	static CURLcode _sslCtxFunction(CURL * curl, void *sslctx, void *param);
195	static size_t curlDownloadCallback(
196		char* data, size_t size, size_t nmemb, void* user_data);
197};
198
199LLXMLRPCTransaction::Impl::Impl(const std::string& uri,
200		XMLRPC_REQUEST request, bool useGzip)
201	: mCurlRequest(0),
202	  mStatus(LLXMLRPCTransaction::StatusNotStarted),
203	  mURI(uri),
204	  mRequestText(0), 
205	  mResponse(0)
206{
207	init(request, useGzip);
208}
209
210
211LLXMLRPCTransaction::Impl::Impl(const std::string& uri,
212		const std::string& method, LLXMLRPCValue params, bool useGzip)
213	: mCurlRequest(0),
214	  mStatus(LLXMLRPCTransaction::StatusNotStarted),
215	  mURI(uri),
216	  mRequestText(0), 
217	  mResponse(0)
218{
219	XMLRPC_REQUEST request = XMLRPC_RequestNew();
220	XMLRPC_RequestSetMethodName(request, method.c_str());
221	XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
222	XMLRPC_RequestSetData(request, params.getValue());
223	
224	init(request, useGzip);
225    // DEV-28398: without this XMLRPC_RequestFree() call, it looks as though
226    // the 'request' object is simply leaked. It's less clear to me whether we
227    // should also ask to free request value data (second param 1), since the
228    // data come from 'params'.
229    XMLRPC_RequestFree(request, 1);
230}
231
232// _sslCertVerifyCallback
233// callback called when a cert verification is requested.
234// calls SECAPI to validate the context
235int LLXMLRPCTransaction::Impl::_sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param)
236{
237	LLXMLRPCTransaction::Impl *transaction = (LLXMLRPCTransaction::Impl *)param;
238	LLPointer<LLCertificateStore> store = gSecAPIHandler->getCertificateStore(transaction->mCertStore);
239	LLPointer<LLCertificateChain> chain = gSecAPIHandler->getCertificateChain(ctx);
240	LLSD validation_params = LLSD::emptyMap();
241	LLURI uri(transaction->mURI);
242	validation_params[CERT_HOSTNAME] = uri.hostName();
243	try
244	{
245		// don't validate hostname.  Let libcurl do it instead.  That way, it'll handle redirects
246		store->validate(VALIDATION_POLICY_SSL & (~VALIDATION_POLICY_HOSTNAME), chain, validation_params);
247	}
248	catch (LLCertValidationTrustException& cert_exception)
249	{
250		// this exception is is handled differently than the general cert
251		// exceptions, as we allow the user to actually add the certificate
252		// for trust.
253		// therefore we pass back a different error code
254		// NOTE: We're currently 'wired' to pass around CURL error codes.  This is
255		// somewhat clumsy, as we may run into errors that do not map directly to curl
256		// error codes.  Should be refactored with login refactoring, perhaps.
257		transaction->mCurlCode = CURLE_SSL_CACERT;
258		// set the status directly.  set curl status generates error messages and we want
259		// to use the fixed ones from the exceptions
260		transaction->setStatus(StatusCURLError, cert_exception.getMessage(), std::string());
261		// We should probably have a more generic way of passing information
262		// back to the error handlers.
263		transaction->mErrorCert = cert_exception.getCert();
264		return 0;		
265	}
266	catch (LLCertException& cert_exception)
267	{
268		transaction->mCurlCode = CURLE_SSL_PEER_CERTIFICATE;
269		// set the status directly.  set curl status generates error messages and we want
270		// to use the fixed ones from the exceptions
271		transaction->setStatus(StatusCURLError, cert_exception.getMessage(), std::string());
272		transaction->mErrorCert = cert_exception.getCert();
273		return 0;
274	}
275	catch (...)
276	{
277		// any other odd error, we just handle as a connect error.
278		transaction->mCurlCode = CURLE_SSL_CONNECT_ERROR;
279		transaction->setCurlStatus(CURLE_SSL_CONNECT_ERROR);
280		return 0;
281	}
282	return 1;
283}
284
285// _sslCtxFunction
286// Callback function called when an SSL Context is created via CURL
287// used to configure the context for custom cert validate(<, <#const & xs#>, <#T * #>, <#long #>)tion
288// based on SECAPI
289
290CURLcode LLXMLRPCTransaction::Impl::_sslCtxFunction(CURL * curl, void *sslctx, void *param)
291{
292	SSL_CTX * ctx = (SSL_CTX *) sslctx;
293	// disable any default verification for server certs
294	SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
295	// set the verification callback.
296	SSL_CTX_set_cert_verify_callback(ctx, _sslCertVerifyCallback, param);
297	// the calls are void
298	return CURLE_OK;
299	
300}
301
302void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip)
303{
304	if (!mCurlRequest)
305	{
306		mCurlRequest = new LLCurlEasyRequest();
307	}
308	if(!mCurlRequest->isValid())
309	{
310		llwarns << "mCurlRequest is invalid." << llendl ;
311
312		delete mCurlRequest ;
313		mCurlRequest = NULL ;
314		return ;
315	}
316
317	mErrorCert = NULL;
318
319//	mCurlRequest->setopt(CURLOPT_VERBOSE, 1); // useful for debugging
320	mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1);
321	mCurlRequest->setWriteCallback(&curlDownloadCallback, (void*)this);
322	BOOL vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert");
323	mCertStore = gSavedSettings.getString("CertStore");
324	mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, vefifySSLCert);
325	mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, vefifySSLCert ? 2 : 0);
326	// Be a little impatient about establishing connections.
327	mCurlRequest->setopt(CURLOPT_CONNECTTIMEOUT, 40L);
328	mCurlRequest->setSSLCtxCallback(_sslCtxFunction, (void *)this);
329
330	/* Setting the DNS cache timeout to -1 disables it completely.
331	   This might help with bug #503 */
332	mCurlRequest->setopt(CURLOPT_DNS_CACHE_TIMEOUT, -1);
333
334    mCurlRequest->slist_append("Content-Type: text/xml");
335
336	if (useGzip)
337	{
338		mCurlRequest->setoptString(CURLOPT_ENCODING, "");
339	}
340	
341	mRequestText = XMLRPC_REQUEST_ToXML(request, &mRequestTextSize);
342	if (mRequestText)
343	{
344		mCurlRequest->setoptString(CURLOPT_POSTFIELDS, mRequestText);
345		mCurlRequest->setopt(CURLOPT_POSTFIELDSIZE, mRequestTextSize);
346	}
347	else
348	{
349		setStatus(StatusOtherError);
350	}
351
352	mCurlRequest->sendRequest(mURI);
353}
354
355
356LLXMLRPCTransaction::Impl::~Impl()
357{
358	if (mResponse)
359	{
360		XMLRPC_RequestFree(mResponse, 1);
361	}
362	
363	if (mRequestText)
364	{
365		XMLRPC_Free(mRequestText);
366	}
367	
368	delete mCurlRequest;
369	mCurlRequest = NULL ;
370}
371
372bool LLXMLRPCTransaction::Impl::process()
373{
374	if(!mCurlRequest || !mCurlRequest->isValid())
375	{
376		llwarns << "transaction failed." << llendl ;
377
378		delete mCurlRequest ;
379		mCurlRequest = NULL ;
380		return true ; //failed, quit.
381	}
382
383	switch(mStatus)
384	{
385		case LLXMLRPCTransaction::StatusComplete:
386		case LLXMLRPCTransaction::StatusCURLError:
387		case LLXMLRPCTransaction::StatusXMLRPCError:
388		case LLXMLRPCTransaction::StatusOtherError:
389		{
390			return true;
391		}
392		
393		case LLXMLRPCTransaction::StatusNotStarted:
394		{
395			setStatus(LLXMLRPCTransaction::StatusStarted);
396			break;
397		}
398		
399		default:
400		{
401			// continue onward
402		}
403	}
404		
405	if(!mCurlRequest->wait())
406	{
407		return false ;
408	}
409
410	while(1)
411	{
412		CURLcode result;
413		bool newmsg = mCurlRequest->getResult(&result, &mTransferInfo);
414		if (newmsg)
415		{
416			if (result != CURLE_OK)
417			{
418				if ((result != CURLE_SSL_PEER_CERTIFICATE) &&
419					(result != CURLE_SSL_CACERT))
420				{
421					// if we have a curl error that's not already been handled
422					// (a non cert error), then generate the error message as
423					// appropriate
424					setCurlStatus(result);
425				
426					llwarns << "LLXMLRPCTransaction CURL error "
427					<< mCurlCode << ": " << mCurlRequest->getErrorString() << llendl;
428					llwarns << "LLXMLRPCTransaction request URI: "
429					<< mURI << llendl;
430				}
431					
432				return true;
433			}
434			
435			setStatus(LLXMLRPCTransaction::StatusComplete);
436
437			mResponse = XMLRPC_REQUEST_FromXML(
438					mResponseText.data(), mResponseText.size(), NULL);
439
440			bool		hasError = false;
441			bool		hasFault = false;
442			int			faultCode = 0;
443			std::string	faultString;
444
445			LLXMLRPCValue error(XMLRPC_RequestGetError(mResponse));
446			if (error.isValid())
447			{
448				hasError = true;
449				faultCode = error["faultCode"].asInt();
450				faultString = error["faultString"].asString();
451			}
452			else if (XMLRPC_ResponseIsFault(mResponse))
453			{
454				hasFault = true;
455				faultCode = XMLRPC_GetResponseFaultCode(mResponse);
456				faultString = XMLRPC_GetResponseFaultString(mResponse);
457			}
458
459			if (hasError || hasFault)
460			{
461				setStatus(LLXMLRPCTransaction::StatusXMLRPCError);
462				
463				llwarns << "LLXMLRPCTransaction XMLRPC "
464						<< (hasError ? "error " : "fault ")
465						<< faultCode << ": "
466						<< faultString << llendl;
467				llwarns << "LLXMLRPCTransaction request URI: "
468						<< mURI << llendl;
469			}
470			
471			return true;
472		}
473		else
474		{
475			break; // done
476		}
477	}
478	
479	return false;
480}
481
482void LLXMLRPCTransaction::Impl::setStatus(EStatus status,
483	const std::string& message, const std::string& uri)
484{
485	mStatus = status;
486	mStatusMessage = message;
487	mStatusURI = uri;
488
489	if (mStatusMessage.empty())
490	{
491		switch (mStatus)
492		{
493			case StatusNotStarted:
494				mStatusMessage = "(not started)";
495				break;
496				
497			case StatusStarted:
498				mStatusMessage = "(waiting for server response)";
499				break;
500				
501			case StatusDownloading:
502				mStatusMessage = "(reading server response)";
503				break;
504				
505			case StatusComplete:
506				mStatusMessage = "(done)";
507				break;
508			default:
509				// Usually this means that there's a problem with the login server,
510				// not with the client.  Direct user to status page.
511				mStatusMessage = LLTrans::getString("server_is_down");
512				mStatusURI = "http://status.secondlifegrid.net/";
513		}
514	}
515}
516
517void LLXMLRPCTransaction::Impl::setCurlStatus(CURLcode code)
518{
519	std::string message;
520	std::string uri = "http://secondlife.com/community/support.php";
521	
522	switch (code)
523	{
524		case CURLE_COULDNT_RESOLVE_HOST:
525			message =
526				"DNS could not resolve the host name.\n"
527				"Please verify that you can connect to the www.secondlife.com\n"
528				"web site.  If you can, but continue to receive this error,\n"
529				"please go to the support section and report this problem.";
530			break;
531			
532		case CURLE_SSL_PEER_CERTIFICATE:
533			message =
534				"The login server couldn't verify itself via SSL.\n"
535				"If you continue to receive this error, please go\n"
536				"to the Support section of the SecondLife.com web site\n"
537				"and report the problem.";
538			break;
539			
540		case CURLE_SSL_CACERT:
541		case CURLE_SSL_CONNECT_ERROR:
542			message =
543				"Often this means that your computer\'s clock is set incorrectly.\n"
544				"Please go to Control Panels and make sure the time and date\n"
545				"are set correctly.\n"
546				"Also check that your network and firewall are set up correctly.\n"
547				"If you continue to receive this error, please go\n"
548				"to the Support section of the SecondLife.com web site\n"
549				"and report the problem.";
550			break;
551			
552		default:
553				break;
554	}
555	
556	mCurlCode = code;
557	setStatus(StatusCURLError, message, uri);
558}
559
560size_t LLXMLRPCTransaction::Impl::curlDownloadCallback(
561		char* data, size_t size, size_t nmemb, void* user_data)
562{
563	Impl& impl(*(Impl*)user_data);
564	
565	size_t n = size * nmemb;
566
567	impl.mResponseText.append(data, n);
568	
569	if (impl.mStatus == LLXMLRPCTransaction::StatusStarted)
570	{
571		impl.setStatus(LLXMLRPCTransaction::StatusDownloading);
572	}
573	
574	return n;
575}
576
577
578LLXMLRPCTransaction::LLXMLRPCTransaction(
579	const std::string& uri, XMLRPC_REQUEST request, bool useGzip)
580: impl(* new Impl(uri, request, useGzip))
581{ }
582
583
584LLXMLRPCTransaction::LLXMLRPCTransaction(
585	const std::string& uri,
586	const std::string& method, LLXMLRPCValue params, bool useGzip)
587: impl(* new Impl(uri, method, params, useGzip))
588{ }
589
590LLXMLRPCTransaction::~LLXMLRPCTransaction()
591{
592	delete &impl;
593}
594
595bool LLXMLRPCTransaction::process()
596{
597	return impl.process();
598}
599
600LLXMLRPCTransaction::EStatus LLXMLRPCTransaction::status(int* curlCode)
601{
602	if (curlCode)
603	{
604		*curlCode = 
605			(impl.mStatus == StatusCURLError)
606				? impl.mCurlCode
607				: CURLE_OK;
608	}
609		
610	return impl.mStatus;
611}
612
613std::string LLXMLRPCTransaction::statusMessage()
614{
615	return impl.mStatusMessage;
616}
617
618LLPointer<LLCertificate> LLXMLRPCTransaction::getErrorCert()
619{
620	return impl.mErrorCert;
621}
622
623std::string LLXMLRPCTransaction::statusURI()
624{
625	return impl.mStatusURI;
626}
627
628XMLRPC_REQUEST LLXMLRPCTransaction::response()
629{
630	return impl.mResponse;
631}
632
633LLXMLRPCValue LLXMLRPCTransaction::responseValue()
634{
635	return LLXMLRPCValue(XMLRPC_RequestGetData(impl.mResponse));
636}
637
638
639F64 LLXMLRPCTransaction::transferRate()
640{
641	if (impl.mStatus != StatusComplete)
642	{
643		return 0.0L;
644	}
645	
646	double rate_bits_per_sec = impl.mTransferInfo.mSpeedDownload * 8.0;
647	
648	LL_INFOS("AppInit") << "Buffer size:   " << impl.mResponseText.size() << " B" << LL_ENDL;
649	LL_DEBUGS("AppInit") << "Transfer size: " << impl.mTransferInfo.mSizeDownload << " B" << LL_ENDL;
650	LL_DEBUGS("AppInit") << "Transfer time: " << impl.mTransferInfo.mTotalTime << " s" << LL_ENDL;
651	LL_INFOS("AppInit") << "Transfer rate: " << rate_bits_per_sec / 1000.0 << " Kb/s" << LL_ENDL;
652
653	return rate_bits_per_sec;
654}