From 74d9bf0d5525426feae4ea21e2a81034ddcf4d7f Mon Sep 17 00:00:00 2001 From: Robin Cornelius Date: Mon, 28 Mar 2011 11:20:06 +0100 Subject: VWR-20801 Implement SOCKS 5 Proxy for the viewer --- indra/llmessage/CMakeLists.txt | 2 + indra/llmessage/llcurl.cpp | 44 +++++++ indra/llmessage/llpacketring.cpp | 60 +++++++++- indra/llmessage/llpacketring.h | 3 + indra/llmessage/llsocks5.cpp | 224 ++++++++++++++++++++++++++++++++++++ indra/llmessage/llsocks5.h | 243 +++++++++++++++++++++++++++++++++++++++ indra/llmessage/net.cpp | 156 +++++++++++++++++++++++++ indra/llmessage/net.h | 5 + 8 files changed, 734 insertions(+), 3 deletions(-) create mode 100644 indra/llmessage/llsocks5.cpp create mode 100644 indra/llmessage/llsocks5.h (limited to 'indra/llmessage') diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 1cad0f6d22..8c68d38926 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -74,6 +74,7 @@ set(llmessage_SOURCE_FILES llsdrpcserver.cpp llservicebuilder.cpp llservice.cpp + llsocks5.cpp llstoredmessage.cpp lltemplatemessagebuilder.cpp lltemplatemessagedispatcher.cpp @@ -172,6 +173,7 @@ set(llmessage_HEADER_FILES llsdrpcserver.h llservice.h llservicebuilder.h + llsocks5.h llstoredmessage.h lltaskname.h llteleportflags.h diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index a485fa0160..020c0d6a4a 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -50,6 +50,8 @@ #include "llsdserialize.h" #include "llthread.h" +#include "llsocks5.h" + ////////////////////////////////////////////////////////////////////////////// /* The trick to getting curl to do keep-alives is to reuse the @@ -273,6 +275,28 @@ LLCurl::Easy* LLCurl::Easy::getEasy() // set no DMS caching as default for all easy handles. This prevents them adopting a // multi handles cache if they are added to one. curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); + + //Set the CURL options for either Socks or HTTP proxy + if (LLSocks::getInstance()->isHttpProxyEnabled()) + { + std::string address = LLSocks::getInstance()->getHTTPProxy().getIPString(); + U16 port = LLSocks::getInstance()->getHTTPProxy().getPort(); + curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_PROXY,address.c_str()); + curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_PROXYPORT,port); + if (LLSocks::getInstance()->getHttpProxyType() == LLPROXY_SOCKS) + { + curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); + if(LLSocks::getInstance()->getSelectedAuthMethod()==METHOD_PASSWORD) + { + curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_PROXYUSERPWD,LLSocks::getInstance()->getProxyUserPwd().c_str()); + } + } + else + { + curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + } + } + ++gCurlEasyCount; return easy; } @@ -446,6 +470,26 @@ void LLCurl::Easy::prepRequest(const std::string& url, // setopt(CURLOPT_VERBOSE, 1); // usefull for debugging setopt(CURLOPT_NOSIGNAL, 1); + if (LLSocks::getInstance()->isHttpProxyEnabled()) + { + std::string address = LLSocks::getInstance()->getHTTPProxy().getIPString(); + U16 port = LLSocks::getInstance()->getHTTPProxy().getPort(); + setoptString(CURLOPT_PROXY, address.c_str()); + setopt(CURLOPT_PROXYPORT, port); + if (LLSocks::getInstance()->getHttpProxyType() == LLPROXY_SOCKS) + { + setopt(CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); + if(LLSocks::getInstance()->getSelectedAuthMethod()==METHOD_PASSWORD) + { + setoptString(CURLOPT_PROXYUSERPWD,LLSocks::getInstance()->getProxyUserPwd()); + } + } + else + { + setopt(CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + } + } + mOutput.reset(new LLBufferArray); setopt(CURLOPT_WRITEFUNCTION, (void*)&curlWriteCallback); setopt(CURLOPT_WRITEDATA, (void*)this); diff --git a/indra/llmessage/llpacketring.cpp b/indra/llmessage/llpacketring.cpp index 8999dec64a..0c8c5f763c 100644 --- a/indra/llmessage/llpacketring.cpp +++ b/indra/llmessage/llpacketring.cpp @@ -35,6 +35,17 @@ #include "llrand.h" #include "u64.h" +#include "llsocks5.h" +#include "message.h" + +#if LL_WINDOWS + #include +#else + #include + #include +#endif + + /////////////////////////////////////////////////////////// LLPacketRing::LLPacketRing () : mUseInThrottle(FALSE), @@ -216,8 +227,31 @@ S32 LLPacketRing::receivePacket (S32 socket, char *datap) else { // no delay, pull straight from net + if (LLSocks::isEnabled()) + { + U8 buffer[MAX_BUFFER_SIZE]; + packet_size = receive_packet(socket, (char*)buffer); + + if (packet_size > 10) + { + memcpy(datap,buffer+10,packet_size-10); + } + else + { + packet_size=0; + } + + proxywrap_t * header; + header = (proxywrap_t *)buffer; + mLastSender.setAddress(header->addr); + mLastSender.setPort(ntohs(header->port)); + } + else + { packet_size = receive_packet(socket, datap); mLastSender = ::get_sender(); + } + mLastReceivingIF = ::get_receiving_interface(); if (packet_size) // did we actually get a packet? @@ -243,7 +277,7 @@ BOOL LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LL BOOL status = TRUE; if (!mUseOutThrottle) { - return send_packet(h_socket, send_buffer, buf_size, host.getAddress(), host.getPort() ); + return doSendPacket(h_socket, send_buffer, buf_size, host ); } else { @@ -264,7 +298,7 @@ BOOL LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LL mOutBufferLength -= packetp->getSize(); packet_size = packetp->getSize(); - status = send_packet(h_socket, packetp->getData(), packet_size, packetp->getHost().getAddress(), packetp->getHost().getPort()); + status = doSendPacket(h_socket, packetp->getData(), packet_size, packetp->getHost()); delete packetp; // Update the throttle @@ -273,7 +307,7 @@ BOOL LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LL else { // If the queue's empty, we can just send this packet right away. - status = send_packet(h_socket, send_buffer, buf_size, host.getAddress(), host.getPort() ); + status = doSendPacket(h_socket, send_buffer, buf_size, host ); packet_size = buf_size; // Update the throttle @@ -311,3 +345,23 @@ BOOL LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LL return status; } + +BOOL LLPacketRing::doSendPacket(int h_socket, const char * send_buffer, S32 buf_size, LLHost host) +{ + + if (!LLSocks::isEnabled()) + { + return send_packet(h_socket, send_buffer, buf_size, host.getAddress(), host.getPort()); + } + + proxywrap_t *socks_header = (proxywrap_t *)&mProxyWrappedSendBuffer; + socks_header->rsv = 0; + socks_header->addr = host.getAddress(); + socks_header->port = htons(host.getPort()); + socks_header->atype = ADDRESS_IPV4; + socks_header->frag = 0; + + memcpy(mProxyWrappedSendBuffer+10, send_buffer, buf_size); + + return send_packet(h_socket,(const char*) mProxyWrappedSendBuffer, buf_size+10, LLSocks::getInstance()->getUDPProxy().getAddress(), LLSocks::getInstance()->getUDPProxy().getPort()); +} diff --git a/indra/llmessage/llpacketring.h b/indra/llmessage/llpacketring.h index e6409d2048..2fe2f8e1e9 100644 --- a/indra/llmessage/llpacketring.h +++ b/indra/llmessage/llpacketring.h @@ -82,6 +82,9 @@ protected: LLHost mLastSender; LLHost mLastReceivingIF; + + BOOL doSendPacket(int h_socket, const char * send_buffer, S32 buf_size, LLHost host); + U8 mProxyWrappedSendBuffer[NET_BUFFER_SIZE]; }; diff --git a/indra/llmessage/llsocks5.cpp b/indra/llmessage/llsocks5.cpp new file mode 100644 index 0000000000..1f458a007c --- /dev/null +++ b/indra/llmessage/llsocks5.cpp @@ -0,0 +1,224 @@ +/** + * @file llsocks5.cpp + * @brief Socks 5 implementation + * + * $LicenseInfo:firstyear=2000&license=viewergpl$ + * + * Copyright (c) 2000-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include + +#include "linden_common.h" +#include "net.h" +#include "llhost.h" +#include "message.h" +#include "llsocks5.h" + +// Static class variable instances + +// We want this to be static to avoid excessive indirection on every +// incomming packet just to do a simple bool test. The getter for this +// member is also static +bool LLSocks::sUdpProxyEnabled; +bool LLSocks::sHttpProxyEnabled; + +LLSocks::LLSocks() +{ + sUdpProxyEnabled = false; + sHttpProxyEnabled = false; + hProxyControlChannel = 0; + mProxyType = LLPROXY_SOCKS; +} + +// Perform a Socks5 authentication and UDP assioacation to the proxy +// specified by proxy, and assiocate UDP port message_port +int LLSocks::proxyHandshake(LLHost proxy, U32 message_port) +{ + int result; + + /* 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 = mAuthMethodSelected; // send only the selected metho + + result = tcp_handshake(hProxyControlChannel, (char*)&socks_auth_request, sizeof(socks_auth_request_t), (char*)&socks_auth_response, sizeof(socks_auth_response_t)); + if (result != 0) + { + llwarns << "Socks authentication request failed, error on TCP control channel : " << result << llendl; + stopProxy(); + return SOCKS_CONNECT_ERROR; + } + + if (socks_auth_response.method == AUTH_NOT_ACCEPTABLE) + { + llwarns << "Socks5 server refused all our authentication methods" << llendl; + stopProxy(); + return SOCKS_NOT_ACCEPTABLE; + } + + // SOCKS5 USERNAME/PASSWORD authentication + if (socks_auth_response.method == METHOD_PASSWORD) + { + // The server has requested a username/password combination + U32 request_size = mSocksUsername.size() + mSocksPassword.size() + 3; + char * password_auth = (char *)malloc(request_size); + password_auth[0] = 0x01; + password_auth[1] = mSocksUsername.size(); + memcpy(&password_auth[2],mSocksUsername.c_str(), mSocksUsername.size()); + password_auth[mSocksUsername.size()+2] = mSocksPassword.size(); + memcpy(&password_auth[mSocksUsername.size()+3], mSocksPassword.c_str(), mSocksPassword.size()); + + authmethod_password_reply_t password_reply; + + result = tcp_handshake(hProxyControlChannel, password_auth, request_size, (char*)&password_reply, sizeof(authmethod_password_reply_t)); + free (password_auth); + + if (result != 0) + { + llwarns << "Socks authentication failed, error on TCP control channel : " << result << llendl; + stopProxy(); + return SOCKS_CONNECT_ERROR; + } + + if (password_reply.status != AUTH_SUCCESS) + { + llwarns << "Socks authentication failed" << llendl; + stopProxy(); + return SOCKS_AUTH_FAIL; + } + } + + /* SOCKS5 connect request */ + + socks_command_request_t connect_request; + socks_command_response_t connect_reply; + + connect_request.version = SOCKS_VERSION; //Socks V5 + connect_request.command = COMMAND_UDP_ASSOCIATE; // Associate UDP + connect_request.flag = FIELD_RESERVED; + connect_request.atype = ADDRESS_IPV4; + connect_request.address = 0; // 0.0.0.0 We are not fussy about address + // UDP is promiscious receive for our protocol + connect_request.port = 0; // Port must be 0 if you ever want to connect via NAT and your router does port rewrite for you + + result = tcp_handshake(hProxyControlChannel, (char*)&connect_request, sizeof(socks_command_request_t), (char*)&connect_reply, sizeof(socks_command_response_t)); + if (result != 0) + { + llwarns << "Socks connect request failed, error on TCP control channel : " << result << llendl; + stopProxy(); + return SOCKS_CONNECT_ERROR; + } + + if (connect_reply.reply != REPLY_REQUEST_GRANTED) + { + //Something went wrong + llwarns << "Connection to SOCKS5 server failed, UDP forward request not granted" << llendl; + stopProxy(); + return SOCKS_UDP_FWD_NOT_GRANTED; + } + + mUDPProxy.setPort(ntohs(connect_reply.port)); // reply port is in network byte order + mUDPProxy.setAddress(proxy.getAddress()); + // All good now we have been given the UDP port to send requests that need forwarding. + llinfos << "Socks 5 UDP proxy connected on " << mUDPProxy << llendl; + return SOCKS_OK; +} + +int LLSocks::startProxy(LLHost proxy, U32 message_port) +{ + int status; + + mTCPProxy = proxy; + + if (hProxyControlChannel) + { + tcp_close_channel(hProxyControlChannel); + hProxyControlChannel=0; + } + + hProxyControlChannel = tcp_open_channel(proxy); + if (hProxyControlChannel == -1) + { + return SOCKS_HOST_CONNECT_FAILED; + } + + status = proxyHandshake(proxy, message_port); + if (status == SOCKS_OK) + { + sUdpProxyEnabled=true; + } + return status; +} + +int LLSocks::startProxy(std::string host, U32 port) +{ + mTCPProxy.setHostByName(host); + mTCPProxy.setPort(port); + return startProxy(mTCPProxy, (U32)gMessageSystem->mPort); +} + +void LLSocks::stopProxy() +{ + sUdpProxyEnabled = false; + + // If the Socks proxy is requested to stop and we are using that for http as well + // 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 == mProxyType) + { + sHttpProxyEnabled = false; + } + + if (hProxyControlChannel) + { + tcp_close_channel(hProxyControlChannel); + hProxyControlChannel=0; + } +} + +void LLSocks::setAuthNone() +{ + mAuthMethodSelected = METHOD_NOAUTH; +} + +void LLSocks::setAuthPassword(std::string username, std::string password) +{ + mAuthMethodSelected = METHOD_PASSWORD; + mSocksUsername = username; + mSocksPassword = password; +} + +void LLSocks::EnableHttpProxy(LLHost httpHost, LLHttpProxyType type) +{ + sHttpProxyEnabled = true; + mHTTPProxy = httpHost; + mProxyType = type; +} diff --git a/indra/llmessage/llsocks5.h b/indra/llmessage/llsocks5.h new file mode 100644 index 0000000000..83e311a962 --- /dev/null +++ b/indra/llmessage/llsocks5.h @@ -0,0 +1,243 @@ +/** + * @file llsocks5.h + * @brief Socks 5 implementation + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_SOCKS5_H +#define LL_SOCKS5_H + +#include "llhost.h" +#include "llmemory.h" +#include "llsingleton.h" +#include + +// Error codes returned from the StartProxy method + +#define SOCKS_OK 0 +#define SOCKS_CONNECT_ERROR -1 +#define SOCKS_NOT_PERMITTED -2 +#define SOCKS_NOT_ACCEPTABLE -3 +#define SOCKS_AUTH_FAIL -4 +#define SOCKS_UDP_FWD_NOT_GRANTED -5 +#define SOCKS_HOST_CONNECT_FAILED -6 + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN (255 + 1) /* socks5: 255, +1 for len. */ +#endif + +#define SOCKS_VERSION 0x05 // we are using socks 5 + +// socks 5 address/hostname types +#define ADDRESS_IPV4 0x01 +#define ADDRESS_HOSTNAME 0x03 +#define ADDRESS_IPV6 0x04 + +// Lets just use our own ipv4 struct rather than dragging in system +// specific headers +union ipv4_address_t { + unsigned char octects[4]; + U32 addr32; +}; + +// Socks 5 control channel commands +#define COMMAND_TCP_STREAM 0x01 +#define COMMAND_TCP_BIND 0x02 +#define COMMAND_UDP_ASSOCIATE 0x03 + +// Socks 5 command replys +#define REPLY_REQUEST_GRANTED 0x00 +#define REPLY_GENERAL_FAIL 0x01 +#define REPLY_RULESET_FAIL 0x02 +#define REPLY_NETWORK_UNREACHABLE 0x03 +#define REPLY_HOST_UNREACHABLE 0x04 +#define REPLY_CONNECTION_REFUSED 0x05 +#define REPLY_TTL_EXPIRED 0x06 +#define REPLY_PROTOCOL_ERROR 0x07 +#define REPLY_TYPE_NOT_SUPPORTED 0x08 + +#define FIELD_RESERVED 0x00 + +// The standard socks5 request packet +// Push current alignment to stack and set alignment to 1 byte boundary +// This enabled us to use structs directly to set up and receive network packets +// into the correct fields, without fear of boundary alignment causing issues +#pragma pack(push,1) + +// Socks5 command packet +struct socks_command_request_t { + unsigned char version; + unsigned char command; + unsigned char flag; + unsigned char atype; + U32 address; + U16 port; +}; + +// Standard socks5 reply packet +struct socks_command_response_t { + unsigned char version; + unsigned char reply; + unsigned char flag; + unsigned char atype; + unsigned char add_bytes[4]; + U16 port; +}; + +#define AUTH_NOT_ACCEPTABLE 0xFF // reply if prefered methods are not avaiable +#define AUTH_SUCCESS 0x00 // reply if authentication successfull + +// socks 5 authentication request, stating which methods the client supports +struct socks_auth_request_t { + unsigned char version; + unsigned char num_methods; + unsigned char methods; // We are only using a single method currently +}; + +// socks 5 authentication response packet, stating server prefered method +struct socks_auth_response_t { + unsigned char version; + unsigned char method; +}; + +// socks 5 password reply packet +struct authmethod_password_reply_t { + unsigned char version; + unsigned char status; +}; + +// socks 5 UDP packet header +struct proxywrap_t { + U16 rsv; + U8 frag; + U8 atype; + U32 addr; + U16 port; +}; + +#pragma pack(pop) /* restore original alignment from stack */ + + +// Currently selected http proxy type +enum LLHttpProxyType +{ + LLPROXY_SOCKS = 0, + LLPROXY_HTTP = 1 +}; + +// Auth types +enum LLSocks5AuthType +{ + METHOD_NOAUTH = 0x00, // Client supports no auth + METHOD_GSSAPI = 0x01, // Client supports GSSAPI (Not currently supported) + METHOD_PASSWORD = 0x02 // Client supports username/password +}; + +class LLSocks: public LLSingleton +{ +public: + LLSocks(); + + // Start a connection to the socks 5 proxy + int startProxy(std::string host,U32 port); + int startProxy(LLHost proxy,U32 messagePort); + + // Disconnect and clean up any connection to the socks 5 proxy + void stopProxy(); + + // Set up to use Password auth when connecting to the socks proxy + void setAuthPassword(std::string username,std::string password); + + // Set up to use No Auth when connecting to the socks proxy; + void setAuthNone(); + + // get the currently selected auth method + LLSocks5AuthType getSelectedAuthMethod() { return mAuthMethodSelected; }; + + // static check for enabled status for UDP packets + static bool isEnabled(){return sUdpProxyEnabled;}; + + // static check for enabled status for http packets + static bool isHttpProxyEnabled(){return sHttpProxyEnabled;}; + + // Proxy http packets via httpHost, which can be a Socks5 or a http proxy + // as specified in type + void EnableHttpProxy(LLHost httpHost,LLHttpProxyType type); + + // Stop proxying http packets + void DisableHttpProxy() {sHttpProxyEnabled = false;}; + + // get the UDP proxy address and port + LLHost getUDPProxy(){return mUDPProxy;}; + + // get the socks 5 TCP control channel address and port + LLHost getTCPProxy(){return mTCPProxy;}; + + //get the http proxy address and port + LLHost getHTTPProxy(){return mHTTPProxy;}; + + // get the currently selected http proxy type + LLHttpProxyType getHttpProxyType(){return mProxyType;}; + + //Get the username password in a curl compatible format + std::string getProxyUserPwd(){ return (mSocksUsername + ":" + mSocksPassword);}; + +private: + + // Open a communication channel to the socks5 proxy proxy, at port messagePort + int proxyHandshake(LLHost proxy,U32 messagePort); + + // socket handle to proxy tcp control channel + S32 hProxyControlChannel; + + // is the UDP proxy enabled + static bool sUdpProxyEnabled; + // is the http proxy enabled + static bool sHttpProxyEnabled; + + // currently selected http proxy type + LLHttpProxyType mProxyType; + + // UDP proxy address and port + LLHost mUDPProxy; + // TCP Proxy control channel address and port + LLHost mTCPProxy; + // HTTP proxy address and port + LLHost mHTTPProxy; + + // socks 5 auth method selected + LLSocks5AuthType mAuthMethodSelected; + + // socks 5 username + std::string mSocksUsername; + // socks 5 password + std::string mSocksPassword; +}; + +#endif diff --git a/indra/llmessage/net.cpp b/indra/llmessage/net.cpp index 97611c3b51..ab5c1950c6 100644 --- a/indra/llmessage/net.cpp +++ b/indra/llmessage/net.cpp @@ -50,6 +50,7 @@ #include "lltimer.h" #include "indra_constants.h" +#include "llsocks5.h" // Globals #if LL_WINDOWS @@ -189,6 +190,90 @@ U32 ip_string_to_u32(const char* ip_string) #if LL_WINDOWS +int tcp_handshake(S32 handle, char * dataout, int outlen, char * datain, int maxinlen) +{ + int result; + result = send(handle, dataout, outlen, 0); + if (result != outlen) + { + S32 err = WSAGetLastError(); + llwarns << "Error sending data to proxy control channel, number of bytes sent were " << result << " error code was " << err << llendl; + return -1; + } + + result = recv(handle, datain, maxinlen, 0); + if (result != maxinlen) + { + S32 err = WSAGetLastError(); + llwarns << "Error receiving data from proxy control channel, number of bytes received were " << result << " error code was " << err << llendl; + return -1; + } + + return 0; +} + +S32 tcp_open_channel(LLHost host) +{ + // Open a TCP channel + // Jump through some hoops to ensure that if the request hosts is down + // or not reachable connect() does not block + + S32 handle; + handle = socket(AF_INET, SOCK_STREAM, 0); + if (!handle) + { + llwarns << "Error opening TCP control socket, socket() returned " << handle << llendl; + return -1; + } + + struct sockaddr_in address; + address.sin_port = htons(host.getPort()); + address.sin_family = AF_INET; + address.sin_addr.s_addr = host.getAddress(); + + // Non blocking + WSAEVENT hEvent=WSACreateEvent(); + WSAEventSelect(handle, hEvent, FD_CONNECT) ; + connect(handle, (struct sockaddr*)&address, sizeof(address)) ; + // Wait fot 5 seconds, if we can't get a TCP channel open in this + // time frame then there is something badly wrong. + WaitForSingleObject(hEvent, 1000*5); // 5 seconds time out + + WSANETWORKEVENTS netevents; + WSAEnumNetworkEvents(handle,hEvent,&netevents); + + // Check the async event status to see if we connected + if ((netevents.lNetworkEvents & FD_CONNECT) == FD_CONNECT) + { + if (netevents.iErrorCode[FD_CONNECT_BIT] != 0) + { + llwarns << "Unable to open TCP channel, WSA returned an error code of " << netevents.iErrorCode[FD_CONNECT_BIT] << llendl; + WSACloseEvent(hEvent); + return -1; + } + + // Now we are connected disable non blocking + // we don't need support an async interface as + // currently our only consumer (socks5) will make one round + // of packets then just hold the connection open + WSAEventSelect(handle, hEvent, NULL) ; + unsigned long NonBlock = 0; + ioctlsocket(handle, FIONBIO, &NonBlock); + + return handle; + } + + llwarns << "Unable to open TCP channel, Timeout is the host up?" << netevents.iErrorCode[FD_CONNECT_BIT] << llendl; + return -1; +} + +void tcp_close_channel(S32 handle) +{ + llinfos << "Closing TCP channel" << llendl; + shutdown(handle, SD_BOTH); + closesocket(handle); +} + S32 start_net(S32& socket_out, int& nPort) { // Create socket, make non-blocking @@ -385,6 +470,77 @@ BOOL send_packet(int hSocket, const char *sendBuffer, int size, U32 recipient, i #else + +int tcp_handshake(S32 handle, char * dataout, int outlen, char * datain, int maxinlen) +{ + if (send(handle, dataout, outlen, 0) != outlen) + { + llwarns << "Error sending data to proxy control channel" << llendl; + return -1; + } + + if (recv(handle, datain, maxinlen, 0) != maxinlen) + { + llwarns << "Error receiving data to proxy control channel" << llendl; + return -1; + } + + return 0; +} + +S32 tcp_open_channel(LLHost host) +{ + S32 handle; + handle = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (!handle) + { + llwarns << "Error opening TCP control socket, socket() returned " << handle << llendl; + return -1; + } + + struct sockaddr_in address; + address.sin_port = htons(host.getPort()); + address.sin_family = AF_INET; + address.sin_addr.s_addr = host.getAddress(); + + // Set the socket to non blocking for the connect() + int flags = fcntl(handle, F_GETFL, 0); + fcntl(handle, F_SETFL, flags | O_NONBLOCK); + + S32 error = connect(handle, (sockaddr*)&address, sizeof(address)); + if (error && (errno != EINPROGRESS)) + { + llwarns << "Unable to open TCP channel, error code: " << errno << llendl; + return -1; + } + + struct timeval timeout; + timeout.tv_sec = 5; // Maximum time to wait for the connect() to complete + timeout.tv_usec = 0; + fd_set fds; + FD_ZERO(&fds); + FD_SET(handle, &fds); + + // See if we have connectde or time out after 5 seconds + U32 rc = select(sizeof(fds)*8, NULL, &fds, NULL, &timeout); + + if (rc != 1) // we require exactly one descriptor to be set + { + llwarns << "Unable to open TCP channel" << llendl; + return -1; + } + + // Return the socket to blocking operations + fcntl(handle, F_SETFL, flags); + + return handle; +} + +void tcp_close_channel(S32 handle) +{ + close(handle); +} + // Create socket, make non-blocking S32 start_net(S32& socket_out, int& nPort) { diff --git a/indra/llmessage/net.h b/indra/llmessage/net.h index 9f4f5c5821..d93ed20c98 100644 --- a/indra/llmessage/net.h +++ b/indra/llmessage/net.h @@ -52,6 +52,11 @@ U32 get_sender_ip(void); LLHost get_receiving_interface(); U32 get_receiving_interface_ip(void); +// Some helpful tcp functions added for the socks 5 proxy support +S32 tcp_open_channel(LLHost host); // Open a tcp channel to a given host +void tcp_close_channel(S32 handle); // Close an open tcp channel +int tcp_handshake(S32 handle, char * dataout, int outlen, char * datain, int maxinlen); // Do a TCP data handshake + const char* u32_to_ip_string(U32 ip); // Returns pointer to internal string buffer, "(bad IP addr)" on failure, cannot nest calls char* u32_to_ip_string(U32 ip, char *ip_string); // NULL on failure, ip_string on success, you must allocate at least MAXADDRSTR chars U32 ip_string_to_u32(const char* ip_string); // Wrapper for inet_addr() -- cgit v1.2.3