From 1f56645b69b853024e77234bd3d5d13785280970 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 28 Jun 2012 05:56:21 +0200 Subject: [PATCH] Always set proxy settings for every HTTP curl connection. Move applyProxySettings to CurlEasyRequest and call it from applyDefaultOptions. Use AIThreadSafe for LLProxy for a more robust threadsafeness. (This forces correct locking, checks that the unshared vars are indeed unshared and made it easy to use read/write locking, which might be important in this case (we do a lot of read-only accesses to it). --- indra/llmessage/aicurl.cpp | 35 +++++- indra/llmessage/aicurlprivate.h | 4 + indra/llmessage/llproxy.cpp | 181 +++++++++----------------------- indra/llmessage/llproxy.h | 152 ++++++++++++++++----------- 4 files changed, 179 insertions(+), 193 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 6278c91c2..3ab86f56f 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -805,6 +805,39 @@ static int curl_debug_callback(CURL*, curl_infotype infotype, char* buf, size_t } #endif +void CurlEasyRequest::applyProxySettings(void) +{ + LLProxy& proxy = *LLProxy::getInstance(); + + // Do a faster unlocked check to see if we are supposed to proxy. + if (proxy.HTTPProxyEnabled()) + { + // We think we should proxy, read lock the shared proxy members. + LLProxy::Shared_crat proxy_r(proxy.shared_lockobj()); + + // Now test again to verify that the proxy wasn't disabled between the first check and the lock. + if (proxy.HTTPProxyEnabled()) + { + setopt(CURLOPT_PROXY, proxy.getHTTPProxy(proxy_r).getIPString().c_str()); + setopt(CURLOPT_PROXYPORT, proxy.getHTTPProxy(proxy_r).getPort()); + + if (proxy.getHTTPProxyType(proxy_r) == LLPROXY_SOCKS) + { + setopt(CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); + if (proxy.getSelectedAuthMethod(proxy_r) == METHOD_PASSWORD) + { + std::string auth_string = proxy.getSocksUser(proxy_r) + ":" + proxy.getSocksPwd(proxy_r); + setopt(CURLOPT_PROXYUSERPWD, auth_string.c_str()); + } + } + else + { + setopt(CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + } + } + } +} + void CurlEasyRequest::applyDefaultOptions(void) { CertificateAuthority_rat CertificateAuthority_r(gCertificateAuthority); @@ -822,7 +855,7 @@ void CurlEasyRequest::applyDefaultOptions(void) // The old code did this for the 'buffered' version, but I think it's nonsense. //setopt(CURLOPT_DNS_CACHE_TIMEOUT, 0); // Set the CURL options for either Socks or HTTP proxy. - LLProxy::getInstance()->applyProxySettings(this); + applyProxySettings(); Debug( if (dc::curl.is_on()) { diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index 9a7f50180..fcf51e927 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -176,6 +176,10 @@ class CurlEasyRequest : public CurlEasyHandle { // Reset everything to the state it was in when this object was just created. void resetState(void); + private: + // Called from applyDefaultOptions. + void applyProxySettings(void); + public: // Set default options that we want applied to all curl easy handles. void applyDefaultOptions(void); diff --git a/indra/llmessage/llproxy.cpp b/indra/llmessage/llproxy.cpp index 3db851001..a82ae3cc7 100644 --- a/indra/llmessage/llproxy.cpp +++ b/indra/llmessage/llproxy.cpp @@ -47,23 +47,22 @@ static apr_status_t tcp_blocking_handshake(LLSocket::ptr_t handle, char * dataou static LLSocket::ptr_t tcp_open_channel(LLHost host); // Open a TCP channel to a given host static void tcp_close_channel(LLSocket::ptr_t* handle_ptr); // Close an open TCP channel -LLProxy::LLProxy(): - mHTTPProxyEnabled(false), - mProxyMutex(), - mUDPProxy(), - mTCPProxy(), - mHTTPProxy(), +ProxyShared::ProxyShared(void): mProxyType(LLPROXY_SOCKS), - mAuthMethodSelected(METHOD_NOAUTH), - mSocksUsername(), - mSocksPassword() + mAuthMethodSelected(METHOD_NOAUTH) +{ +} + +LLProxy::LLProxy(): + mHTTPProxyEnabled(false) { } LLProxy::~LLProxy() { stopSOCKSProxy(); - disableHTTPProxy(); + Shared_wat shared_w(mShared); + disableHTTPProxy(shared_w); } /** @@ -78,15 +77,18 @@ S32 LLProxy::proxyHandshake(LLHost proxy) { S32 result; + Unshared_rat unshared_r(mUnshared); + Shared_rat shared_r(mShared); + /* SOCKS 5 Auth request */ socks_auth_request_t socks_auth_request; socks_auth_response_t socks_auth_response; socks_auth_request.version = SOCKS_VERSION; // SOCKS version 5 socks_auth_request.num_methods = 1; // Sending 1 method. - socks_auth_request.methods = getSelectedAuthMethod(); // Send only the selected method. + socks_auth_request.methods = getSelectedAuthMethod(shared_r); // Send only the selected method. - result = tcp_blocking_handshake(mProxyControlChannel, + result = tcp_blocking_handshake(unshared_r->mProxyControlChannel, static_cast(static_cast(&socks_auth_request)), sizeof(socks_auth_request), static_cast(static_cast(&socks_auth_response)), @@ -109,8 +111,8 @@ S32 LLProxy::proxyHandshake(LLHost proxy) if (socks_auth_response.method == METHOD_PASSWORD) { // The server has requested a username/password combination - std::string socks_username(getSocksUser()); - std::string socks_password(getSocksPwd()); + std::string socks_username(getSocksUser(shared_r)); + std::string socks_password(getSocksPwd(shared_r)); U32 request_size = socks_username.size() + socks_password.size() + 3; char * password_auth = new char[request_size]; password_auth[0] = 0x01; @@ -121,7 +123,7 @@ S32 LLProxy::proxyHandshake(LLHost proxy) authmethod_password_reply_t password_reply; - result = tcp_blocking_handshake(mProxyControlChannel, + result = tcp_blocking_handshake(unshared_r->mProxyControlChannel, password_auth, request_size, static_cast(static_cast(&password_reply)), @@ -157,7 +159,7 @@ S32 LLProxy::proxyHandshake(LLHost proxy) // "If the client is not in possession of the information at the time of the UDP ASSOCIATE, // the client MUST use a port number and address of all zeros. RFC 1928" - result = tcp_blocking_handshake(mProxyControlChannel, + result = tcp_blocking_handshake(unshared_r->mProxyControlChannel, static_cast(static_cast(&connect_request)), sizeof(connect_request), static_cast(static_cast(&connect_reply)), @@ -176,10 +178,14 @@ S32 LLProxy::proxyHandshake(LLHost proxy) return SOCKS_UDP_FWD_NOT_GRANTED; } - mUDPProxy.setPort(ntohs(connect_reply.port)); // reply port is in network byte order - mUDPProxy.setAddress(proxy.getAddress()); + { + // Write acccess type and read access type are really the same, so unshared_w must be simply a reference. + Unshared_wat& unshared_w = unshared_r; + unshared_w->mUDPProxy.setPort(ntohs(connect_reply.port)); // reply port is in network byte order + unshared_w->mUDPProxy.setAddress(proxy.getAddress()); + } // The connection was successful. We now have the UDP port to send requests that need forwarding to. - LL_INFOS("Proxy") << "SOCKS 5 UDP proxy connected on " << mUDPProxy << LL_ENDL; + LL_INFOS("Proxy") << "SOCKS 5 UDP proxy connected on " << unshared_r->mUDPProxy << LL_ENDL; return SOCKS_OK; } @@ -197,9 +203,11 @@ S32 LLProxy::proxyHandshake(LLHost proxy) */ S32 LLProxy::startSOCKSProxy(LLHost host) { + Unshared_wat unshared_w(mUnshared); + if (host.isOk()) { - mTCPProxy = host; + unshared_w->mTCPProxy = host; } else { @@ -209,13 +217,13 @@ S32 LLProxy::startSOCKSProxy(LLHost host) // Close any running SOCKS connection. stopSOCKSProxy(); - mProxyControlChannel = tcp_open_channel(mTCPProxy); - if (!mProxyControlChannel) + unshared_w->mProxyControlChannel = tcp_open_channel(unshared_w->mTCPProxy); + if (!unshared_w->mProxyControlChannel) { return SOCKS_HOST_CONNECT_FAILED; } - S32 status = proxyHandshake(mTCPProxy); + S32 status = proxyHandshake(unshared_w->mTCPProxy); if (status != SOCKS_OK) { @@ -246,14 +254,16 @@ void LLProxy::stopSOCKSProxy() // then we must shut down any HTTP proxy operations. But it is allowable if web // proxy is being used to continue proxying HTTP. - if (LLPROXY_SOCKS == getHTTPProxyType()) + Shared_rat shared_r(mShared); + if (LLPROXY_SOCKS == getHTTPProxyType(shared_r)) { - disableHTTPProxy(); + Shared_wat shared_w(shared_r); + disableHTTPProxy(shared_w); } - - if (mProxyControlChannel) + Unshared_wat unshared_w(mUnshared); + if (unshared_w->mProxyControlChannel) { - tcp_close_channel(&mProxyControlChannel); + tcp_close_channel(&unshared_w->mProxyControlChannel); } } @@ -262,9 +272,7 @@ void LLProxy::stopSOCKSProxy() */ void LLProxy::setAuthNone() { - LLMutexLock lock(&mProxyMutex); - - mAuthMethodSelected = METHOD_NOAUTH; + Shared_wat(mShared)->mAuthMethodSelected = METHOD_NOAUTH; } /** @@ -288,11 +296,10 @@ bool LLProxy::setAuthPassword(const std::string &username, const std::string &pa return false; } - LLMutexLock lock(&mProxyMutex); - - mAuthMethodSelected = METHOD_PASSWORD; - mSocksUsername = username; - mSocksPassword = password; + Shared_wat shared_w(mShared); + shared_w->mAuthMethodSelected = METHOD_PASSWORD; + shared_w->mSocksUsername = username; + shared_w->mSocksPassword = password; return true; } @@ -314,12 +321,10 @@ bool LLProxy::enableHTTPProxy(LLHost httpHost, LLHttpProxyType type) return false; } - LLMutexLock lock(&mProxyMutex); - - mHTTPProxy = httpHost; - mProxyType = type; - + Shared_wat shared_w(mShared); mHTTPProxyEnabled = true; + shared_w->mHTTPProxy = httpHost; + shared_w->mProxyType = type; return true; } @@ -335,9 +340,8 @@ bool LLProxy::enableHTTPProxy() { bool ok; - LLMutexLock lock(&mProxyMutex); - - ok = (mHTTPProxy.isOk()); + Shared_rat shared_r(mShared); + ok = (shared_r->mHTTPProxy.isOk()); if (ok) { mHTTPProxyEnabled = true; @@ -346,54 +350,6 @@ bool LLProxy::enableHTTPProxy() return ok; } -/** - * @brief Disable the HTTP proxy. - */ -void LLProxy::disableHTTPProxy() -{ - LLMutexLock lock(&mProxyMutex); - - mHTTPProxyEnabled = false; -} - -/** - * @brief Get the currently selected HTTP proxy type - */ -LLHttpProxyType LLProxy::getHTTPProxyType() const -{ - LLMutexLock lock(&mProxyMutex); - return mProxyType; -} - -/** - * @brief Get the SOCKS 5 password. - */ -std::string LLProxy::getSocksPwd() const -{ - LLMutexLock lock(&mProxyMutex); - return mSocksPassword; -} - -/** - * @brief Get the SOCKS 5 username. - */ -std::string LLProxy::getSocksUser() const -{ - LLMutexLock lock(&mProxyMutex); - return mSocksUsername; -} - -/** - * @brief Get the currently selected SOCKS 5 authentication method. - * - * @return Returns either none or password. - */ -LLSocks5AuthType LLProxy::getSelectedAuthMethod() const -{ - LLMutexLock lock(&mProxyMutex); - return mAuthMethodSelected; -} - /** * @brief Stop the LLProxy and make certain that any APR pools and classes are deleted before terminating APR. * @@ -406,47 +362,6 @@ void LLProxy::cleanupClass() deleteSingleton(); } -/** - * @brief Apply proxy settings to a CuRL request if an HTTP proxy is enabled. - * - * This method has been designed to be safe to call from - * any thread in the viewer. This allows requests in the - * texture fetch thread to be aware of the proxy settings. - * When the HTTP proxy is enabled, the proxy mutex will - * be locked every time this method is called. - * - * @param curlEasyRequest_w An already locked curl easy handle, before it has been performed. - */ -void LLProxy::applyProxySettings(AICurlEasyRequest_wat const& curlEasyRequest_w) -{ - // Do a faster unlocked check to see if we are supposed to proxy. - if (mHTTPProxyEnabled) - { - // We think we should proxy, lock the proxy mutex. - LLMutexLock lock(&mProxyMutex); - // Now test again to verify that the proxy wasn't disabled between the first check and the lock. - if (mHTTPProxyEnabled) - { - curlEasyRequest_w->setopt(CURLOPT_PROXY, mHTTPProxy.getIPString().c_str()); - curlEasyRequest_w->setopt(CURLOPT_PROXYPORT, mHTTPProxy.getPort()); - - if (mProxyType == LLPROXY_SOCKS) - { - curlEasyRequest_w->setopt(CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); - if (mAuthMethodSelected == METHOD_PASSWORD) - { - std::string auth_string = mSocksUsername + ":" + mSocksPassword; - curlEasyRequest_w->setopt(CURLOPT_PROXYUSERPWD, auth_string.c_str()); - } - } - else - { - curlEasyRequest_w->setopt(CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - } - } - } -} - /** * @brief Send one TCP packet and receive one in return. * diff --git a/indra/llmessage/llproxy.h b/indra/llmessage/llproxy.h index 94f031fc3..c0d6ab183 100644 --- a/indra/llmessage/llproxy.h +++ b/indra/llmessage/llproxy.h @@ -33,6 +33,7 @@ #include "llmemory.h" #include "llsingleton.h" #include "llthread.h" +#include "aithreadsafe.h" #include // SOCKS error codes returned from the StartProxy method @@ -211,28 +212,87 @@ enum LLSocks5AuthType * To ensure thread safety, all LLProxy members that relate to the HTTP * proxy require the LLProxyMutex to be locked before accessing. */ + +struct ProxyUnshared +{ + /*########################################################################################### + MEMBERS READ AND WRITTEN ONLY IN THE MAIN THREAD. + ###########################################################################################*/ + + // UDP proxy address and port + LLHost mUDPProxy; + + // TCP proxy control channel address and port + LLHost mTCPProxy; + + // socket handle to proxy TCP control channel + LLSocket::ptr_t mProxyControlChannel; + + /*########################################################################################### + END OF UNSHARED MEMBERS + ###########################################################################################*/ +}; + +struct ProxyShared +{ + ProxyShared(void); + + /*########################################################################################### + MEMBERS WRITTEN IN MAIN THREAD AND READ IN ANY THREAD. + ###########################################################################################*/ + + // HTTP proxy address and port + LLHost mHTTPProxy; + + // Currently selected HTTP proxy type. Can be web or socks. + LLHttpProxyType mProxyType; + + // SOCKS 5 selected authentication method. + LLSocks5AuthType mAuthMethodSelected; + + // SOCKS 5 username + std::string mSocksUsername; + // SOCKS 5 password + std::string mSocksPassword; + + /*########################################################################################### + END OF SHARED MEMBERS + ###########################################################################################*/ +}; + class LLProxy: public LLSingleton { LOG_CLASS(LLProxy); + public: + typedef AISTAccessConst Unshared_crat; // Constant Read Access Type for Unshared (cannot be converted to write access). + typedef AISTAccess Unshared_rat; // Read Access Type for Unshared (same as write access type, since we don't lock at all). + typedef AISTAccess Unshared_wat; // Write Access Type, for Unshared. + typedef AIReadAccessConst Shared_crat; // Constant Read Access Type for Shared (cannot be converted to write access). + typedef AIReadAccess Shared_rat; // Read Access Type for Shared. + typedef AIWriteAccess Shared_wat; // Write Access Type for Shared. + /*########################################################################################### - METHODS THAT DO NOT LOCK mProxyMutex! + Public methods that only access variables not shared between threads. ###########################################################################################*/ // Constructor, cannot have parameters due to LLSingleton parent class. Call from main thread only. LLProxy(); - // Static check for enabled status for UDP packets. Call from main thread only. - static bool isSOCKSProxyEnabled() { return sUDPProxyEnabled; } + // Static check for enabled status for UDP packets. Called from main thread only. + static bool isSOCKSProxyEnabled(void) { llassert(is_main_thread()); return sUDPProxyEnabled; } - // Get the UDP proxy address and port. Call from main thread only. - LLHost getUDPProxy() const { return mUDPProxy; } + // Get the UDP proxy address and port. Called from main thread only. + LLHost getUDPProxy(void) const { return Unshared_crat(mUnshared)->mUDPProxy; } /*########################################################################################### - END OF NON-LOCKING METHODS + End of methods that only access variables not shared between threads. ###########################################################################################*/ + // Return true if there is a good chance that the HTTP proxy is currently enabled. + bool HTTPProxyEnabled(void) const { return mHTTPProxyEnabled; } + /*########################################################################################### - METHODS THAT LOCK mProxyMutex! DO NOT CALL WHILE mProxyMutex IS LOCKED! + Public methods that access variables shared between threads. ###########################################################################################*/ // Destructor, closes open connections. Do not call directly, use cleanupClass(). ~LLProxy(); @@ -263,30 +323,37 @@ public: bool enableHTTPProxy(); // Stop proxying HTTP packets. Call from main thread only. - void disableHTTPProxy(); + // Note that this needs shared_w to be passed because we want the shared members to be locked when this is reset to false. + void disableHTTPProxy(Shared_wat const& shared_w) { mHTTPProxyEnabled = false; } + void disableHTTPProxy(void) { disableHTTPProxy(Shared_wat(mShared)); } + + // Get the currently selected HTTP proxy address and port + LLHost const& getHTTPProxy(Shared_crat const& shared_r) const { return shared_r->mHTTPProxy; } + + // Get the currently selected HTTP proxy type + LLHttpProxyType getHTTPProxyType(Shared_crat const& shared_r) const { return shared_r->mProxyType; } + + // Get the currently selected auth method. + LLSocks5AuthType getSelectedAuthMethod(Shared_crat const& shared_r) const { return shared_r->mAuthMethodSelected; } + + // SOCKS 5 username and password accessors. + std::string getSocksUser(Shared_crat const& shared_r) const { return shared_r->mSocksUsername; } + std::string getSocksPwd(Shared_crat const& shared_r) const { return shared_r->mSocksPassword; } /*########################################################################################### - END OF LOCKING METHODS + End of methods that access variables shared between threads. ###########################################################################################*/ + private: /*########################################################################################### - METHODS THAT LOCK mProxyMutex! DO NOT CALL WHILE mProxyMutex IS LOCKED! + Private methods that access variables shared between threads. ###########################################################################################*/ // Perform a SOCKS 5 authentication and UDP association with the proxy server. S32 proxyHandshake(LLHost proxy); - // Get the currently selected auth method. - LLSocks5AuthType getSelectedAuthMethod() const; - - // Get the currently selected HTTP proxy type - LLHttpProxyType getHTTPProxyType() const; - - std::string getSocksPwd() const; - std::string getSocksUser() const; - /*########################################################################################### - END OF LOCKING METHODS + End of methods that access variables shared between threads. ###########################################################################################*/ private: @@ -294,49 +361,16 @@ private: // Instead use enableHTTPProxy() and disableHTTPProxy() instead. mutable LLAtomic32 mHTTPProxyEnabled; - // Mutex to protect shared members in non-main thread calls to applyProxySettings(). - mutable LLMutex mProxyMutex; - - /*########################################################################################### - MEMBERS READ AND WRITTEN ONLY IN THE MAIN THREAD. DO NOT SHARE! - ###########################################################################################*/ - // Is the UDP proxy enabled? static bool sUDPProxyEnabled; - // UDP proxy address and port - LLHost mUDPProxy; - // TCP proxy control channel address and port - LLHost mTCPProxy; + AIThreadSafeSingleThreadDC mUnshared; + AIThreadSafeDC mShared; - // socket handle to proxy TCP control channel - LLSocket::ptr_t mProxyControlChannel; - - /*########################################################################################### - END OF UNSHARED MEMBERS - ###########################################################################################*/ - - /*########################################################################################### - MEMBERS WRITTEN IN MAIN THREAD AND READ IN ANY THREAD. ONLY READ OR WRITE AFTER LOCKING mProxyMutex! - ###########################################################################################*/ - - // HTTP proxy address and port - LLHost mHTTPProxy; - - // Currently selected HTTP proxy type. Can be web or socks. - LLHttpProxyType mProxyType; - - // SOCKS 5 selected authentication method. - LLSocks5AuthType mAuthMethodSelected; - - // SOCKS 5 username - std::string mSocksUsername; - // SOCKS 5 password - std::string mSocksPassword; - - /*########################################################################################### - END OF SHARED MEMBERS - ###########################################################################################*/ +public: + // For thread-safe read access. Use the _crat access types with these. + AIThreadSafeSingleThreadDC const& unshared_lockobj(void) const { return mUnshared; } + AIThreadSafeDC const& shared_lockobj(void) const { return mShared; } }; #endif