//
// This file is part of the aMule Project.
//
// Copyright (c) 2004-2009 aMule Team ( admin@amule.org / http://www.amule.org )
// Copyright (c) 2004-2009 Marcelo Jimenez ( phoenix@amule.org )
//
// Any parts of this program derived from the xMule, lMule or eMule project,
// or contributed by third-party developers are copyrighted by their
// respective authors.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
//


#include "Proxy.h"		/* for Interface		*/

#include <common/EventIDs.h>

#include "ArchSpecific.h"	/* for ENDIAN_HTONS()		*/
#include "Logger.h"		/* for AddDebugLogLineM		*/
#include "OtherFunctions.h"	/* for EncodeBase64()		*/
#include <common/StringFunctions.h>	/* for unicode2char */

//------------------------------------------------------------------------------
// CProxyData
//------------------------------------------------------------------------------

CProxyData::CProxyData()
{
	Clear();
}

CProxyData::CProxyData(
	bool		proxyEnable,
	CProxyType	proxyType,
	const wxString	&proxyHostName,
	unsigned short	proxyPort,
	bool		enablePassword,
	const wxString	&userName,
	const wxString	&password)
:
m_proxyEnable(proxyEnable),
m_proxyType(proxyType),
m_proxyHostName(proxyHostName),
m_proxyPort(proxyPort),
/* 
 * The flag m_enablePassword is currently not used. The first 
 * authentication method tryed is No-Authentication, the second
 * is username/password. If there is no username/password in
 * CProxyData, a NULL username/password is sent. That will probably
 * lead to a failure, but at least we tryed. Maybe this behaviour
 * could be altered later.
 */
m_enablePassword(enablePassword),
m_userName(userName),
m_password(password)
{
}

void CProxyData::Clear()
{
	m_proxyEnable = false;
	m_proxyType = PROXY_NONE;
	m_proxyHostName.Clear();
	m_proxyPort = 0;
	m_enablePassword = false;
	m_userName.Clear();
	m_password.Clear();
}

#ifndef CLIENT_GUI

#include <typeinfo> // Do_not_auto_remove (NetBSD, older gccs)

//------------------------------------------------------------------------------
// ProxyEventHandler
//------------------------------------------------------------------------------

CProxyEventHandler::CProxyEventHandler()
{
}

BEGIN_EVENT_TABLE(CProxyEventHandler, wxEvtHandler)
	EVT_SOCKET(ID_PROXY_SOCKET_EVENT, CProxyEventHandler::ProxySocketHandler)
END_EVENT_TABLE()

//
// THE one and only Event Handler
//
static CProxyEventHandler g_proxyEventHandler;

void CProxyEventHandler::ProxySocketHandler(wxSocketEvent& event)
{
	CProxySocket *sock = dynamic_cast<CProxySocket *>(event.GetSocket());
	if (sock) {
		sock->m_proxyStateMachine->Schedule(event.GetSocketEvent());
	} else {
		// we're doomed :)
	}
	sock->m_proxyStateMachine->Clock();
}

//------------------------------------------------------------------------------
// CProxyStateMachine
//------------------------------------------------------------------------------

CProxyStateMachine::CProxyStateMachine(
		wxString name,
		const unsigned int max_states,
		const CProxyData &proxyData,
		CProxyCommand proxyCommand)
:
CStateMachine(NewName(name, proxyCommand), max_states, PROXY_STATE_START),
m_proxyData(proxyData),
m_proxyCommand(proxyCommand),
m_isLost(false),
m_isConnected(false),
m_canReceive(false),
m_canSend(false),
m_ok(true),
m_lastRead(0),
m_lastWritten(0),
// Will be initialized at Start()
m_peerAddress(NULL),
m_proxyClientSocket(NULL),
m_proxyBoundAddress(NULL),
// Temporary variables
m_lastReply(0),
m_packetLenght(0)
{
}

CProxyStateMachine::~CProxyStateMachine()
{
	delete m_peerAddress;
}

wxString &CProxyStateMachine::NewName(wxString &s, CProxyCommand proxyCommand)
{
	switch (proxyCommand) {
	case PROXY_CMD_CONNECT:
		s += wxT("-CONNECT");
		break;
		
	case PROXY_CMD_BIND:
		s += wxT("-BIND");
		break;
		
	case PROXY_CMD_UDP_ASSOCIATE:
		s += wxT("-UDP");
		break;
	}
	
	return s;
}

bool CProxyStateMachine::Start(const wxIPaddress &peerAddress, wxSocketClient *proxyClientSocket)
{
	m_proxyClientSocket = proxyClientSocket;
	try {
		const wxIPV4address &peer = dynamic_cast<const wxIPV4address &>(peerAddress);
		m_peerAddress = new amuleIPV4Address(peer);
	} catch (const std::bad_cast& WXUNUSED(e)) {
		// Should process other types of wxIPAddres before quitting
		AddDebugLogLineM(false, logProxy, wxT("(1)bad_cast exception!"));
		wxASSERT(false);
		return false;
	}
	
	// To run the state machine, return and just let the events start to happen.
	return true;
}

t_sm_state CProxyStateMachine::HandleEvent(t_sm_event event)
{
	// Default is stay in current state	
	t_sm_state ret = GetState();
	switch(event)
	{
	case wxSOCKET_CONNECTION:
		AddDebugLogLineM(false, logProxy, wxT("Connection event"));
		m_isConnected = true;
		break;
		
	case wxSOCKET_INPUT:
		AddDebugLogLineM(false, logProxy, wxT("Input event"));
		m_canReceive = true;
		break;
		
	case wxSOCKET_OUTPUT:
		AddDebugLogLineM(false, logProxy, wxT("Output event"));
		m_canSend = true;
		break;
		
	case wxSOCKET_LOST:
		AddDebugLogLineM(false, logProxy, wxT("Lost connection event"));
		m_isLost = true;
		m_ok = false;
		break;
		
	default:
		AddDebugLogLineM(false, logProxy, wxT("Unknown event"));
		break;
	}
	
	// Aborting conditions:
	// - wxSOCKET_LOST event
	// - More than 10 times on the same state
	if (	m_isLost ||
		GetClocksInCurrentState() > 10) {
		ret = PROXY_STATE_END;
	}
	
	return ret;
}

void CProxyStateMachine::AddDummyEvent()
{
	wxSocketEvent e(ID_PROXY_SOCKET_EVENT);
	// Make sure this is an unknown event :)
	e.m_event = (wxSocketNotify)(
		wxSOCKET_INPUT + wxSOCKET_OUTPUT +
		wxSOCKET_CONNECTION + wxSOCKET_LOST);
	e.SetEventObject(m_proxyClientSocket);
	g_proxyEventHandler.AddPendingEvent(e);
}

