/indra/llmessage/llproxy.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 546 lines · 311 code · 72 blank · 163 comment · 44 complexity · 5a5d8e21245f26b6d90a05ad5eec6a45 MD5 · raw file

  1. /**
  2. * @file llproxy.cpp
  3. * @brief UDP and HTTP proxy communications
  4. *
  5. * $LicenseInfo:firstyear=2011&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2011, 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. #include "linden_common.h"
  27. #include "llproxy.h"
  28. #include <string>
  29. #include <curl/curl.h>
  30. #include "llapr.h"
  31. #include "llcurl.h"
  32. #include "llhost.h"
  33. // Static class variable instances
  34. // We want this to be static to avoid excessive indirection on every
  35. // incoming packet just to do a simple bool test. The getter for this
  36. // member is also static
  37. bool LLProxy::sUDPProxyEnabled = false;
  38. // Some helpful TCP static functions.
  39. static apr_status_t tcp_blocking_handshake(LLSocket::ptr_t handle, char * dataout, apr_size_t outlen, char * datain, apr_size_t maxinlen); // Do a TCP data handshake
  40. static LLSocket::ptr_t tcp_open_channel(LLHost host); // Open a TCP channel to a given host
  41. static void tcp_close_channel(LLSocket::ptr_t* handle_ptr); // Close an open TCP channel
  42. LLProxy::LLProxy():
  43. mHTTPProxyEnabled(false),
  44. mProxyMutex(NULL),
  45. mUDPProxy(),
  46. mTCPProxy(),
  47. mHTTPProxy(),
  48. mProxyType(LLPROXY_SOCKS),
  49. mAuthMethodSelected(METHOD_NOAUTH),
  50. mSocksUsername(),
  51. mSocksPassword()
  52. {
  53. }
  54. LLProxy::~LLProxy()
  55. {
  56. stopSOCKSProxy();
  57. disableHTTPProxy();
  58. }
  59. /**
  60. * @brief Open the SOCKS 5 TCP control channel.
  61. *
  62. * Perform a SOCKS 5 authentication and UDP association with the proxy server.
  63. *
  64. * @param proxy The SOCKS 5 server to connect to.
  65. * @return SOCKS_OK if successful, otherwise a socks error code from llproxy.h.
  66. */
  67. S32 LLProxy::proxyHandshake(LLHost proxy)
  68. {
  69. S32 result;
  70. /* SOCKS 5 Auth request */
  71. socks_auth_request_t socks_auth_request;
  72. socks_auth_response_t socks_auth_response;
  73. socks_auth_request.version = SOCKS_VERSION; // SOCKS version 5
  74. socks_auth_request.num_methods = 1; // Sending 1 method.
  75. socks_auth_request.methods = getSelectedAuthMethod(); // Send only the selected method.
  76. result = tcp_blocking_handshake(mProxyControlChannel,
  77. static_cast<char*>(static_cast<void*>(&socks_auth_request)),
  78. sizeof(socks_auth_request),
  79. static_cast<char*>(static_cast<void*>(&socks_auth_response)),
  80. sizeof(socks_auth_response));
  81. if (result != APR_SUCCESS)
  82. {
  83. LL_WARNS("Proxy") << "SOCKS authentication request failed, error on TCP control channel : " << result << LL_ENDL;
  84. stopSOCKSProxy();
  85. return SOCKS_CONNECT_ERROR;
  86. }
  87. if (socks_auth_response.method == AUTH_NOT_ACCEPTABLE)
  88. {
  89. LL_WARNS("Proxy") << "SOCKS 5 server refused all our authentication methods." << LL_ENDL;
  90. stopSOCKSProxy();
  91. return SOCKS_NOT_ACCEPTABLE;
  92. }
  93. /* SOCKS 5 USERNAME/PASSWORD authentication */
  94. if (socks_auth_response.method == METHOD_PASSWORD)
  95. {
  96. // The server has requested a username/password combination
  97. std::string socks_username(getSocksUser());
  98. std::string socks_password(getSocksPwd());
  99. U32 request_size = socks_username.size() + socks_password.size() + 3;
  100. char * password_auth = new char[request_size];
  101. password_auth[0] = 0x01;
  102. password_auth[1] = socks_username.size();
  103. memcpy(&password_auth[2], socks_username.c_str(), socks_username.size());
  104. password_auth[socks_username.size() + 2] = socks_password.size();
  105. memcpy(&password_auth[socks_username.size() + 3], socks_password.c_str(), socks_password.size());
  106. authmethod_password_reply_t password_reply;
  107. result = tcp_blocking_handshake(mProxyControlChannel,
  108. password_auth,
  109. request_size,
  110. static_cast<char*>(static_cast<void*>(&password_reply)),
  111. sizeof(password_reply));
  112. delete[] password_auth;
  113. if (result != APR_SUCCESS)
  114. {
  115. LL_WARNS("Proxy") << "SOCKS authentication failed, error on TCP control channel : " << result << LL_ENDL;
  116. stopSOCKSProxy();
  117. return SOCKS_CONNECT_ERROR;
  118. }
  119. if (password_reply.status != AUTH_SUCCESS)
  120. {
  121. LL_WARNS("Proxy") << "SOCKS authentication failed" << LL_ENDL;
  122. stopSOCKSProxy();
  123. return SOCKS_AUTH_FAIL;
  124. }
  125. }
  126. /* SOCKS5 connect request */
  127. socks_command_request_t connect_request;
  128. socks_command_response_t connect_reply;
  129. connect_request.version = SOCKS_VERSION; // SOCKS V5
  130. connect_request.command = COMMAND_UDP_ASSOCIATE; // Associate UDP
  131. connect_request.reserved = FIELD_RESERVED;
  132. connect_request.atype = ADDRESS_IPV4;
  133. connect_request.address = htonl(0); // 0.0.0.0
  134. connect_request.port = htons(0); // 0
  135. // "If the client is not in possession of the information at the time of the UDP ASSOCIATE,
  136. // the client MUST use a port number and address of all zeros. RFC 1928"
  137. result = tcp_blocking_handshake(mProxyControlChannel,
  138. static_cast<char*>(static_cast<void*>(&connect_request)),
  139. sizeof(connect_request),
  140. static_cast<char*>(static_cast<void*>(&connect_reply)),
  141. sizeof(connect_reply));
  142. if (result != APR_SUCCESS)
  143. {
  144. LL_WARNS("Proxy") << "SOCKS connect request failed, error on TCP control channel : " << result << LL_ENDL;
  145. stopSOCKSProxy();
  146. return SOCKS_CONNECT_ERROR;
  147. }
  148. if (connect_reply.reply != REPLY_REQUEST_GRANTED)
  149. {
  150. LL_WARNS("Proxy") << "Connection to SOCKS 5 server failed, UDP forward request not granted" << LL_ENDL;
  151. stopSOCKSProxy();
  152. return SOCKS_UDP_FWD_NOT_GRANTED;
  153. }
  154. mUDPProxy.setPort(ntohs(connect_reply.port)); // reply port is in network byte order
  155. mUDPProxy.setAddress(proxy.getAddress());
  156. // The connection was successful. We now have the UDP port to send requests that need forwarding to.
  157. LL_INFOS("Proxy") << "SOCKS 5 UDP proxy connected on " << mUDPProxy << LL_ENDL;
  158. return SOCKS_OK;
  159. }
  160. /**
  161. * @brief Initiates a SOCKS 5 proxy session.
  162. *
  163. * Performs basic checks on host to verify that it is a valid address. Opens the control channel
  164. * and then negotiates the proxy connection with the server. Closes any existing SOCKS
  165. * connection before proceeding. Also disables an HTTP proxy if it is using SOCKS as the proxy.
  166. *
  167. *
  168. * @param host Socks server to connect to.
  169. * @return SOCKS_OK if successful, otherwise a SOCKS error code defined in llproxy.h.
  170. */
  171. S32 LLProxy::startSOCKSProxy(LLHost host)
  172. {
  173. if (host.isOk())
  174. {
  175. mTCPProxy = host;
  176. }
  177. else
  178. {
  179. return SOCKS_INVALID_HOST;
  180. }
  181. // Close any running SOCKS connection.
  182. stopSOCKSProxy();
  183. mProxyControlChannel = tcp_open_channel(mTCPProxy);
  184. if (!mProxyControlChannel)
  185. {
  186. return SOCKS_HOST_CONNECT_FAILED;
  187. }
  188. S32 status = proxyHandshake(mTCPProxy);
  189. if (status != SOCKS_OK)
  190. {
  191. // Shut down the proxy if any of the above steps failed.
  192. stopSOCKSProxy();
  193. }
  194. else
  195. {
  196. // Connection was successful.
  197. sUDPProxyEnabled = true;
  198. }
  199. return status;
  200. }
  201. /**
  202. * @brief Stop using the SOCKS 5 proxy.
  203. *
  204. * This will stop sending UDP packets through the SOCKS 5 proxy
  205. * and will also stop the HTTP proxy if it is configured to use SOCKS.
  206. * The proxy control channel will also be disconnected.
  207. */
  208. void LLProxy::stopSOCKSProxy()
  209. {
  210. sUDPProxyEnabled = false;
  211. // If the SOCKS proxy is requested to stop and we are using that for HTTP as well
  212. // then we must shut down any HTTP proxy operations. But it is allowable if web
  213. // proxy is being used to continue proxying HTTP.
  214. if (LLPROXY_SOCKS == getHTTPProxyType())
  215. {
  216. disableHTTPProxy();
  217. }
  218. if (mProxyControlChannel)
  219. {
  220. tcp_close_channel(&mProxyControlChannel);
  221. }
  222. }
  223. /**
  224. * @brief Set the proxy's SOCKS authentication method to none.
  225. */
  226. void LLProxy::setAuthNone()
  227. {
  228. LLMutexLock lock(&mProxyMutex);
  229. mAuthMethodSelected = METHOD_NOAUTH;
  230. }
  231. /**
  232. * @brief Set the proxy's SOCKS authentication method to password.
  233. *
  234. * Check whether the lengths of the supplied username
  235. * and password conform to the lengths allowed by the
  236. * SOCKS protocol.
  237. *
  238. * @param username The SOCKS username to send.
  239. * @param password The SOCKS password to send.
  240. * @return Return true if applying the settings was successful. No changes are made if false.
  241. *
  242. */
  243. bool LLProxy::setAuthPassword(const std::string &username, const std::string &password)
  244. {
  245. if (username.length() > SOCKSMAXUSERNAMELEN || password.length() > SOCKSMAXPASSWORDLEN ||
  246. username.length() < SOCKSMINUSERNAMELEN || password.length() < SOCKSMINPASSWORDLEN)
  247. {
  248. LL_WARNS("Proxy") << "Invalid SOCKS 5 password or username length." << LL_ENDL;
  249. return false;
  250. }
  251. LLMutexLock lock(&mProxyMutex);
  252. mAuthMethodSelected = METHOD_PASSWORD;
  253. mSocksUsername = username;
  254. mSocksPassword = password;
  255. return true;
  256. }
  257. /**
  258. * @brief Enable the HTTP proxy for either SOCKS or HTTP.
  259. *
  260. * Check the supplied host to see if it is a valid IP and port.
  261. *
  262. * @param httpHost Proxy server to connect to.
  263. * @param type Is the host a SOCKS or HTTP proxy.
  264. * @return Return true if applying the setting was successful. No changes are made if false.
  265. */
  266. bool LLProxy::enableHTTPProxy(LLHost httpHost, LLHttpProxyType type)
  267. {
  268. if (!httpHost.isOk())
  269. {
  270. LL_WARNS("Proxy") << "Invalid SOCKS 5 Server" << LL_ENDL;
  271. return false;
  272. }
  273. LLMutexLock lock(&mProxyMutex);
  274. mHTTPProxy = httpHost;
  275. mProxyType = type;
  276. mHTTPProxyEnabled = true;
  277. return true;
  278. }
  279. /**
  280. * @brief Enable the HTTP proxy without changing the proxy settings.
  281. *
  282. * This should not be called unless the proxy has already been set up.
  283. *
  284. * @return Return true only if the current settings are valid and the proxy was enabled.
  285. */
  286. bool LLProxy::enableHTTPProxy()
  287. {
  288. bool ok;
  289. LLMutexLock lock(&mProxyMutex);
  290. ok = (mHTTPProxy.isOk());
  291. if (ok)
  292. {
  293. mHTTPProxyEnabled = true;
  294. }
  295. return ok;
  296. }
  297. /**
  298. * @brief Disable the HTTP proxy.
  299. */
  300. void LLProxy::disableHTTPProxy()
  301. {
  302. LLMutexLock lock(&mProxyMutex);
  303. mHTTPProxyEnabled = false;
  304. }
  305. /**
  306. * @brief Get the currently selected HTTP proxy type
  307. */
  308. LLHttpProxyType LLProxy::getHTTPProxyType() const
  309. {
  310. LLMutexLock lock(&mProxyMutex);
  311. return mProxyType;
  312. }
  313. /**
  314. * @brief Get the SOCKS 5 password.
  315. */
  316. std::string LLProxy::getSocksPwd() const
  317. {
  318. LLMutexLock lock(&mProxyMutex);
  319. return mSocksPassword;
  320. }
  321. /**
  322. * @brief Get the SOCKS 5 username.
  323. */
  324. std::string LLProxy::getSocksUser() const
  325. {
  326. LLMutexLock lock(&mProxyMutex);
  327. return mSocksUsername;
  328. }
  329. /**
  330. * @brief Get the currently selected SOCKS 5 authentication method.
  331. *
  332. * @return Returns either none or password.
  333. */
  334. LLSocks5AuthType LLProxy::getSelectedAuthMethod() const
  335. {
  336. LLMutexLock lock(&mProxyMutex);
  337. return mAuthMethodSelected;
  338. }
  339. /**
  340. * @brief Stop the LLProxy and make certain that any APR pools and classes are deleted before terminating APR.
  341. *
  342. * Deletes the LLProxy singleton, destroying the APR pool used by the control channel as well as .
  343. */
  344. //static
  345. void LLProxy::cleanupClass()
  346. {
  347. getInstance()->stopSOCKSProxy();
  348. deleteSingleton();
  349. }
  350. void LLProxy::applyProxySettings(LLCurlEasyRequest* handle)
  351. {
  352. applyProxySettings(handle->getEasy());
  353. }
  354. void LLProxy::applyProxySettings(LLCurl::Easy* handle)
  355. {
  356. applyProxySettings(handle->getCurlHandle());
  357. }
  358. /**
  359. * @brief Apply proxy settings to a CuRL request if an HTTP proxy is enabled.
  360. *
  361. * This method has been designed to be safe to call from
  362. * any thread in the viewer. This allows requests in the
  363. * texture fetch thread to be aware of the proxy settings.
  364. * When the HTTP proxy is enabled, the proxy mutex will
  365. * be locked every time this method is called.
  366. *
  367. * @param handle A pointer to a valid CURL request, before it has been performed.
  368. */
  369. void LLProxy::applyProxySettings(CURL* handle)
  370. {
  371. // Do a faster unlocked check to see if we are supposed to proxy.
  372. if (mHTTPProxyEnabled)
  373. {
  374. // We think we should proxy, lock the proxy mutex.
  375. LLMutexLock lock(&mProxyMutex);
  376. // Now test again to verify that the proxy wasn't disabled between the first check and the lock.
  377. if (mHTTPProxyEnabled)
  378. {
  379. LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXY, mHTTPProxy.getIPString().c_str()));
  380. LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYPORT, mHTTPProxy.getPort()));
  381. if (mProxyType == LLPROXY_SOCKS)
  382. {
  383. LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5));
  384. if (mAuthMethodSelected == METHOD_PASSWORD)
  385. {
  386. std::string auth_string = mSocksUsername + ":" + mSocksPassword;
  387. LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, auth_string.c_str()));
  388. }
  389. }
  390. else
  391. {
  392. LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP));
  393. }
  394. }
  395. }
  396. }
  397. /**
  398. * @brief Send one TCP packet and receive one in return.
  399. *
  400. * This operation is done synchronously with a 1000ms timeout. Therefore, it should not be used when a blocking
  401. * operation would impact the operation of the viewer.
  402. *
  403. * @param handle_ptr Pointer to a connected LLSocket of type STREAM_TCP.
  404. * @param dataout Data to send.
  405. * @param outlen Length of dataout.
  406. * @param datain Buffer for received data. Undefined if return value is not APR_SUCCESS.
  407. * @param maxinlen Maximum possible length of received data. Short reads are allowed.
  408. * @return Indicates APR status code of exchange. APR_SUCCESS if exchange was successful, -1 if invalid data length was received.
  409. */
  410. static apr_status_t tcp_blocking_handshake(LLSocket::ptr_t handle, char * dataout, apr_size_t outlen, char * datain, apr_size_t maxinlen)
  411. {
  412. apr_socket_t* apr_socket = handle->getSocket();
  413. apr_status_t rv = APR_SUCCESS;
  414. apr_size_t expected_len = outlen;
  415. handle->setBlocking(1000);
  416. rv = apr_socket_send(apr_socket, dataout, &outlen);
  417. if (APR_SUCCESS != rv)
  418. {
  419. LL_WARNS("Proxy") << "Error sending data to proxy control channel, status: " << rv << LL_ENDL;
  420. ll_apr_warn_status(rv);
  421. }
  422. else if (expected_len != outlen)
  423. {
  424. LL_WARNS("Proxy") << "Incorrect data length sent. Expected: " << expected_len <<
  425. " Sent: " << outlen << LL_ENDL;
  426. rv = -1;
  427. }
  428. if (APR_SUCCESS == rv)
  429. {
  430. expected_len = maxinlen;
  431. rv = apr_socket_recv(apr_socket, datain, &maxinlen);
  432. if (rv != APR_SUCCESS)
  433. {
  434. LL_WARNS("Proxy") << "Error receiving data from proxy control channel, status: " << rv << LL_ENDL;
  435. ll_apr_warn_status(rv);
  436. }
  437. else if (expected_len < maxinlen)
  438. {
  439. LL_WARNS("Proxy") << "Incorrect data length received. Expected: " << expected_len <<
  440. " Received: " << maxinlen << LL_ENDL;
  441. rv = -1;
  442. }
  443. }
  444. handle->setNonBlocking();
  445. return rv;
  446. }
  447. /**
  448. * @brief Open a LLSocket and do a blocking connect to the chosen host.
  449. *
  450. * Checks for a successful connection, and makes sure the connection is closed if it fails.
  451. *
  452. * @param host The host to open the connection to.
  453. * @return The created socket. Will evaluate as NULL if the connection is unsuccessful.
  454. */
  455. static LLSocket::ptr_t tcp_open_channel(LLHost host)
  456. {
  457. LLSocket::ptr_t socket = LLSocket::create(NULL, LLSocket::STREAM_TCP);
  458. bool connected = socket->blockingConnect(host);
  459. if (!connected)
  460. {
  461. tcp_close_channel(&socket);
  462. }
  463. return socket;
  464. }
  465. /**
  466. * @brief Close the socket.
  467. *
  468. * @param handle_ptr The handle of the socket being closed. A pointer-to-pointer to avoid increasing the use count.
  469. */
  470. static void tcp_close_channel(LLSocket::ptr_t* handle_ptr)
  471. {
  472. LL_DEBUGS("Proxy") << "Resetting proxy LLSocket handle, use_count == " << handle_ptr->use_count() << LL_ENDL;
  473. handle_ptr->reset();
  474. }