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).
This commit is contained in:
Aleric Inglewood
2012-06-28 05:56:21 +02:00
parent 69ca6cd5b2
commit 1f56645b69
4 changed files with 179 additions and 193 deletions

View File

@@ -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())
{

View File

@@ -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);

View File

@@ -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<char*>(static_cast<void*>(&socks_auth_request)),
sizeof(socks_auth_request),
static_cast<char*>(static_cast<void*>(&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<char*>(static_cast<void*>(&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<char*>(static_cast<void*>(&connect_request)),
sizeof(connect_request),
static_cast<char*>(static_cast<void*>(&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.
*

View File

@@ -33,6 +33,7 @@
#include "llmemory.h"
#include "llsingleton.h"
#include "llthread.h"
#include "aithreadsafe.h"
#include <string>
// 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<LLProxy>
{
LOG_CLASS(LLProxy);
public:
typedef AISTAccessConst<ProxyUnshared> Unshared_crat; // Constant Read Access Type for Unshared (cannot be converted to write access).
typedef AISTAccess<ProxyUnshared> Unshared_rat; // Read Access Type for Unshared (same as write access type, since we don't lock at all).
typedef AISTAccess<ProxyUnshared> Unshared_wat; // Write Access Type, for Unshared.
typedef AIReadAccessConst<ProxyShared> Shared_crat; // Constant Read Access Type for Shared (cannot be converted to write access).
typedef AIReadAccess<ProxyShared> Shared_rat; // Read Access Type for Shared.
typedef AIWriteAccess<ProxyShared> 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<bool> 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<ProxyUnshared> mUnshared;
AIThreadSafeDC<ProxyShared> 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<ProxyUnshared> const& unshared_lockobj(void) const { return mUnshared; }
AIThreadSafeDC<ProxyShared> const& shared_lockobj(void) const { return mShared; }
};
#endif