/*
 * Notice! These includes are here as long as it is impossible to retrieve 
 * the event handler from the socket. They should be removed. For now,
 * please leave it here.
 */

void CProxyStateMachine::ReactivateSocket()
{
	/*    If proxy is beeing used, then the TCP socket handlers 
	 * (CServerSocketHandler and CClientTCPSocketHandler) will not
	 * receive a wxSOCKET_CONNECTION event, because the connection has
	 * already started with the proxy. So we must add a wxSOCKET_CONNECTION
	 * event to make things go undetected. A wxSOCKET_OUTPUT event is also
	 * necessary to start sending data to the server. */ 
	CProxySocket *s = dynamic_cast<CProxySocket *>(m_proxyClientSocket);
	// If that is not true, we are in serious trouble...
	wxASSERT(s);
	if (CDatagramSocketProxy *udp = s->GetUDPSocket()) {
		// The original socket was an UDP socket
		if(m_ok) {
			// From now on, the UDP socket can be used,
			// remove the protection.
			udp->SetUDPSocketOk();
		}
		// No need to call RestoreState(), that socket will no longer
		// be used after proxy negotiation.
	} else {
		// The original socket was a TCP socket
		s->RestoreEventHandler();
		wxSocketEvent e(s->GetEventHandlerId());
		e.m_event = wxSOCKET_CONNECTION;
		e.SetEventObject(s);
		wxEvtHandler *h(s->GetEventHandler());
		h->AddPendingEvent(e);
		e.m_event = wxSOCKET_OUTPUT;
		h->AddPendingEvent(e);
		if (!m_ok) {
			e.m_event = wxSOCKET_LOST;
			h->AddPendingEvent(e);
		}
		s->RestoreState();
	}
}

wxSocketBase &CProxyStateMachine::ProxyWrite(wxSocketBase &socket, const void *buffer, wxUint32 nbytes)
{
	wxSocketBase &ret = socket.Write(buffer, nbytes);
	m_lastWritten = m_proxyClientSocket->LastCount();
	/* Set the status of this operation */
	m_lastError = wxSOCKET_NOERROR;
	m_ok = !m_proxyClientSocket->Error();
	if (!m_ok) {
		m_lastError = m_proxyClientSocket->LastError();
		m_ok = m_lastError == wxSOCKET_WOULDBLOCK;
		if (m_ok) {
			m_canSend = false;
		}
	}
	
	return ret;
}

wxSocketBase &CProxyStateMachine::ProxyRead(wxSocketBase &socket, void *buffer)
{
	/* Always try to read the full buffer. That explicitly demands that
	 * the socket has the flag wxSOCKET_NONE. */
	wxSocketBase &ret = socket.Read(buffer, PROXY_BUFFER_SIZE);
	m_lastRead = m_proxyClientSocket->LastCount();
	/* Set the status of this operation */
	m_lastError = wxSOCKET_NOERROR;
	m_ok = !m_proxyClientSocket->Error();
	if (!m_ok) {
		m_lastError = m_proxyClientSocket->LastError();
// ADUNANZA BEGIN
#if 0
		m_ok = m_lastError == wxSOCKET_WOULDBLOCK;
#else
		m_ok = (m_lastError == wxSOCKET_WOULDBLOCK);
#endif
// ADUNANZA END
		if (m_ok) {
			m_canReceive = false;
		}
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// CSocks5StateMachine
//------------------------------------------------------------------------------

/**
 * The state machine constructor must initialize the array of pointer to member
 * functions. Don't waste you time trying to statically initialize this, pointer
 * to member functions require an object to operate on, so this array must be
 * initialized at run time.
 */
CSocks5StateMachine::CSocks5StateMachine(
	const CProxyData &proxyData,
	CProxyCommand proxyCommand)
:
CProxyStateMachine(
	wxString(wxT("Socks5")), SOCKS5_MAX_STATES, proxyData, proxyCommand)
{
	m_process_state[ 0] = &CSocks5StateMachine::process_start;
	m_state_name[ 0]    = wxT("process_start");
	m_process_state[ 1] = &CSocks5StateMachine::process_end;
	m_state_name[ 1]    = wxT("process_end");
	m_process_state[ 2] = &CSocks5StateMachine::process_send_query_authentication_method;
	m_state_name[ 2]    = wxT("process_send_query_authentication_method");
	m_process_state[ 3] = &CSocks5StateMachine::process_receive_authentication_method;
	m_state_name[ 3]    = wxT("process_receive_authentication_method");
	m_process_state[ 4] = &CSocks5StateMachine::process_process_authentication_method;
	m_state_name[ 4]    = wxT("process_process_authentication_method");
	m_process_state[ 5] = &CSocks5StateMachine::process_send_authentication_gssapi;
	m_state_name[ 5]    = wxT("process_send_authentication_gssapi");
	m_process_state[ 6] = &CSocks5StateMachine::process_receive_authentication_gssapi;
	m_state_name[ 6]    = wxT("process_receive_authentication_gssapi");
	m_process_state[ 7] = &CSocks5StateMachine::process_process_authentication_gssapi;
	m_state_name[ 7]    = wxT("process_process_authentication_gssapi");
	m_process_state[ 8] = &CSocks5StateMachine::process_send_authentication_username_password;
	m_state_name[ 8]    = wxT("process_send_authentication_username_password");
	m_process_state[ 9] = &CSocks5StateMachine::process_receive_authentication_username_password;
	m_state_name[ 9]    = wxT("process_receive_authentication_username_password");
	m_process_state[10] = &CSocks5StateMachine::process_process_authentication_username_password;
	m_state_name[10]    = wxT("process_process_authentication_username_password");
	m_process_state[11] = &CSocks5StateMachine::process_send_command_request;
	m_state_name[11]    = wxT("process_send_command_request");
	m_process_state[12] = &CSocks5StateMachine::process_receive_command_reply;
	m_state_name[12]    = wxT("process_receive_command_reply");
	m_process_state[13] = &CSocks5StateMachine::process_process_command_reply;
	m_state_name[13]    = wxT("process_process_command_reply");
}

void CSocks5StateMachine::process_state(t_sm_state state, bool entry)
{
	/* Ok, the syntax is terrible, but this is correct. This is a
	 * pointer to a member function. No C equivalent for that. */
	(this->*m_process_state[state])(entry);
#ifdef __DEBUG__
	int n = 0;
	
	switch (state) {
	case SOCKS5_STATE_START:
	case SOCKS5_STATE_END:
	case SOCKS5_STATE_RECEIVE_AUTHENTICATION_METHOD:
	case SOCKS5_STATE_RECEIVE_AUTHENTICATION_GSSAPI:
	case SOCKS5_STATE_RECEIVE_AUTHENTICATION_USERNAME_PASSWORD:
	case SOCKS5_STATE_RECEIVE_COMMAND_REPLY:
	default:
		n = 0;
		break;
		
	case SOCKS5_STATE_SEND_QUERY_AUTHENTICATION_METHOD:
	case SOCKS5_STATE_SEND_AUTHENTICATION_GSSAPI:
	case SOCKS5_STATE_SEND_AUTHENTICATION_USERNAME_PASSWORD:
	case SOCKS5_STATE_SEND_COMMAND_REQUEST:
		n = m_packetLenght;
		break;
	
	case SOCKS5_STATE_PROCESS_AUTHENTICATION_METHOD:
	case SOCKS5_STATE_PROCESS_AUTHENTICATION_GSSAPI:
	case SOCKS5_STATE_PROCESS_AUTHENTICATION_USERNAME_PASSWORD:
	case SOCKS5_STATE_PROCESS_COMMAND_REPLY:
		n = m_lastRead;
		break;
	}
	
	if (entry) {
		DumpMem(m_buffer, n, m_state_name[state], m_ok);
	} else {
		AddDebugLogLineM(false, logProxy,
			wxString(wxT("wait state -- ")) << m_state_name[state]);
	}
#endif // __DEBUG__
}

/**
 * Code this such that the next state is only entered when it is able to
 * perform the operation (read or write). State processing will assume
 * that it can read or write upon entry of the state. This is done using
 * CanSend() and CanReceive(). On daemon, these functions always return
 * true, because sockets are blocking.
 */
t_sm_state CSocks5StateMachine::next_state(t_sm_event event)
{
	// Default is stay in current state
	t_sm_state ret = HandleEvent(event);
	switch (GetState()) {
	case SOCKS5_STATE_START:
		if (m_isConnected && !m_isLost && CanSend()) {
			ret = SOCKS5_STATE_SEND_QUERY_AUTHENTICATION_METHOD;
		}
		break;
		
	case SOCKS5_STATE_SEND_QUERY_AUTHENTICATION_METHOD:
		if (CanReceive()) {
			ret = SOCKS5_STATE_RECEIVE_AUTHENTICATION_METHOD;
		}
		break;
		
	case SOCKS5_STATE_RECEIVE_AUTHENTICATION_METHOD:
		ret = SOCKS5_STATE_PROCESS_AUTHENTICATION_METHOD;
		break;
		
	case SOCKS5_STATE_PROCESS_AUTHENTICATION_METHOD:
		if (m_ok) {
			if (CanSend()) {
				switch (m_lastReply) {
				case SOCKS5_AUTH_METHOD_NO_AUTH_REQUIRED:
					ret = SOCKS5_STATE_SEND_COMMAND_REQUEST;
					break;
					
				case SOCKS5_AUTH_METHOD_GSSAPI:
					ret = SOCKS5_STATE_SEND_AUTHENTICATION_GSSAPI;
					break;
					
				case SOCKS5_AUTH_METHOD_USERNAME_PASSWORD:
					ret = SOCKS5_STATE_SEND_AUTHENTICATION_USERNAME_PASSWORD;
					break;
				
				case SOCKS5_AUTH_METHOD_NO_ACCEPTABLE_METHODS:
				default:
					ret = SOCKS5_STATE_END;
					break;
				}
			} else {
				AddDebugLogLineM(false, logProxy, wxT("Cant send"));
			}
		} else {
			ret = SOCKS5_STATE_END;
		}
		break;
		
	case SOCKS5_STATE_SEND_AUTHENTICATION_GSSAPI:
		if (m_ok) {
			if (CanReceive()) {
				ret = SOCKS5_STATE_RECEIVE_AUTHENTICATION_GSSAPI;
			}
		} else {
			ret = SOCKS5_STATE_END;
		}
		break;
		
	case SOCKS5_STATE_SEND_AUTHENTICATION_USERNAME_PASSWORD:
		if (m_ok) {
			if (CanReceive()) {
				ret = SOCKS5_STATE_RECEIVE_AUTHENTICATION_USERNAME_PASSWORD;
			}
		} else {
			ret = SOCKS5_STATE_END;
		}
		break;
		
	case SOCKS5_STATE_RECEIVE_AUTHENTICATION_GSSAPI:
		ret = SOCKS5_STATE_PROCESS_AUTHENTICATION_GSSAPI;
		break;
		
	case SOCKS5_STATE_RECEIVE_AUTHENTICATION_USERNAME_PASSWORD:
		ret = SOCKS5_STATE_PROCESS_AUTHENTICATION_USERNAME_PASSWORD;
		break;
		
	case SOCKS5_STATE_PROCESS_AUTHENTICATION_GSSAPI:
	case SOCKS5_STATE_PROCESS_AUTHENTICATION_USERNAME_PASSWORD:
		if (m_ok) {
			if (CanSend()) {
				ret = SOCKS5_STATE_SEND_COMMAND_REQUEST;
			}
		} else {
			ret = SOCKS5_STATE_END;
		}
		break;
		
	case SOCKS5_STATE_SEND_COMMAND_REQUEST:
		if (m_ok) {
			if (CanReceive()) {
				ret = SOCKS5_STATE_RECEIVE_COMMAND_REPLY;
			}
		} else {
			ret = SOCKS5_STATE_END;
		}
		break;
		
	case SOCKS5_STATE_RECEIVE_COMMAND_REPLY:
		ret = SOCKS5_STATE_PROCESS_COMMAND_REPLY;
		break;
		
	case SOCKS5_STATE_PROCESS_COMMAND_REPLY:
		ret = SOCKS5_STATE_END;
		break;
		
	case SOCKS5_STATE_END:
	default:
		break;
	}
	
	return ret;
}

/**
 * 	So, this is how you do it: the state machine is clocked by the events
 * that happen inside the event handler. You can add a dummy event whenever
 * you see that the system will not generate an event. But don't add dummy
 * events before reads, reads should only be performed after input events.
 *
 * 	Maybe it makes sense to add a dummy event before a read if there is no
 * state change (wait state).
 *
 * 	The event system will generate at least 2 events, one wxSOCKET_CONNECTION,
 * one wxSOCKET_OUTPUT, so we will have 2 clocks in our state machine. Plus, each
 * time there is unread data in the receive buffer of the socket, a wxSOCKET_INPUT
 * event will be generated. If you feel you will need more clocks than these, use
 * AddDummyEvent(), but I suggest you review your state machine design first.
 */
void CSocks5StateMachine::process_start(bool entry)
{
	if (entry) {
	} else {
	}
}

void CSocks5StateMachine::process_end(bool)
{
	ReactivateSocket();
}

void CSocks5StateMachine::process_send_query_authentication_method(bool entry)
{
	if (entry) {
		// Prepare the authentication method negotiation packet
		m_buffer[0] = SOCKS5_VERSION;
		m_buffer[1] = 2; // Number of supported methods
		//m_buffer[1] = 3; // Number of supported methods
		m_buffer[2] = SOCKS5_AUTH_METHOD_NO_AUTH_REQUIRED;
		m_buffer[3] = SOCKS5_AUTH_METHOD_USERNAME_PASSWORD;
		m_buffer[4] = SOCKS5_AUTH_METHOD_GSSAPI;
		m_packetLenght = 4;
		//m_packetLenght = 5;
		
		// Send the authentication method negotiation packet
		ProxyWrite(*m_proxyClientSocket, m_buffer, m_packetLenght);
	}
}

void CSocks5StateMachine::process_receive_authentication_method(bool entry)
{
	if (entry) {
		// Receive the method selection message
		m_packetLenght = 2;
		ProxyRead(*m_proxyClientSocket, m_buffer);
	}
	/* This is added because there will be no more input events. If the 
	 * world was a nice place, we could think about joining the
	 * process_receive and the process_process states here, but some day
	 * we might have to deal with the fact that the i/o operation has been
	 * incomplete, and that we must finish our job the next time we enter
	 * this state. */
	AddDummyEvent();
}

void CSocks5StateMachine::process_process_authentication_method(bool entry)
{
	if (entry) {
		m_lastReply = m_buffer[1];
		m_ok = m_ok && m_buffer[0] == SOCKS5_VERSION;
	}
	/* Ok, this one is here because wxSOCKET_OUTPUT events only happen
	 * once when you connect the socket, and after that, only after a
	 * wxSOCKET_WOULDBLOCK error happens. */
	AddDummyEvent();
}

void CSocks5StateMachine::process_send_authentication_gssapi(bool)
{
	// TODO or not TODO? That is the question...
	m_ok = false;
}

void CSocks5StateMachine::process_receive_authentication_gssapi(bool)
{
	AddDummyEvent();
}

void CSocks5StateMachine::process_process_authentication_gssapi(bool)
{
	AddDummyEvent();
}

void CSocks5StateMachine::process_send_authentication_username_password(bool entry)
{
	if (entry) {
		unsigned char lenUser = m_proxyData.m_userName.Len();
		unsigned char lenPassword = m_proxyData.m_password.Len();
		m_packetLenght = 1 + 1 + lenUser + 1 + lenPassword;
		unsigned int offsetUser = 2;
		unsigned int offsetPassword = offsetUser + lenUser + 1;
		
		// Prepare username/password buffer
		m_buffer[0] = SOCKS5_AUTH_VERSION_USERNAME_PASSWORD;
		m_buffer[offsetUser-1] = lenUser;
		memcpy(m_buffer+offsetUser, unicode2char(m_proxyData.m_userName),
			lenUser);
		m_buffer[offsetPassword-1] = lenPassword;
		memcpy(m_buffer+offsetPassword, unicode2char(m_proxyData.m_password),
			lenPassword);
		
		// Send the username/password packet
		ProxyWrite(*m_proxyClientSocket, m_buffer, m_packetLenght);
	}
}

void CSocks5StateMachine::process_receive_authentication_username_password(bool entry)
{
	if (entry) {
		// Receive the server's authentication response
		m_packetLenght = 2;
		ProxyRead(*m_proxyClientSocket, m_buffer);
	}
	AddDummyEvent();
}

void CSocks5StateMachine::process_process_authentication_username_password(bool entry)
{
	if (entry) {
		// Process the server's reply
		m_lastReply = m_buffer[1];
		m_ok =	m_ok &&
			m_buffer[0] == SOCKS5_AUTH_VERSION_USERNAME_PASSWORD &&
			m_buffer[1] == SOCKS5_REPLY_SUCCEED;
	}
	AddDummyEvent();
}

void CSocks5StateMachine::process_send_command_request(bool entry)
{
	if (entry) {
		// Prepare the request command buffer
		m_buffer[0] = SOCKS5_VERSION;
		switch (m_proxyCommand) {
		case PROXY_CMD_CONNECT:
			m_buffer[1] = SOCKS5_CMD_CONNECT;
			break;
			
		case PROXY_CMD_BIND:
			m_buffer[1] = SOCKS5_CMD_BIND;
			break;
			
		case PROXY_CMD_UDP_ASSOCIATE:
			m_buffer[1] = SOCKS5_CMD_UDP_ASSOCIATE;
			break;
		}
		m_buffer[2] = SOCKS5_RSV;
		m_buffer[3] = SOCKS5_ATYP_IPV4_ADDRESS;
		PokeUInt32( m_buffer+4, StringIPtoUint32(m_peerAddress->IPAddress()) );
		RawPokeUInt16( m_buffer+8, ENDIAN_HTONS( m_peerAddress->Service() ) );
		
		// Send the command packet
		m_packetLenght = 10;
		ProxyWrite(*m_proxyClientSocket, m_buffer, m_packetLenght);
	}
}

void CSocks5StateMachine::process_receive_command_reply(bool entry)
{
	if (entry) {
		// The minimum number of bytes to read is 10 in the case of
		// ATYP == SOCKS5_ATYP_IPV4_ADDRESS
		m_packetLenght = 10;
		ProxyRead(*m_proxyClientSocket, m_buffer);
	}
	AddDummyEvent();
}

void CSocks5StateMachine::process_process_command_reply(bool entry)
{
	if (entry) {
		m_lastReply = m_buffer[1];
		unsigned char addressType = m_buffer[3];
		// Process the server's reply
		m_ok = m_ok &&
			m_buffer[0] == SOCKS5_VERSION &&
			m_buffer[1] == SOCKS5_REPLY_SUCCEED;
		if (m_ok) {
			// Read BND.ADDR
			unsigned int portOffset = 0;
			switch(addressType) {
			case SOCKS5_ATYP_IPV4_ADDRESS:
			{
				const unsigned int addrOffset = 4;
				portOffset = 8;
				m_proxyBoundAddressIPV4.Hostname( PeekUInt32( m_buffer+addrOffset) );
				m_proxyBoundAddress = &m_proxyBoundAddressIPV4;
				break;
			}
			case SOCKS5_ATYP_DOMAINNAME:
			{
				// Read the domain name
				const unsigned int addrOffset = 5;
				portOffset = 10 + m_buffer[4];
				char c = m_buffer[portOffset];
				m_buffer[portOffset] = 0;
				m_proxyBoundAddressIPV4.Hostname(
					char2unicode(m_buffer+addrOffset));
				m_proxyBoundAddress = &m_proxyBoundAddressIPV4;
				m_buffer[portOffset] = c;
				break;
			}
			case SOCKS5_ATYP_IPV6_ADDRESS:
			{
				portOffset = 20;
				// TODO
				// IPV6 not yet implemented in wx
				//m_proxyBoundAddress.Hostname(Uint128toStringIP(
				//	*((uint128 *)(m_buffer+addrOffset)) ));
				//m_proxyBoundAddress = &m_proxyBoundAddressIPV6;
				m_ok = false;
				break;
			}
			}
			// Set the packet length at last
			m_packetLenght = portOffset + 2;
			// Read BND.PORT
			m_proxyBoundAddress->Service( ENDIAN_NTOHS( RawPeekUInt16( m_buffer+portOffset) ) );
		}
	}
	AddDummyEvent();
}

//------------------------------------------------------------------------------
// CSocks4StateMachine
//------------------------------------------------------------------------------

CSocks4StateMachine::CSocks4StateMachine(
	const CProxyData &proxyData,
	CProxyCommand proxyCommand)
:
CProxyStateMachine(
	wxString(wxT("Socks4")), SOCKS4_MAX_STATES, proxyData, proxyCommand)
{
	m_process_state[0] = &CSocks4StateMachine::process_start;
	m_state_name[0] = wxT("process_start");
	m_process_state[1] = &CSocks4StateMachine::process_end;
	m_state_name[1] = wxT("process_end");
	m_process_state[2] = &CSocks4StateMachine::process_send_command_request;
	m_state_name[2] = wxT("process_send_command_request");
	m_process_state[3] = &CSocks4StateMachine::process_receive_command_reply;
	m_state_name[3] = wxT("process_receive_command_reply");
	m_process_state[4] = &CSocks4StateMachine::process_process_command_reply;
	m_state_name[4] = wxT("process_process_command_reply");
}

void CSocks4StateMachine::process_state(t_sm_state state, bool entry)
{
	(this->*m_process_state[state])(entry);
#ifdef __DEBUG__
	int n = 0;
	
	switch (state) {
	case SOCKS4_STATE_START:
	case SOCKS4_STATE_END:
	case SOCKS4_STATE_RECEIVE_COMMAND_REPLY:
	default:
		n = 0;
		break;
		
	case SOCKS4_STATE_SEND_COMMAND_REQUEST:
		n = m_packetLenght;
		break;
	
	case SOCKS4_STATE_PROCESS_COMMAND_REPLY:
		n = m_lastRead;
		break;
	}
	
	if (entry) {
		DumpMem(m_buffer, n, m_state_name[state], m_ok);
	} else {
		AddDebugLogLineM(false, logProxy,
			wxString(wxT("wait state -- ")) << m_state_name[state]);
	}
#endif // __DEBUG__
}

t_sm_state CSocks4StateMachine::next_state(t_sm_event event)
{
	// Default is stay in current state
	t_sm_state ret = HandleEvent(event);
	switch (GetState()) {
	case SOCKS4_STATE_START:
		if (m_isConnected && !m_isLost && CanSend()) {
			ret = SOCKS4_STATE_SEND_COMMAND_REQUEST;
		}
		break;
		
	case SOCKS4_STATE_SEND_COMMAND_REQUEST:
		if (m_ok) {
			if (CanReceive()) {
				ret = SOCKS4_STATE_RECEIVE_COMMAND_REPLY;
			}
		} else {
			ret = SOCKS4_STATE_END;
		}
		break;
		
	case SOCKS4_STATE_RECEIVE_COMMAND_REPLY:
		ret = SOCKS4_STATE_PROCESS_COMMAND_REPLY;
		break;
		
	case SOCKS4_STATE_PROCESS_COMMAND_REPLY:
		ret = SOCKS4_STATE_END;
		break;
		
	case SOCKS4_STATE_END:
	default:
		break;
	}
	
	return ret;
}

void CSocks4StateMachine::process_start(bool entry)
{
	if (entry) {
	} else {
	}
}

void CSocks4StateMachine::process_end(bool)
{
	ReactivateSocket();
}

void CSocks4StateMachine::process_send_command_request(bool entry)
{
	if (entry) {
		// Prepare the request command buffer
		m_buffer[0] = SOCKS4_VERSION;
		switch (m_proxyCommand) {
		case PROXY_CMD_CONNECT:
			m_buffer[1] = SOCKS4_CMD_CONNECT;
			break;
			
		case PROXY_CMD_BIND:
			m_buffer[1] = SOCKS4_CMD_BIND;
			break;
			
		case PROXY_CMD_UDP_ASSOCIATE:
			m_ok = false;
			return;
			break;
		}
		RawPokeUInt16(m_buffer+2, ENDIAN_HTONS(m_peerAddress->Service()));
		// Special processing for SOCKS4a
		switch (m_proxyData.m_proxyType) {
		case PROXY_SOCKS4a:
			PokeUInt32(m_buffer+4, StringIPtoUint32(wxT("0.0.0.1")));
			break;
		case PROXY_SOCKS4:
		default:
			PokeUInt32(m_buffer+4, StringIPtoUint32(m_peerAddress->IPAddress()));
			break;
		}
		// Common processing for SOCKS4/SOCKS4a
		unsigned int offsetUser = 8;
		unsigned char lenUser = m_proxyData.m_userName.Len();
		memcpy(m_buffer + offsetUser, 
			unicode2char(m_proxyData.m_userName), lenUser);
		m_buffer[offsetUser + lenUser] = 0;
		// Special processing for SOCKS4a
		switch (m_proxyData.m_proxyType) {
		case PROXY_SOCKS4a: {
			unsigned int offsetDomain = offsetUser + lenUser + 1;
			unsigned char lenDomain = m_peerAddress->Hostname().Len();
			memcpy(m_buffer + offsetDomain, 
				unicode2char(m_peerAddress->Hostname()), lenDomain);
			m_buffer[offsetDomain + lenDomain] = 0;
			m_packetLenght = 1 + 1 + 2 + 4 + lenUser + 1 + lenDomain + 1;
			break;
		}
		case PROXY_SOCKS4:
		default:
			m_packetLenght = 1 + 1 + 2 + 4 + lenUser + 1;
			break;
		}
		// Send the command packet
		ProxyWrite(*m_proxyClientSocket, m_buffer, m_packetLenght);
	}
}

void CSocks4StateMachine::process_receive_command_reply(bool entry)
{
	if (entry) {
		// Receive the server's reply
		m_packetLenght = 8;
		ProxyRead(*m_proxyClientSocket, m_buffer);
	}
	AddDummyEvent();
}

void CSocks4StateMachine::process_process_command_reply(bool entry)
{
	if (entry) {
		m_lastReply = m_buffer[1];
		
		// Process the server's reply
		m_ok = m_ok &&
			m_buffer[0] == SOCKS4_REPLY_CODE &&
			m_buffer[1] == SOCKS4_REPLY_GRANTED;
		if (m_ok) {
			// Read BND.PORT
			const unsigned int portOffset = 2;
			m_ok = m_proxyBoundAddressIPV4.Service(ENDIAN_NTOHS(
				RawPeekUInt16( m_buffer+portOffset) ) );
			// Read BND.ADDR
			const unsigned int addrOffset = 4;
			m_ok = m_ok &&
				m_proxyBoundAddressIPV4.Hostname( PeekUInt32( m_buffer+addrOffset ) );
			m_proxyBoundAddress = &m_proxyBoundAddressIPV4;
		}
	}
	AddDummyEvent();
}

//------------------------------------------------------------------------------
// CHttpStateMachine
//------------------------------------------------------------------------------

CHttpStateMachine::CHttpStateMachine(
	const CProxyData &proxyData,
	CProxyCommand proxyCommand)
:
CProxyStateMachine(
	wxString(wxT("Http")), HTTP_MAX_STATES, proxyData, proxyCommand)
{
	m_process_state[0] = &CHttpStateMachine::process_start;
	m_state_name[0] = wxT("process_start");
	m_process_state[1] = &CHttpStateMachine::process_end;
	m_state_name[1] = wxT("process_end");
	m_process_state[2] = &CHttpStateMachine::process_send_command_request;
	m_state_name[2] = wxT("process_send_command_request");
	m_process_state[3] = &CHttpStateMachine::process_receive_command_reply;
	m_state_name[3] = wxT("process_receive_command_reply");
	m_process_state[4] = &CHttpStateMachine::process_process_command_reply;
	m_state_name[4] = wxT("process_process_command_reply");
}

void CHttpStateMachine::process_state(t_sm_state state, bool entry)
{
	(this->*m_process_state[state])(entry);
#ifdef __DEBUG__
	int n = 0;
	
	switch (state) {
	case HTTP_STATE_START:
	case HTTP_STATE_END:
	case HTTP_STATE_RECEIVE_COMMAND_REPLY:
	default:
		n = 0;
		break;
		
	case HTTP_STATE_SEND_COMMAND_REQUEST:
		n = m_packetLenght;
		break;
	
	case HTTP_STATE_PROCESS_COMMAND_REPLY:
		n = m_lastRead;
		break;
	}
	
	if (entry) {
		DumpMem(m_buffer, n, m_state_name[state], m_ok);
	} else {
		AddDebugLogLineM(false, logProxy,
			wxString(wxT("wait state -- ")) << m_state_name[state]);
	}
#endif // __DEBUG__
}

t_sm_state CHttpStateMachine::next_state(t_sm_event event)
{
	// Default is stay in current state
	t_sm_state ret = HandleEvent(event);
	switch (GetState()) {
	case HTTP_STATE_START:
		if (m_isConnected && !m_isLost && CanSend()) {
			ret = HTTP_STATE_SEND_COMMAND_REQUEST;
		}
		break;
		
	case HTTP_STATE_SEND_COMMAND_REQUEST:
		if (m_ok) {
			if (CanReceive()) {
				ret = HTTP_STATE_RECEIVE_COMMAND_REPLY;
			}
		} else {
			ret = HTTP_STATE_END;
		}
		break;
		
	case HTTP_STATE_RECEIVE_COMMAND_REPLY:
		ret = HTTP_STATE_PROCESS_COMMAND_REPLY;
		break;
		
	case HTTP_STATE_PROCESS_COMMAND_REPLY:
		ret = HTTP_STATE_END;
		break;
		
	case HTTP_STATE_END:
	default:
		break;
	}
	
	return ret;
}

void CHttpStateMachine::process_start(bool entry)
{
	if (entry) {
	} else {
	}
}

void CHttpStateMachine::process_end(bool)
{
	ReactivateSocket();
}

void CHttpStateMachine::process_send_command_request(bool entry)
{
	if (entry) {
		// Prepare the request command buffer
		wxString ip = m_peerAddress->IPAddress();
		uint16 port = m_peerAddress->Service();
		wxString userPass;
		wxString userPassEncoded;
		if (m_proxyData.m_enablePassword) {
			userPass = m_proxyData.m_userName + wxT(":") + m_proxyData.m_password;
			userPassEncoded =
				EncodeBase64(unicode2char(userPass), PROXY_BUFFER_SIZE);
		}
		wxString msg;
		
		switch (m_proxyCommand) {
		case PROXY_CMD_CONNECT:
			msg <<
			wxT("CONNECT ") << ip << wxT(":") << port << wxT(" HTTP/1.1\r\n") <<
			wxT("Host: ")   << ip << wxT(":") << port << wxT("\r\n");
			if (m_proxyData.m_enablePassword) {
				msg << 
				wxT("Authorization: Basic ")       << userPassEncoded << wxT("\r\n") <<
				wxT("Proxy-Authorization: Basic ") << userPassEncoded << wxT("\r\n");
			} else {
				msg << wxT("\r\n");
			}
			break;
			
		case PROXY_CMD_BIND:
			m_ok = false;	
			break;
			
		case PROXY_CMD_UDP_ASSOCIATE:
			m_ok = false;
			return;
			break;
		}
		// Send the command packet
		m_packetLenght = msg.Len();
		memcpy(m_buffer, unicode2char(msg), m_packetLenght+1);
		ProxyWrite(*m_proxyClientSocket, m_buffer, m_packetLenght);
	}
}

void CHttpStateMachine::process_receive_command_reply(bool entry)
{
	if (entry) {
		// Receive the server's reply -- Use a large number, but don't
		// Expect to get it all. HTTP protocol does not have a fixed length.
		m_packetLenght = PROXY_BUFFER_SIZE;
		ProxyRead(*m_proxyClientSocket, m_buffer);
	}
	AddDummyEvent();
}

/*
 * HTTP Proxy server response should be something like:
 * "HTTP/1.1 200 Connection established\r\n\r\n"
 * but that may vary. The important thing is the "200"
 * code, that means success.
 */
static const char HTTP_AUTH_RESPONSE[] = "HTTP/";
static const int  HTTP_AUTH_RESPONSE_LENGHT = strlen(HTTP_AUTH_RESPONSE);
void CHttpStateMachine::process_process_command_reply(bool entry)
{
	if (entry) {
		// The position of the first space in the buffer
		int i = 8;
		while (m_buffer[i] == ' ') {
			i++;
		}
		// Process the server's reply
		m_ok =	!memcmp(m_buffer + 0, HTTP_AUTH_RESPONSE, HTTP_AUTH_RESPONSE_LENGHT) &&
			!memcmp(m_buffer + i, "200", 3);
	}
	AddDummyEvent();
}

//------------------------------------------------------------------------------
// CProxySocket
//------------------------------------------------------------------------------

CProxySocket::CProxySocket(
	wxSocketFlags flags,
	const CProxyData *proxyData,
	CProxyCommand proxyCommand,
	CDatagramSocketProxy *udpSocket)
:
wxSocketClient(flags),
m_proxyStateMachine(NULL),
m_udpSocket(udpSocket),
m_socketEventHandler(NULL),
m_socketEventHandlerId(0),
m_savedSocketEventHandler(NULL),
m_savedSocketEventHandlerId(0)
{
	SetProxyData(proxyData);
	if (m_useProxy) {
		switch (m_proxyData.m_proxyType) {
		case PROXY_NONE:
			break;
	
		case PROXY_SOCKS5:
			m_proxyStateMachine =
				new CSocks5StateMachine(*proxyData, proxyCommand);
			break;
		
		case PROXY_SOCKS4:
		case PROXY_SOCKS4a:
			m_proxyStateMachine =
				new CSocks4StateMachine(*proxyData, proxyCommand);
			break;
		
		case PROXY_HTTP:
			m_proxyStateMachine =
				new CHttpStateMachine(*proxyData, proxyCommand);
			break;
		
		default:
			break;
		}
	}
}

CProxySocket::~CProxySocket()
{
	delete m_proxyStateMachine;
}

void CProxySocket::SetProxyData(const CProxyData *proxyData)
{
// ADUNANZA BEGIN
#if 0
	m_useProxy = proxyData != NULL && proxyData->m_proxyEnable;
#else
	m_useProxy = ((proxyData != NULL) && (proxyData->m_proxyEnable));
#endif
// ADUNANZA BEGIN

	if (proxyData) {
		m_proxyData = *proxyData;
		m_proxyAddress.Hostname(m_proxyData.m_proxyHostName);
		m_proxyAddress.Service(m_proxyData.m_proxyPort);
	} else {
		m_proxyData.Clear();
	}
}

bool CProxySocket::Start(const wxIPaddress &peerAddress)
{
	SaveState();
	// Important note! SaveState()/RestoreState() DO NOT save/restore
	// the event handler. The method SaveEventHandler() has been created
	// for that.
	SaveEventHandler();
	SetEventHandler(g_proxyEventHandler, ID_PROXY_SOCKET_EVENT);
	SetNotify(
		wxSOCKET_CONNECTION_FLAG |
		wxSOCKET_INPUT_FLAG |
		wxSOCKET_OUTPUT_FLAG |
		wxSOCKET_LOST_FLAG);
	Notify(true);
	Connect(m_proxyAddress, false);
	SetFlags(wxSOCKET_NONE);
	bool ok = m_proxyStateMachine->Start(peerAddress, this);
	
	return ok;
}

bool CProxySocket::ProxyIsCapableOf(CProxyCommand proxyCommand) const
{
	bool ret = false;
	
	switch (m_proxyData.m_proxyType) {
	case PROXY_NONE:
		ret = false;
		break;
		
	case PROXY_SOCKS5:
		ret =	proxyCommand == PROXY_CMD_CONNECT ||
			proxyCommand == PROXY_CMD_BIND ||
			proxyCommand == PROXY_CMD_UDP_ASSOCIATE;
		break;
		
	case PROXY_SOCKS4:
	case PROXY_SOCKS4a:
		ret =	proxyCommand == PROXY_CMD_CONNECT ||
			proxyCommand == PROXY_CMD_BIND;
		break;
		
	case PROXY_HTTP:
		ret =	proxyCommand == PROXY_CMD_CONNECT;
		break;
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// CSocketClientProxy
//------------------------------------------------------------------------------

CSocketClientProxy::CSocketClientProxy(
	wxSocketFlags flags,
	const CProxyData *proxyData)
:
CProxySocket(flags, proxyData, PROXY_CMD_CONNECT)
{
}

bool CSocketClientProxy::Connect(wxIPaddress &address, bool wait)
{
	wxMutexLocker lock(m_socketLocker);
	bool ok;
	
	if (GetUseProxy() && ProxyIsCapableOf(PROXY_CMD_CONNECT)) {
		ok = Start(address);
	} else {
		ok = wxSocketClient::Connect(address, wait);
	}
	
	return ok;
}

CSocketClientProxy& CSocketClientProxy::Read(void *buffer, wxUint32 nbytes)
{
	wxMutexLocker lock(m_socketLocker);
	CProxySocket::Read(buffer, nbytes);

	return *this;
	
}

CSocketClientProxy& CSocketClientProxy::Write(const void *buffer, wxUint32 nbytes)
{
	wxMutexLocker lock(m_socketLocker);
	CProxySocket::Write(buffer, nbytes);
	
	return *this;
}

//------------------------------------------------------------------------------
// CSocketServerProxy
//------------------------------------------------------------------------------

CSocketServerProxy::CSocketServerProxy(
	wxIPaddress &address,
	wxSocketFlags flags,
	const CProxyData *)
:
wxSocketServer(address, flags)
{
	/* Maybe some day when socks6 is out... :) */
}

CSocketServerProxy& CSocketServerProxy::Read(void *buffer, wxUint32 nbytes)
{
	wxMutexLocker lock(m_socketLocker);
	wxSocketServer::Read(buffer, nbytes);

	return *this;
}

CSocketServerProxy& CSocketServerProxy::Write(const void *buffer, wxUint32 nbytes)
{
	wxMutexLocker lock(m_socketLocker);
	wxSocketServer::Write(buffer, nbytes);

	return *this;
}

//------------------------------------------------------------------------------
// CDatagramSocketProxy
//------------------------------------------------------------------------------

CDatagramSocketProxy::CDatagramSocketProxy(
	wxIPaddress &address, wxSocketFlags flags, const CProxyData *proxyData)
:
wxDatagramSocket(address, flags),
m_proxyTCPSocket(wxSOCKET_NOWAIT, proxyData, PROXY_CMD_UDP_ASSOCIATE, this)
{
	m_udpSocketOk = false;
	if (	m_proxyTCPSocket.GetUseProxy() &&
		m_proxyTCPSocket.ProxyIsCapableOf(PROXY_CMD_UDP_ASSOCIATE)) {
			m_proxyTCPSocket.Start(address);
	} else {
	}
	m_lastUDPOperation = UDP_OPERATION_NONE;
}

CDatagramSocketProxy::~CDatagramSocketProxy()
{
	// From RFC-1928:
	// "A UDP association terminates when the TCP connection that the
	// UDP ASSOCIATE request arrived terminates."
}

wxDatagramSocket &CDatagramSocketProxy::RecvFrom(
	wxSockAddress &addr, void* buf, wxUint32 nBytes )
{
	wxMutexLocker lock(m_socketLocker);
	m_lastUDPOperation = UDP_OPERATION_RECV_FROM;
	if (m_proxyTCPSocket.GetUseProxy()) {
		if (m_udpSocketOk) {
			char *bufUDP = NULL;
			if (nBytes + PROXY_UDP_MAXIMUM_OVERHEAD > PROXY_BUFFER_SIZE) {
				bufUDP = new char[nBytes + PROXY_UDP_MAXIMUM_OVERHEAD];
			} else {
				bufUDP = m_proxyTCPSocket.GetBuffer();
			}
			wxDatagramSocket::RecvFrom(
				m_proxyTCPSocket.GetProxyBoundAddress(),
				bufUDP, nBytes + PROXY_UDP_MAXIMUM_OVERHEAD);
			unsigned int offset;
			switch (m_proxyTCPSocket.GetBuffer()[3]) {
			case SOCKS5_ATYP_IPV4_ADDRESS: {
				offset = PROXY_UDP_OVERHEAD_IPV4;
				try {
					amuleIPV4Address &a = dynamic_cast<amuleIPV4Address &>(addr);
					a.Hostname( PeekUInt32( m_proxyTCPSocket.GetBuffer()+4 ) );
					a.Service( ENDIAN_NTOHS( RawPeekUInt16( m_proxyTCPSocket.GetBuffer()+8) ) );
				} catch (const std::bad_cast& WXUNUSED(e)) {
					AddDebugLogLineM(false, logProxy,
						wxT("(2)bad_cast exception!"));
					wxASSERT(false);
				}
			}
				break;
				
			case SOCKS5_ATYP_DOMAINNAME:
				offset = PROXY_UDP_OVERHEAD_DOMAIN_NAME;
				break;
				
			case SOCKS5_ATYP_IPV6_ADDRESS:
				offset = PROXY_UDP_OVERHEAD_IPV6;
				break;
				
			default:
				/* Error! */
				offset = 0;
				break;
			}
			memcpy(buf, bufUDP + offset, nBytes);
			// Uncomment here to see the buffer contents on console
			// DumpMem(bufUDP, wxDatagramSocket::LastCount(), wxT("RecvFrom"), 3);
			
			/* Only delete buffer if it was dynamically created */
			if (bufUDP != m_proxyTCPSocket.GetBuffer()) {
				/* We should use a fixed buffer to avoid
				 * new/delete it all the time. 
				 * I need an upper bound */
				delete bufUDP;
			}
			/* There is still one problem pending, fragmentation.
			 * Either we support it or we have to drop fragmented
			 * messages. I vote for drop :)
			 */
		}
	} else {
		wxDatagramSocket::RecvFrom(addr, buf, nBytes);
	}
	
	return *this;
}

wxDatagramSocket &CDatagramSocketProxy::SendTo(
	wxIPaddress &addr, const void* buf, wxUint32 nBytes )
{
	wxMutexLocker lock(m_socketLocker);
	m_lastUDPOperation = UDP_OPERATION_SEND_TO;
	m_lastUDPOverhead = PROXY_UDP_OVERHEAD_IPV4;
	if (m_proxyTCPSocket.GetUseProxy()) {
		if (m_udpSocketOk) {
			m_proxyTCPSocket.GetBuffer()[0] = SOCKS5_RSV;	// Reserved
			m_proxyTCPSocket.GetBuffer()[1] = SOCKS5_RSV;	// Reserved
			m_proxyTCPSocket.GetBuffer()[2] = 0;		// FRAG
			m_proxyTCPSocket.GetBuffer()[3] = SOCKS5_ATYP_IPV4_ADDRESS;
			PokeUInt32( m_proxyTCPSocket.GetBuffer()+4, StringIPtoUint32(addr.IPAddress()));
			RawPokeUInt16( m_proxyTCPSocket.GetBuffer()+8, ENDIAN_HTONS( addr.Service() ) );
			memcpy(m_proxyTCPSocket.GetBuffer() + PROXY_UDP_OVERHEAD_IPV4, buf, nBytes);
			nBytes += PROXY_UDP_OVERHEAD_IPV4;
			wxDatagramSocket::SendTo(
				m_proxyTCPSocket.GetProxyBoundAddress(),
				m_proxyTCPSocket.GetBuffer(), nBytes);
			// Uncomment here to see the buffer contents on console
			// DumpMem(m_proxyTCPSocket.GetBuffer(), nBytes, wxT("SendTo"), 3);
		}
	} else {
		wxDatagramSocket::SendTo(addr, buf, nBytes);
	}
	
	return *this;
}

wxUint32 CDatagramSocketProxy::LastCount(void) const
{
	wxUint32 ret;

	if (m_proxyTCPSocket.GetUseProxy()) {
		switch (m_lastUDPOperation) {
		case UDP_OPERATION_RECV_FROM:
		case UDP_OPERATION_SEND_TO:
			ret = Ok() ? wxDatagramSocket::LastCount() - m_lastUDPOverhead : 0;
			break;
			
		case UDP_OPERATION_NONE:
		default:
			ret = 0;
			break;
		
		}
	} else {
		ret = wxDatagramSocket::LastCount();
	}
	
	return ret;
}

#endif // CLIENT_GUI

/******************************************************************************/
// File_checked_for_headers
